diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java b/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java index 6f1a5754f..27e706754 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java @@ -14,8 +14,8 @@ import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.visualization.VisualEmbedding; import com.jozufozu.flywheel.api.visualization.VisualizationContext; -import com.jozufozu.flywheel.backend.engine.embed.EmbeddedEnvironment; import com.jozufozu.flywheel.backend.engine.embed.Environment; +import com.jozufozu.flywheel.backend.engine.embed.TopLevelEmbeddedEnvironment; import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.lib.task.Flag; @@ -30,7 +30,7 @@ import net.minecraft.world.phys.Vec3; public class EngineImpl implements Engine { private final int sqrMaxOriginDistance; private final DrawManager> drawManager; - private final EnvironmentStorage environmentStorage = new EnvironmentStorage(this); + private final EnvironmentStorage environmentStorage = new EnvironmentStorage(); private final Flag flushFlag = new NamedFlag("flushed"); private BlockPos renderOrigin = BlockPos.ZERO; @@ -110,12 +110,8 @@ public class EngineImpl implements Engine { flushFlag.raise(); } - public VisualEmbedding createEmbedding(RenderStage renderStage) { - return environmentStorage.create(renderStage); - } - - public void freeEmbedding(EmbeddedEnvironment embeddedEnvironment) { - environmentStorage.enqueueDeletion(embeddedEnvironment); + public EnvironmentStorage environmentStorage() { + return environmentStorage; } private class VisualizationContextImpl implements VisualizationContext { @@ -139,7 +135,9 @@ public class EngineImpl implements Engine { @Override public VisualEmbedding createEmbedding() { - return EngineImpl.this.createEmbedding(stage); + var out = new TopLevelEmbeddedEnvironment(EngineImpl.this, stage); + environmentStorage.track(out); + return out; } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java b/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java index 9a0e72270..e991f32f9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java @@ -3,49 +3,38 @@ package com.jozufozu.flywheel.backend.engine; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.visualization.VisualEmbedding; -import com.jozufozu.flywheel.backend.engine.embed.EmbeddedEnvironment; +import com.jozufozu.flywheel.backend.engine.embed.AbstractEmbeddedEnvironment; import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ReferenceSet; import it.unimi.dsi.fastutil.objects.ReferenceSets; public class EnvironmentStorage { - protected final ReferenceSet environments = ReferenceSets.synchronize(new ReferenceLinkedOpenHashSet<>()); - private final Queue forDeletion = new ConcurrentLinkedQueue<>(); - private final EngineImpl engine; + protected final ReferenceSet environments = ReferenceSets.synchronize(new ReferenceLinkedOpenHashSet<>()); + private final Queue forDeletion = new ConcurrentLinkedQueue<>(); - public EnvironmentStorage(EngineImpl engine) { - this.engine = engine; + public void track(AbstractEmbeddedEnvironment environment) { + environments.add(environment); } - public VisualEmbedding create(RenderStage stage) { - var out = new EmbeddedEnvironment(engine, stage); - - environments.add(out); - - return out; - } - - public void enqueueDeletion(EmbeddedEnvironment environment) { + public void enqueueDeletion(AbstractEmbeddedEnvironment environment) { environments.remove(environment); forDeletion.add(environment); } public void flush() { - EmbeddedEnvironment env; + AbstractEmbeddedEnvironment env; while ((env = forDeletion.poll()) != null) { env.actuallyDelete(); } - environments.forEach(EmbeddedEnvironment::flush); + environments.forEach(AbstractEmbeddedEnvironment::flush); } public void delete() { - environments.forEach(EmbeddedEnvironment::actuallyDelete); + environments.forEach(AbstractEmbeddedEnvironment::actuallyDelete); environments.clear(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/AbstractEmbeddedEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/AbstractEmbeddedEnvironment.java new file mode 100644 index 000000000..03ecd9441 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/AbstractEmbeddedEnvironment.java @@ -0,0 +1,125 @@ +package com.jozufozu.flywheel.backend.engine.embed; + +import org.joml.Matrix3f; +import org.joml.Matrix3fc; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; + +import com.jozufozu.flywheel.api.event.RenderStage; +import com.jozufozu.flywheel.api.instance.Instance; +import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.api.instance.Instancer; +import com.jozufozu.flywheel.api.instance.InstancerProvider; +import com.jozufozu.flywheel.api.model.Model; +import com.jozufozu.flywheel.api.visualization.VisualEmbedding; +import com.jozufozu.flywheel.backend.compile.ContextShader; +import com.jozufozu.flywheel.backend.engine.EngineImpl; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.util.AtomicReferenceCounted; + +import net.minecraft.core.Vec3i; + +public abstract class AbstractEmbeddedEnvironment extends AtomicReferenceCounted implements Environment, VisualEmbedding { + protected final Matrix4f pose = new Matrix4f(); + protected final Matrix3f normal = new Matrix3f(); + private final Matrix4f poseComposed = new Matrix4f(); + private final Matrix3f normalComposed = new Matrix3f(); + private final InstancerProvider instancerProvider; + private final EngineImpl engine; + private final RenderStage renderStage; + + public AbstractEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { + this.engine = engine; + this.renderStage = renderStage; + + instancerProvider = new InstancerProvider() { + @Override + public Instancer instancer(InstanceType type, Model model) { + // Kinda cursed usage of anonymous classes here, but it does the job. + return engine.instancer(AbstractEmbeddedEnvironment.this, type, model, renderStage); + } + }; + + // Acquire the reference owned by the visual that created this. + acquire(); + } + + @Override + public void transforms(Matrix4fc pose, Matrix3fc normal) { + this.pose.set(pose); + this.normal.set(normal); + } + + public void flush() { + poseComposed.identity(); + normalComposed.identity(); + + composeMatrices(poseComposed, normalComposed); + } + + @Override + public void setupDraw(GlProgram program) { + setupLight(program); + + program.setMat4("_flw_modelMatrix", poseComposed); + program.setMat3("_flw_normalMatrix", normalComposed); + } + + @Override + public void setupCull(GlProgram program) { + program.setBool("_flw_useModelMatrix", true); + + program.setMat4("_flw_modelMatrix", poseComposed); + } + + @Override + public ContextShader contextShader() { + return ContextShader.EMBEDDED; + } + + @Override + public InstancerProvider instancerProvider() { + return instancerProvider; + } + + @Override + public Vec3i renderOrigin() { + return Vec3i.ZERO; + } + + @Override + public VisualEmbedding createEmbedding() { + var out = new NestedEmbeddedEnvironment(this, engine, renderStage); + engine.environmentStorage() + .track(out); + return out; + } + + /** + * Called by visuals + */ + @Override + public void delete() { + // Release the reference owned by the visual that created this. + // Note that visuals don't explicitly call acquire, instead the + // storage acquired a reference when this was constructed. + release(); + } + + /** + * Called when referenceCount goes to 0 + */ + @Override + public void _delete() { + engine.environmentStorage().enqueueDeletion(this); + } + + public abstract void setupLight(GlProgram program); + + public abstract void composeMatrices(Matrix4f pose, Matrix3f normal); + + /** + * Called in EnvironmentStorage#flush + */ + public abstract void actuallyDelete(); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java deleted file mode 100644 index 11eeb0302..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.jozufozu.flywheel.backend.engine.embed; - -import org.joml.Matrix3f; -import org.joml.Matrix3fc; -import org.joml.Matrix4f; -import org.joml.Matrix4fc; - -import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.instance.Instance; -import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.api.instance.Instancer; -import com.jozufozu.flywheel.api.instance.InstancerProvider; -import com.jozufozu.flywheel.api.model.Model; -import com.jozufozu.flywheel.api.visualization.VisualEmbedding; -import com.jozufozu.flywheel.backend.Samplers; -import com.jozufozu.flywheel.backend.compile.ContextShader; -import com.jozufozu.flywheel.backend.engine.EngineImpl; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.backend.util.AtomicReferenceCounted; - -import net.minecraft.core.Vec3i; -import net.minecraft.world.level.BlockAndTintGetter; - -public class EmbeddedEnvironment extends AtomicReferenceCounted implements Environment, VisualEmbedding { - private final Matrix4f pose = new Matrix4f(); - private final Matrix3f normal = new Matrix3f(); - - private final EmbeddedLightVolume lightVolume = new EmbeddedLightVolume(); - private final EmbeddedLightTexture lightTexture = new EmbeddedLightTexture(); - - private final InstancerProvider instancerProvider; - private final EngineImpl engine; - private final RenderStage renderStage; - - public EmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { - this.engine = engine; - this.renderStage = renderStage; - - instancerProvider = new InstancerProvider() { - @Override - public Instancer instancer(InstanceType type, Model model) { - // Kinda cursed usage of anonymous classes here, but it does the job. - return engine.instancer(EmbeddedEnvironment.this, type, model, renderStage); - } - }; - - // Acquire the reference owned by the visual that created this. - acquire(); - } - - public void flush() { - if (lightVolume.empty()) { - return; - } - Samplers.EMBEDDED_LIGHT.makeActive(); - - lightTexture.bind(); - - lightTexture.ensureCapacity(lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ()); - - lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ()); - } - - @Override - public void transforms(Matrix4fc pose, Matrix3fc normal) { - this.pose.set(pose); - this.normal.set(normal); - } - - @Override - public void collectLight(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) { - lightVolume.collect(level, minX, minY, minZ, sizeX, sizeY, sizeZ); - } - - @Override - public void invalidateLight() { - lightVolume.clear(); - } - - @Override - public ContextShader contextShader() { - return ContextShader.EMBEDDED; - } - - @Override - public void setupDraw(GlProgram drawProgram) { - if (!lightVolume.empty()) { - Samplers.EMBEDDED_LIGHT.makeActive(); - - lightTexture.bind(); - - float oneOverSizeX = 1f / (float) lightTexture.sizeX; - float oneOverSizeY = 1f / (float) lightTexture.sizeY; - float oneOverSizeZ = 1f / (float) lightTexture.sizeZ; - - drawProgram.setVec3("_flw_oneOverLightBoxSize", oneOverSizeX, oneOverSizeY, oneOverSizeZ); - drawProgram.setVec3("_flw_lightVolumeMin", lightVolume.x(), lightVolume.y(), lightVolume.z()); - drawProgram.setBool("_flw_useLightVolume", true); - } else { - drawProgram.setBool("_flw_useLightVolume", false); - } - drawProgram.setMat4("_flw_model", pose); - drawProgram.setMat3("_flw_normal", normal); - } - - @Override - public void setupCull(GlProgram cullProgram) { - cullProgram.setBool("_flw_useEmbeddedModel", true); - cullProgram.setMat4("_flw_embeddedModel", pose); - } - - @Override - public InstancerProvider instancerProvider() { - return instancerProvider; - } - - @Override - public Vec3i renderOrigin() { - return Vec3i.ZERO; - } - - @Override - public VisualEmbedding createEmbedding() { - return engine.createEmbedding(renderStage); - } - - /** - * Called by visuals - */ - @Override - public void delete() { - // Release the reference owned by the visual that created this. - // Note that visuals don't explicitly call acquire, instead the - // storage acquired a reference when this was constructed. - release(); - } - - /** - * Called when referenceCount goes to 0 - */ - @Override - public void _delete() { - engine.freeEmbedding(this); - } - - /** - * Called in EnvironmentStorage#flush - */ - public void actuallyDelete() { - // We could technically free the light volume right away in _delete, but - // the control flow here is so convoluted that it's probably best to do - // everything in one place. - lightVolume.delete(); - lightTexture.delete(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/NestedEmbeddedEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/NestedEmbeddedEnvironment.java new file mode 100644 index 000000000..f97dc9aa6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/NestedEmbeddedEnvironment.java @@ -0,0 +1,45 @@ +package com.jozufozu.flywheel.backend.engine.embed; + +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +import com.jozufozu.flywheel.api.event.RenderStage; +import com.jozufozu.flywheel.backend.engine.EngineImpl; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; + +import net.minecraft.world.level.BlockAndTintGetter; + +public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment { + private final AbstractEmbeddedEnvironment parent; + + public NestedEmbeddedEnvironment(AbstractEmbeddedEnvironment parent, EngineImpl engine, RenderStage renderStage) { + super(engine, renderStage); + this.parent = parent; + parent.acquire(); + } + + @Override + public void collectLight(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) { + } + + @Override + public void invalidateLight() { + } + + @Override + public void setupLight(GlProgram program) { + parent.setupLight(program); + } + + @Override + public void composeMatrices(Matrix4f pose, Matrix3f normal) { + parent.composeMatrices(pose, normal); + pose.mul(this.pose); + normal.mul(this.normal); + } + + @Override + public void actuallyDelete() { + parent.release(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/TopLevelEmbeddedEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/TopLevelEmbeddedEnvironment.java new file mode 100644 index 000000000..451563d2f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/TopLevelEmbeddedEnvironment.java @@ -0,0 +1,81 @@ +package com.jozufozu.flywheel.backend.engine.embed; + +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +import com.jozufozu.flywheel.api.event.RenderStage; +import com.jozufozu.flywheel.backend.Samplers; +import com.jozufozu.flywheel.backend.engine.EngineImpl; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; + +import net.minecraft.world.level.BlockAndTintGetter; + +public class TopLevelEmbeddedEnvironment extends AbstractEmbeddedEnvironment { + private final EmbeddedLightVolume lightVolume = new EmbeddedLightVolume(); + private final EmbeddedLightTexture lightTexture = new EmbeddedLightTexture(); + + public TopLevelEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { + super(engine, renderStage); + } + + @Override + public void flush() { + super.flush(); + + if (lightVolume.empty()) { + return; + } + Samplers.EMBEDDED_LIGHT.makeActive(); + + lightTexture.bind(); + + lightTexture.ensureCapacity(lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ()); + + lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ()); + } + + + @Override + public void collectLight(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) { + lightVolume.collect(level, minX, minY, minZ, sizeX, sizeY, sizeZ); + } + + @Override + public void invalidateLight() { + lightVolume.clear(); + } + + @Override + public void setupLight(GlProgram program) { + if (!lightVolume.empty()) { + Samplers.EMBEDDED_LIGHT.makeActive(); + + lightTexture.bind(); + + float oneOverSizeX = 1f / (float) lightTexture.sizeX; + float oneOverSizeY = 1f / (float) lightTexture.sizeY; + float oneOverSizeZ = 1f / (float) lightTexture.sizeZ; + + program.setVec3("_flw_oneOverLightBoxSize", oneOverSizeX, oneOverSizeY, oneOverSizeZ); + program.setVec3("_flw_lightVolumeMin", lightVolume.x(), lightVolume.y(), lightVolume.z()); + program.setBool("_flw_useLightVolume", true); + } else { + program.setBool("_flw_useLightVolume", false); + } + } + + @Override + public void composeMatrices(Matrix4f pose, Matrix3f normal) { + pose.set(this.pose); + normal.set(this.normal); + } + + @Override + public void actuallyDelete() { + // We could technically free the light volume right away in _delete, but + // the control flow here is so convoluted that it's probably best to do + // everything in one place. + lightVolume.delete(); + lightTexture.delete(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/lib/util/SectionUtil.java b/src/main/java/com/jozufozu/flywheel/lib/util/SectionUtil.java new file mode 100644 index 000000000..9bd7afaf9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/lib/util/SectionUtil.java @@ -0,0 +1,53 @@ +package com.jozufozu.flywheel.lib.util; + +import java.util.Collection; +import java.util.function.LongConsumer; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; + +public class SectionUtil { + public static void containingAll(Collection blocks, LongConsumer consumer) { + if (blocks.isEmpty()) { + return; + } + + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int minZ = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + int maxZ = Integer.MIN_VALUE; + for (BlockPos pos : blocks) { + minX = Math.min(minX, pos.getX()); + minY = Math.min(minY, pos.getY()); + minZ = Math.min(minZ, pos.getZ()); + maxX = Math.max(maxX, pos.getX()); + maxY = Math.max(maxY, pos.getY()); + maxZ = Math.max(maxZ, pos.getZ()); + } + + betweenClosedBlocks(minX, minY, minZ, maxX, maxY, maxZ, consumer); + } + + public static void betweenClosedBox(int x, int y, int z, int sizeX, int sizeY, int sizeZ, LongConsumer consumer) { + betweenClosedBlocks(x, y, z, x + sizeX, y + sizeY, z + sizeZ, consumer); + } + + public static void betweenClosedBlocks(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, LongConsumer consumer) { + int minSectionX = SectionPos.blockToSectionCoord(minX); + int minSectionY = SectionPos.blockToSectionCoord(minY); + int minSectionZ = SectionPos.blockToSectionCoord(minZ); + int maxSectionX = SectionPos.blockToSectionCoord(maxX); + int maxSectionY = SectionPos.blockToSectionCoord(maxY); + int maxSectionZ = SectionPos.blockToSectionCoord(maxZ); + + for (int sectionX = minSectionX; sectionX <= maxSectionX; sectionX++) { + for (int sectionY = minSectionY; sectionY <= maxSectionY; sectionY++) { + for (int sectionZ = minSectionZ; sectionZ <= maxSectionZ; sectionZ++) { + consumer.accept(SectionPos.asLong(sectionX, sectionY, sectionZ)); + } + } + } + } +} diff --git a/src/main/resources/assets/flywheel/flywheel/internal/common.vert b/src/main/resources/assets/flywheel/flywheel/internal/common.vert index e6d2937df..ec009e414 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/common.vert +++ b/src/main/resources/assets/flywheel/flywheel/internal/common.vert @@ -69,8 +69,8 @@ vec2 getCrumblingTexCoord() { #ifdef _FLW_EMBEDDED uniform vec3 _flw_oneOverLightBoxSize; uniform vec3 _flw_lightVolumeMin; -uniform mat4 _flw_model; -uniform mat3 _flw_normal; +uniform mat4 _flw_modelMatrix; +uniform mat3 _flw_normalMatrix; out vec3 _flw_lightVolumeCoord; #endif @@ -87,8 +87,8 @@ void _flw_main(in FlwInstance instance, in uint stableInstanceID) { #endif #ifdef _FLW_EMBEDDED - flw_vertexPos = _flw_model * flw_vertexPos; - flw_vertexNormal = _flw_normal * flw_vertexNormal; + flw_vertexPos = _flw_modelMatrix * flw_vertexPos; + flw_vertexNormal = _flw_normalMatrix * flw_vertexNormal; _flw_lightVolumeCoord = (flw_vertexPos.xyz - _flw_lightVolumeMin) * _flw_oneOverLightBoxSize; #endif diff --git a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl index 20ad771bb..af0fec4f0 100644 --- a/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl +++ b/src/main/resources/assets/flywheel/flywheel/internal/indirect/cull.glsl @@ -17,8 +17,8 @@ layout(std430, binding = _FLW_MODEL_BUFFER_BINDING) restrict buffer ModelBuffer ModelDescriptor _flw_models[]; }; -uniform mat4 _flw_embeddedModel; -uniform bool _flw_useEmbeddedModel = false; +uniform mat4 _flw_modelMatrix; +uniform bool _flw_useModelMatrix = false; // Disgustingly vectorized sphere frustum intersection taking advantage of ahead of time packing. // Only uses 6 fmas and some boolean ops. @@ -44,8 +44,8 @@ bool _flw_isVisible(uint instanceIndex, uint modelIndex) { flw_transformBoundingSphere(instance, center, radius); - if (_flw_useEmbeddedModel) { - transformBoundingSphere(_flw_embeddedModel, center, radius); + if (_flw_useModelMatrix) { + transformBoundingSphere(_flw_modelMatrix, center, radius); } return _flw_testSphere(center, radius);