From 22a90c8e5d3f8f9ff8ba8aae82f952a78a73c10a Mon Sep 17 00:00:00 2001 From: JozsefA Date: Sun, 17 Jan 2021 22:18:31 -0800 Subject: [PATCH] on our way to super fancy contraption lighting --- .../com/simibubi/create/CreateClient.java | 5 +- .../AbstractContraptionEntityRenderer.java | 6 +- .../structureMovement/Contraption.java | 9 + .../ContraptionRenderer.java | 2 +- .../bearing/BearingContraption.java | 8 + .../bearing/BearingLighter.java | 19 ++ .../simibubi/create/events/ClientEvents.java | 3 +- .../foundation/render/ContraptionLighter.java | 149 ------------ .../render/ContraptionRenderDispatcher.java | 111 +++++++++ .../render/FastContraptionRenderer.java | 226 ------------------ .../render/FastKineticRenderer.java | 3 +- .../render/FastRenderDispatcher.java | 5 +- .../create/foundation/render/RenderMath.java | 5 + .../render/RenderedContraption.java | 152 ++++++++++++ .../render/instancing/InstanceContext.java | 6 +- .../render/light/ContraptionLighter.java | 27 +++ .../render/light/CoordinateConsumer.java | 6 + .../foundation/render/light/EmptyLighter.java | 15 ++ .../render/light/GridAlignedBB.java | 203 ++++++++++++++++ .../foundation/render/light/LightVolume.java | 204 ++++++++++++++++ .../render/light/LightVolumeDebugger.java | 18 ++ .../render/shader/ShaderHelper.java | 14 +- src/main/resources/META-INF/mods.toml | 2 +- .../resources/assets/create/shader/belt.vert | 6 +- 24 files changed, 810 insertions(+), 394 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingLighter.java delete mode 100644 src/main/java/com/simibubi/create/foundation/render/ContraptionLighter.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/ContraptionRenderDispatcher.java delete mode 100644 src/main/java/com/simibubi/create/foundation/render/FastContraptionRenderer.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/RenderedContraption.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/light/ContraptionLighter.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/light/CoordinateConsumer.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/light/EmptyLighter.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/light/GridAlignedBB.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/light/LightVolumeDebugger.java diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index 26f17f91c..ca349a611 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -16,7 +16,8 @@ import com.simibubi.create.foundation.block.render.CustomBlockModels; import com.simibubi.create.foundation.block.render.SpriteShifter; import com.simibubi.create.foundation.item.CustomItemModels; import com.simibubi.create.foundation.item.CustomRenderedItems; -import com.simibubi.create.foundation.render.FastContraptionRenderer; +import com.simibubi.create.foundation.render.ContraptionRenderDispatcher; +import com.simibubi.create.foundation.render.RenderedContraption; import com.simibubi.create.foundation.render.FastKineticRenderer; import com.simibubi.create.foundation.render.SuperByteBufferCache; import com.simibubi.create.foundation.utility.outliner.Outliner; @@ -182,6 +183,6 @@ public class CreateClient { public static void invalidateRenderers() { CreateClient.bufferCache.invalidate(); CreateClient.kineticRenderer.invalidate(); - FastContraptionRenderer.invalidateAll(); + ContraptionRenderDispatcher.invalidateAll(); } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java index c81b4ef13..2634d9f64 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java @@ -2,7 +2,9 @@ package com.simibubi.create.content.contraptions.components.structureMovement; import com.mojang.blaze3d.matrix.MatrixStack; -import com.simibubi.create.foundation.render.FastContraptionRenderer; +import com.simibubi.create.foundation.render.ContraptionRenderDispatcher; +import com.simibubi.create.foundation.render.RenderedContraption; +import net.java.games.input.Controller; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.culling.ClippingHelperImpl; @@ -50,7 +52,7 @@ public abstract class AbstractContraptionEntityRenderer makeLighter() { + return new EmptyLighter(this); + } + } \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionRenderer.java index c5c5f0ad4..9b00b8f6e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionRenderer.java @@ -84,7 +84,7 @@ public class ContraptionRenderer { return new SuperByteBuffer(builder); } - protected static BufferBuilder buildStructure(Contraption c, RenderType layer) { + public static BufferBuilder buildStructure(Contraption c, RenderType layer) { if (renderWorld == null || renderWorld.getWorld() != Minecraft.getInstance().world) renderWorld = new PlacementSimulationWorld(Minecraft.getInstance().world); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java index 76db5bd8b..37ea55934 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java @@ -1,5 +1,8 @@ package com.simibubi.create.content.contraptions.components.structureMovement.bearing; +import com.simibubi.create.foundation.render.light.ContraptionLighter; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.commons.lang3.tuple.Pair; import com.simibubi.create.AllTags.AllBlockTags; @@ -88,4 +91,9 @@ public class BearingContraption extends Contraption { return axis == facing.getAxis(); } + @OnlyIn(Dist.CLIENT) + @Override + public ContraptionLighter makeLighter() { + return new BearingLighter(this); + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingLighter.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingLighter.java new file mode 100644 index 000000000..de960b40d --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingLighter.java @@ -0,0 +1,19 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.bearing; + +import com.simibubi.create.foundation.render.light.ContraptionLighter; +import com.simibubi.create.foundation.render.light.GridAlignedBB; +import net.minecraft.util.math.AxisAlignedBB; + +public class BearingLighter extends ContraptionLighter { + + public BearingLighter(BearingContraption contraption) { + super(contraption); + } + + @Override + public GridAlignedBB getContraptionBounds() { + GridAlignedBB localBounds = GridAlignedBB.fromAABB(contraption.bounds); + localBounds.translate(contraption.anchor); + return localBounds; + } +} diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 45858e93d..d0b8fce07 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -26,6 +26,7 @@ import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.LeftClickPacket; import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.render.RenderWork; +import com.simibubi.create.foundation.render.light.LightVolumeDebugger; import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; import com.simibubi.create.foundation.tileEntity.behaviour.edgeInteraction.EdgeInteractionRenderer; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringRenderer; @@ -118,7 +119,6 @@ public class ClientEvents { Vec3d cameraPos = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView(); MatrixStack ms = event.getMatrixStack(); - ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getActiveRenderInfo(); ms.push(); ms.translate(-cameraPos.getX(), -cameraPos.getY(), -cameraPos.getZ()); SuperRenderTypeBuffer buffer = SuperRenderTypeBuffer.getInstance(); @@ -126,6 +126,7 @@ public class ClientEvents { CouplingRenderer.renderAll(ms, buffer); CreateClient.schematicHandler.render(ms, buffer); CreateClient.outliner.renderOutlines(ms, buffer); + LightVolumeDebugger.render(ms, buffer); // CollisionDebugger.render(ms, buffer); buffer.draw(); diff --git a/src/main/java/com/simibubi/create/foundation/render/ContraptionLighter.java b/src/main/java/com/simibubi/create/foundation/render/ContraptionLighter.java deleted file mode 100644 index ec824b8c0..000000000 --- a/src/main/java/com/simibubi/create/foundation/render/ContraptionLighter.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.simibubi.create.foundation.render; - -import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.LightType; -import net.minecraft.world.World; -import org.lwjgl.opengl.*; - -import java.nio.ByteBuffer; - -import static com.simibubi.create.foundation.render.RenderMath.nextPowerOf2; - -public class ContraptionLighter { - - private int minX; - private int minY; - private int minZ; - - private final int sizeX; - private final int sizeY; - private final int sizeZ; - - private SafeDirectBuffer lightVolume; - - private boolean dirty; - - private int texture; - - public ContraptionLighter(Contraption contraption) { - texture = GL11.glGenTextures(); - - AxisAlignedBB bounds = contraption.bounds; - - int minX = (int) Math.floor(bounds.minX) - 1; - int minY = (int) Math.floor(bounds.minY) - 1; - int minZ = (int) Math.floor(bounds.minZ) - 1; - int maxX = (int) Math.ceil(bounds.maxX) + 1; - int maxY = (int) Math.ceil(bounds.maxY) + 1; - int maxZ = (int) Math.ceil(bounds.maxZ) + 1; - - sizeX = nextPowerOf2(maxX - minX); - sizeY = nextPowerOf2(maxY - minY); - sizeZ = nextPowerOf2(maxZ - minZ); - - lightVolume = new SafeDirectBuffer(sizeX * sizeY * sizeZ * 2); - - update(contraption); - } - - public int getSizeX() { - return sizeX; - } - - public int getSizeY() { - return sizeY; - } - - public int getSizeZ() { - return sizeZ; - } - - public int getMinX() { - return minX; - } - - public int getMinY() { - return minY; - } - - public int getMinZ() { - return minZ; - } - - public void delete() { - RenderWork.enqueue(() -> { - GL15.glDeleteTextures(texture); - texture = 0; - try { - lightVolume.close(); - } catch (Exception e) { - e.printStackTrace(); - } - lightVolume = null; - }); - } - - private void setupPosition(Contraption c) { - Vec3d positionVec = c.entity.getPositionVec(); - minX = (int) (Math.floor(positionVec.x) - sizeX / 2); - minY = (int) (Math.floor(positionVec.y) - 1); - minZ = (int) (Math.floor(positionVec.z) - sizeZ / 2); - } - - public void update(Contraption c) { - if (lightVolume == null) return; - - setupPosition(c); - - World world = c.entity.world; - - BlockPos.Mutable pos = new BlockPos.Mutable(); - - for (int x = 0; x < sizeX; x++) { - for (int y = 0; y < sizeY; y++) { - for (int z = 0; z < sizeZ; z++) { - pos.setPos(minX + x, minY + y, minZ + z); - - int blockLight = world.getLightLevel(LightType.BLOCK, pos); - int skyLight = world.getLightLevel(LightType.SKY, pos); - - writeLight(x, y, z, blockLight, skyLight); - } - } - } - - dirty = true; - } - - private void writeLight(int x, int y, int z, int block, int sky) { - int i = (x + sizeX * (y + z * sizeY)) * 2; - - byte b = (byte) ((block & 0xF) << 4); - byte s = (byte) ((sky & 0xF) << 4); - - lightVolume.put(i, b); - lightVolume.put(i + 1, s); - } - - public void use() { - if (texture == 0 || lightVolume == null) return; - - GL12.glBindTexture(GL12.GL_TEXTURE_3D, texture); - GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR); - GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR); - GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP); - GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL13.GL_CLAMP); - GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP); - if (dirty) { - GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL40.GL_RG8, sizeX, sizeY, sizeZ, 0, GL40.GL_RG, GL40.GL_UNSIGNED_BYTE, lightVolume.getBacking()); - dirty = false; - } - } - - public void release() { - GL12.glBindTexture(GL12.GL_TEXTURE_3D, 0); - } -} diff --git a/src/main/java/com/simibubi/create/foundation/render/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/ContraptionRenderDispatcher.java new file mode 100644 index 000000000..c06c4ee2d --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/ContraptionRenderDispatcher.java @@ -0,0 +1,111 @@ +package com.simibubi.create.foundation.render; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import com.simibubi.create.foundation.render.light.LightVolume; +import com.simibubi.create.foundation.render.shader.Shader; +import com.simibubi.create.foundation.render.shader.ShaderCallback; +import com.simibubi.create.foundation.render.shader.ShaderHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Matrix4f; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.SectionPos; +import net.minecraft.world.ILightReader; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL40; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ContraptionRenderDispatcher { + public static final HashMap renderers = new HashMap<>(); + + public static void markForRendering(World world, Contraption c, MatrixStack model) { + getRenderer(world, c).setRenderSettings(model.peek().getModel()); + } + + public static void notifyLightUpdate(ILightReader world, LightType type, SectionPos pos) { + for (RenderedContraption renderer : renderers.values()) { + renderer.getLighter().lightVolume.notifyLightUpdate(world, type, pos); + } + } + + private static RenderedContraption getRenderer(World world, Contraption c) { + RenderedContraption renderer; + int entityId = c.entity.getEntityId(); + if (renderers.containsKey(entityId)) { + renderer = renderers.get(entityId); + } else { + renderer = new RenderedContraption(world, c); + renderers.put(entityId, renderer); + } + + return renderer; + } + + public static void renderLayer(RenderType renderType, Matrix4f projectionMat, Matrix4f viewMat) { + removeDeadContraptions(); + + if (renderers.isEmpty()) return; + + FastKineticRenderer.setup(Minecraft.getInstance().gameRenderer); + GL11.glEnable(GL13.GL_TEXTURE_3D); + GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4 + + ShaderCallback callback = ShaderHelper.getViewProjectionCallback(projectionMat, viewMat); + + int structureShader = ShaderHelper.useShader(Shader.CONTRAPTION_STRUCTURE, callback); + for (RenderedContraption renderer : renderers.values()) { + renderer.doRenderLayer(renderType, structureShader); + } + + if (renderType == FastKineticRenderer.getKineticRenderLayer()) { + int rotatingShader = ShaderHelper.useShader(Shader.CONTRAPTION_ROTATING, callback); + for (RenderedContraption renderer : renderers.values()) { + renderer.setup(rotatingShader); + renderer.kinetics.renderRotating(); + renderer.teardown(); + } + + int beltShader = ShaderHelper.useShader(Shader.CONTRAPTION_BELT, callback); + for (RenderedContraption renderer : renderers.values()) { + renderer.setup(beltShader); + renderer.kinetics.renderBelts(); + renderer.teardown(); + } + } + + ShaderHelper.releaseShader(); + + GL11.glDisable(GL13.GL_TEXTURE_3D); + FastKineticRenderer.teardown(); + GL13.glActiveTexture(GL40.GL_TEXTURE0); + } + + public static void removeDeadContraptions() { + ArrayList toRemove = new ArrayList<>(); + + for (RenderedContraption renderer : renderers.values()) { + if (renderer.isDead()) { + toRemove.add(renderer.getEntityId()); + renderer.invalidate(); + } + } + + for (Integer id : toRemove) { + renderers.remove(id); + } + } + + public static void invalidateAll() { + for (RenderedContraption renderer : renderers.values()) { + renderer.invalidate(); + } + + renderers.clear(); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/FastContraptionRenderer.java b/src/main/java/com/simibubi/create/foundation/render/FastContraptionRenderer.java deleted file mode 100644 index 05589b6b4..000000000 --- a/src/main/java/com/simibubi/create/foundation/render/FastContraptionRenderer.java +++ /dev/null @@ -1,226 +0,0 @@ -package com.simibubi.create.foundation.render; - -import com.mojang.blaze3d.matrix.MatrixStack; -import com.mojang.blaze3d.platform.GlStateManager; -import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; -import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRenderer; -import com.simibubi.create.foundation.render.instancing.IInstanceRendered; -import com.simibubi.create.foundation.render.instancing.IInstancedTileEntityRenderer; -import com.simibubi.create.foundation.render.shader.Shader; -import com.simibubi.create.foundation.render.shader.ShaderCallback; -import com.simibubi.create.foundation.render.shader.ShaderHelper; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.Matrix4f; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.tileentity.TileEntityRenderer; -import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.world.World; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL13; -import org.lwjgl.opengl.GL40; - -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class FastContraptionRenderer extends ContraptionRenderer { - - private static final HashMap renderers = new HashMap<>(); - - private HashMap renderLayers = new HashMap<>(); - - private ContraptionLighter lighter; - - public final FastKineticRenderer kinetics; - - private Contraption c; - - private Matrix4f model; - - public FastContraptionRenderer(World world, Contraption c) { - this.c = c; - this.lighter = new ContraptionLighter(c); - this.kinetics = new FastKineticRenderer(); - - buildLayers(c); - buildInstancedTiles(c); - } - - private void buildLayers(Contraption c) { - for (ContraptionBuffer buffer : renderLayers.values()) { - buffer.delete(); - } - - renderLayers.clear(); - - List blockLayers = RenderType.getBlockLayers(); - - for (RenderType layer : blockLayers) { - renderLayers.put(layer, buildStructureBuffer(c, layer)); - } - } - - private void buildInstancedTiles(Contraption c) { - List tileEntities = c.renderedTileEntities; - if (!tileEntities.isEmpty()) { - for (TileEntity te : tileEntities) { - if (te instanceof IInstanceRendered) { - TileEntityRenderer renderer = TileEntityRendererDispatcher.instance.getRenderer(te); - - if (renderer instanceof IInstancedTileEntityRenderer) { - kinetics.addInstancedData(this, te, (IInstancedTileEntityRenderer) renderer); - } - } - } - } - - kinetics.markAllDirty(); - } - - public static void tick() { - if (Minecraft.getInstance().isGamePaused()) return; - - for (FastContraptionRenderer renderer : renderers.values()) { - renderer.lighter.update(renderer.c); - } - } - - private void setRenderSettings(Matrix4f model) { - this.model = model; - } - - private void setup(int shader) { - setupShaderUniforms(shader); - lighter.use(); - } - - private void teardown() { - lighter.release(); - } - - private void setupShaderUniforms(int shader) { - FloatBuffer buf = ShaderHelper.VEC3_BUFFER; - - int lightBoxSize = GlStateManager.getUniformLocation(shader, "lightBoxSize"); - buf.put(0, (float) lighter.getSizeX()); - buf.put(1, (float) lighter.getSizeY()); - buf.put(2, (float) lighter.getSizeZ()); - buf.rewind(); - GlStateManager.uniform3(lightBoxSize, buf); - - int lightBoxMin = GlStateManager.getUniformLocation(shader, "lightBoxMin"); - buf.put(0, (float) lighter.getMinX()); - buf.put(1, (float) lighter.getMinY()); - buf.put(2, (float) lighter.getMinZ()); - buf.rewind(); - GlStateManager.uniform3(lightBoxMin, buf); - - int model = GlStateManager.getUniformLocation(shader, "model"); - this.model.write(ShaderHelper.MATRIX_BUFFER); - ShaderHelper.MATRIX_BUFFER.rewind(); - GlStateManager.uniformMatrix4(model, false, ShaderHelper.MATRIX_BUFFER); - } - - private void invalidate() { - for (ContraptionBuffer buffer : renderLayers.values()) { - buffer.delete(); - } - renderLayers.clear(); - - lighter.delete(); - - kinetics.invalidate(); - } - - public static void markForRendering(World world, Contraption c, MatrixStack model) { - getRenderer(world, c).setRenderSettings(model.peek().getModel()); - } - - private static FastContraptionRenderer getRenderer(World world, Contraption c) { - FastContraptionRenderer renderer; - int entityId = c.entity.getEntityId(); - if (renderers.containsKey(entityId)) { - renderer = renderers.get(entityId); - } else { - renderer = new FastContraptionRenderer(world, c); - renderers.put(entityId, renderer); - } - - return renderer; - } - - public static void renderLayer(RenderType renderType, Matrix4f projectionMat, Matrix4f viewMat) { - removeDeadContraptions(); - - if (renderers.isEmpty()) return; - - FastKineticRenderer.setup(Minecraft.getInstance().gameRenderer); - GL11.glEnable(GL13.GL_TEXTURE_3D); - GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4 - - ShaderCallback callback = ShaderHelper.getViewProjectionCallback(projectionMat, viewMat); - - int structureShader = ShaderHelper.useShader(Shader.CONTRAPTION_STRUCTURE, callback); - for (FastContraptionRenderer renderer : renderers.values()) { - ContraptionBuffer buffer = renderer.renderLayers.get(renderType); - if (buffer != null) { - renderer.setup(structureShader); - buffer.render(); - renderer.teardown(); - } - } - - if (renderType == FastKineticRenderer.getKineticRenderLayer()) { - int rotatingShader = ShaderHelper.useShader(Shader.CONTRAPTION_ROTATING, callback); - for (FastContraptionRenderer renderer : renderers.values()) { - renderer.setup(rotatingShader); - renderer.kinetics.renderRotating(); - renderer.teardown(); - } - - int beltShader = ShaderHelper.useShader(Shader.CONTRAPTION_BELT, callback); - for (FastContraptionRenderer renderer : renderers.values()) { - renderer.setup(beltShader); - renderer.kinetics.renderBelts(); - renderer.teardown(); - } - } - - ShaderHelper.releaseShader(); - - GL11.glDisable(GL13.GL_TEXTURE_3D); - FastKineticRenderer.teardown(); - GL13.glActiveTexture(GL40.GL_TEXTURE0); - } - - public static void removeDeadContraptions() { - ArrayList toRemove = new ArrayList<>(); - - for (FastContraptionRenderer renderer : renderers.values()) { - if (!renderer.c.entity.isAlive()) { - toRemove.add(renderer.c.entity.getEntityId()); - renderer.invalidate(); - } - } - - for (Integer id : toRemove) { - renderers.remove(id); - } - } - - public static void invalidateAll() { - for (FastContraptionRenderer renderer : renderers.values()) { - renderer.invalidate(); - } - - renderers.clear(); - } - - private static ContraptionBuffer buildStructureBuffer(Contraption c, RenderType layer) { - BufferBuilder builder = buildStructure(c, layer); - return new ContraptionBuffer(builder); - } -} diff --git a/src/main/java/com/simibubi/create/foundation/render/FastKineticRenderer.java b/src/main/java/com/simibubi/create/foundation/render/FastKineticRenderer.java index c9de28113..437bf1387 100644 --- a/src/main/java/com/simibubi/create/foundation/render/FastKineticRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/render/FastKineticRenderer.java @@ -70,7 +70,7 @@ public class FastKineticRenderer { renderer.addInstanceData(new InstanceContext.World<>(te)); } - void addInstancedData(FastContraptionRenderer c, T te, IInstancedTileEntityRenderer renderer) { + void addInstancedData(RenderedContraption c, T te, IInstancedTileEntityRenderer renderer) { renderer.addInstanceData(new InstanceContext.Contraption<>(te, c)); } @@ -87,6 +87,7 @@ public class FastKineticRenderer { runOnAll(InstanceBuffer::delete); belts.values().forEach(Cache::invalidateAll); rotating.values().forEach(Cache::invalidateAll); + dirty = true; } private void runOnAll(Consumer> f) { diff --git a/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java index fca350101..6054e1b00 100644 --- a/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java @@ -19,6 +19,7 @@ import net.minecraft.potion.Effects; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.SectionPos; +import net.minecraft.world.ILightReader; import net.minecraft.world.LightType; import net.minecraft.world.chunk.Chunk; @@ -42,11 +43,11 @@ public class FastRenderDispatcher { CreateClient.kineticRenderer.renderInstancesAsWorld(type, projection, view); } - FastContraptionRenderer.renderLayer(type, projection, view); + ContraptionRenderDispatcher.renderLayer(type, projection, view); } public static void notifyLightUpdate(ClientChunkProvider world, LightType type, SectionPos pos) { - FastContraptionRenderer.tick(); + ContraptionRenderDispatcher.notifyLightUpdate((ILightReader) world.getWorld(), type, pos); Chunk chunk = world.getChunk(pos.getSectionX(), pos.getSectionZ(), false); diff --git a/src/main/java/com/simibubi/create/foundation/render/RenderMath.java b/src/main/java/com/simibubi/create/foundation/render/RenderMath.java index 7d143e4f7..3839ff718 100644 --- a/src/main/java/com/simibubi/create/foundation/render/RenderMath.java +++ b/src/main/java/com/simibubi/create/foundation/render/RenderMath.java @@ -5,4 +5,9 @@ public class RenderMath { int h = Integer.highestOneBit(a); return (h == a) ? h : (h << 1); } + + public static boolean isPowerOf2(int n) { + int b = n & (n - 1); + return b == 0 && n != 0; + } } diff --git a/src/main/java/com/simibubi/create/foundation/render/RenderedContraption.java b/src/main/java/com/simibubi/create/foundation/render/RenderedContraption.java new file mode 100644 index 000000000..07f87978c --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/RenderedContraption.java @@ -0,0 +1,152 @@ +package com.simibubi.create.foundation.render; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRenderer; +import com.simibubi.create.foundation.render.instancing.IInstanceRendered; +import com.simibubi.create.foundation.render.instancing.IInstancedTileEntityRenderer; +import com.simibubi.create.foundation.render.light.ContraptionLighter; +import com.simibubi.create.foundation.render.light.LightVolume; +import com.simibubi.create.foundation.render.shader.ShaderHelper; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.Matrix4f; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.tileentity.TileEntityRenderer; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.SectionPos; +import net.minecraft.world.ILightReader; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import org.lwjgl.opengl.GL20; + +import java.nio.FloatBuffer; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +public class RenderedContraption { + private HashMap renderLayers = new HashMap<>(); + + private final ContraptionLighter lighter; + + public final FastKineticRenderer kinetics; + + private Contraption contraption; + + private Matrix4f model; + + public RenderedContraption(World world, Contraption contraption) { + this.contraption = contraption; + this.lighter = contraption.makeLighter(); + this.kinetics = new FastKineticRenderer(); + + buildLayers(contraption); + buildInstancedTiles(contraption); + } + + public int getEntityId() { + return contraption.entity.getEntityId(); + } + + public boolean isDead() { + return !contraption.entity.isAlive(); + } + + public ContraptionLighter getLighter() { + return lighter; + } + + public void doRenderLayer(RenderType layer, int shader) { + ContraptionBuffer buffer = renderLayers.get(layer); + if (buffer != null) { + setup(shader); + buffer.render(); + teardown(); + } + } + + private void buildLayers(Contraption c) { + for (ContraptionBuffer buffer : renderLayers.values()) { + buffer.delete(); + } + + renderLayers.clear(); + + List blockLayers = RenderType.getBlockLayers(); + + for (RenderType layer : blockLayers) { + renderLayers.put(layer, buildStructureBuffer(c, layer)); + } + } + + private void buildInstancedTiles(Contraption c) { + List tileEntities = c.renderedTileEntities; + if (!tileEntities.isEmpty()) { + for (TileEntity te : tileEntities) { + if (te instanceof IInstanceRendered) { + TileEntityRenderer renderer = TileEntityRendererDispatcher.instance.getRenderer(te); + + if (renderer instanceof IInstancedTileEntityRenderer) { + kinetics.addInstancedData(this, te, (IInstancedTileEntityRenderer) renderer); + } + } + } + } + + kinetics.markAllDirty(); + } + + void setRenderSettings(Matrix4f model) { + this.model = model; + } + + void setup(int shader) { + setupShaderUniforms(shader); + lighter.lightVolume.use(); + } + + void teardown() { + lighter.lightVolume.release(); + } + + void setupShaderUniforms(int shader) { + FloatBuffer buf = ShaderHelper.VEC3_BUFFER; + + int lightBoxSize = GlStateManager.getUniformLocation(shader, "lightBoxSize"); + buf.put(0, (float) lighter.lightVolume.getSizeX()); + buf.put(1, (float) lighter.lightVolume.getSizeY()); + buf.put(2, (float) lighter.lightVolume.getSizeZ()); + buf.rewind(); + GlStateManager.uniform3(lightBoxSize, buf); + + int lightBoxMin = GlStateManager.getUniformLocation(shader, "lightBoxMin"); + buf.put(0, (float) lighter.lightVolume.getMinX()); + buf.put(1, (float) lighter.lightVolume.getMinY()); + buf.put(2, (float) lighter.lightVolume.getMinZ()); + buf.rewind(); + GlStateManager.uniform3(lightBoxMin, buf); + + int model = GlStateManager.getUniformLocation(shader, "model"); + this.model.write(ShaderHelper.MATRIX_BUFFER); + ShaderHelper.MATRIX_BUFFER.rewind(); + GlStateManager.uniformMatrix4(model, false, ShaderHelper.MATRIX_BUFFER); + } + + void invalidate() { + for (ContraptionBuffer buffer : renderLayers.values()) { + buffer.delete(); + } + renderLayers.clear(); + + lighter.lightVolume.delete(); + + kinetics.invalidate(); + } + + private static ContraptionBuffer buildStructureBuffer(Contraption c, RenderType layer) { + BufferBuilder builder = ContraptionRenderer.buildStructure(c, layer); + return new ContraptionBuffer(builder); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/instancing/InstanceContext.java b/src/main/java/com/simibubi/create/foundation/render/instancing/InstanceContext.java index 4ec2fbd64..354c149b4 100644 --- a/src/main/java/com/simibubi/create/foundation/render/instancing/InstanceContext.java +++ b/src/main/java/com/simibubi/create/foundation/render/instancing/InstanceContext.java @@ -1,7 +1,7 @@ package com.simibubi.create.foundation.render.instancing; import com.simibubi.create.CreateClient; -import com.simibubi.create.foundation.render.FastContraptionRenderer; +import com.simibubi.create.foundation.render.RenderedContraption; import com.simibubi.create.foundation.render.FastKineticRenderer; import net.minecraft.tileentity.TileEntity; @@ -19,9 +19,9 @@ public abstract class InstanceContext { public static class Contraption extends InstanceContext { - public final FastContraptionRenderer c; + public final RenderedContraption c; - public Contraption(T te, FastContraptionRenderer c) { + public Contraption(T te, RenderedContraption c) { super(te); this.c = c; } diff --git a/src/main/java/com/simibubi/create/foundation/render/light/ContraptionLighter.java b/src/main/java/com/simibubi/create/foundation/render/light/ContraptionLighter.java new file mode 100644 index 000000000..648055de4 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/light/ContraptionLighter.java @@ -0,0 +1,27 @@ +package com.simibubi.create.foundation.render.light; + +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import net.minecraft.util.math.SectionPos; +import net.minecraft.world.ILightReader; +import net.minecraft.world.LightType; + +import static com.simibubi.create.foundation.render.RenderMath.nextPowerOf2; + +public abstract class ContraptionLighter { + protected final C contraption; + public final LightVolume lightVolume; + + protected ContraptionLighter(C contraption) { + this.contraption = contraption; + + GridAlignedBB bounds = getContraptionBounds(); + bounds.grow(1); // so we have at least enough data on the edges to avoid artifacts + bounds.nextPowerOf2Centered(); + + lightVolume = new LightVolume(bounds); + + lightVolume.initialize(contraption.entity.world); + } + + public abstract GridAlignedBB getContraptionBounds(); +} diff --git a/src/main/java/com/simibubi/create/foundation/render/light/CoordinateConsumer.java b/src/main/java/com/simibubi/create/foundation/render/light/CoordinateConsumer.java new file mode 100644 index 000000000..ff9ee492b --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/light/CoordinateConsumer.java @@ -0,0 +1,6 @@ +package com.simibubi.create.foundation.render.light; + +@FunctionalInterface +public interface CoordinateConsumer { + void consume(int x, int y, int z); +} diff --git a/src/main/java/com/simibubi/create/foundation/render/light/EmptyLighter.java b/src/main/java/com/simibubi/create/foundation/render/light/EmptyLighter.java new file mode 100644 index 000000000..e942664c7 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/light/EmptyLighter.java @@ -0,0 +1,15 @@ +package com.simibubi.create.foundation.render.light; + +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; + +// so other contraptions don't crash before they have a lighter +public class EmptyLighter extends ContraptionLighter { + public EmptyLighter(Contraption contraption) { + super(contraption); + } + + @Override + public GridAlignedBB getContraptionBounds() { + return new GridAlignedBB(0, 0, 0, 1, 1, 1); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/light/GridAlignedBB.java b/src/main/java/com/simibubi/create/foundation/render/light/GridAlignedBB.java new file mode 100644 index 000000000..03eb94efd --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/light/GridAlignedBB.java @@ -0,0 +1,203 @@ +package com.simibubi.create.foundation.render.light; + +import com.simibubi.create.foundation.render.RenderMath; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.SectionPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.LightType; + +import java.util.function.IntFunction; + +import static com.simibubi.create.foundation.render.RenderMath.isPowerOf2; + +public class GridAlignedBB { + public int minX; + public int minY; + public int minZ; + public int maxX; + public int maxY; + public int maxZ; + + public GridAlignedBB(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + } + + public static GridAlignedBB copy(GridAlignedBB bb) { + return new GridAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); + } + + public static GridAlignedBB fromAABB(AxisAlignedBB aabb) { + int minX = (int) Math.floor(aabb.minX); + int minY = (int) Math.floor(aabb.minY); + int minZ = (int) Math.floor(aabb.minZ); + int maxX = (int) Math.ceil(aabb.maxX); + int maxY = (int) Math.ceil(aabb.maxY); + int maxZ = (int) Math.ceil(aabb.maxZ); + return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public static GridAlignedBB fromSection(SectionPos pos) { + return new GridAlignedBB(pos.getWorldStartX(), + pos.getWorldStartY(), + pos.getWorldStartZ(), + pos.getWorldEndX() + 1, + pos.getWorldEndY() + 1, + pos.getWorldEndZ() + 1); + } + + public static AxisAlignedBB toAABB(GridAlignedBB bb) { + return new AxisAlignedBB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); + } + + public int sizeX() { + return maxX - minX; + } + + public int sizeY() { + return maxY - minY; + } + + public int sizeZ() { + return maxZ - minZ; + } + + public int volume() { + return sizeX() * sizeY() * sizeZ(); + } + + public boolean empty() { + // if any dimension has side length 0 this box contains no volume + return minX == maxX || + minY == maxY || + minZ == maxZ; + } + + public void translate(Vec3i by) { + this.minX += by.getX(); + this.minY += by.getY(); + this.minZ += by.getZ(); + this.maxX += by.getX(); + this.maxY += by.getY(); + this.maxZ += by.getZ(); + } + + /** + * Grow this bounding box to have power of 2 side length, scaling from the center. + */ + public void nextPowerOf2Centered() { + int sizeX = sizeX(); + int sizeY = sizeY(); + int sizeZ = sizeZ(); + + int newSizeX = RenderMath.nextPowerOf2(sizeX); + int newSizeY = RenderMath.nextPowerOf2(sizeY); + int newSizeZ = RenderMath.nextPowerOf2(sizeZ); + + int diffX = newSizeX - sizeX; + int diffY = newSizeY - sizeY; + int diffZ = newSizeZ - sizeZ; + + this.minX -= diffX / 2; // floor division for the minimums + this.minY -= diffY / 2; + this.minZ -= diffZ / 2; + this.maxX += (diffX + 1) / 2; // ceiling divison for the maximums + this.maxY += (diffY + 1) / 2; + this.maxZ += (diffZ + 1) / 2; + } + + /** + * Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords. + */ + public void nextPowerOf2() { + int sizeX = RenderMath.nextPowerOf2(sizeX()); + int sizeY = RenderMath.nextPowerOf2(sizeY()); + int sizeZ = RenderMath.nextPowerOf2(sizeZ()); + + this.maxX = this.minX + sizeX; + this.maxY = this.minY + sizeY; + this.maxZ = this.minZ + sizeZ; + } + + public boolean hasPowerOf2Sides() { + // this is only true if all individual side lengths are powers of 2 + return isPowerOf2(volume()); + } + + public void grow(int s) { + this.grow(s, s, s); + } + + public void grow(int x, int y, int z) { + this.minX -= x; + this.minY -= y; + this.minZ -= z; + this.maxX += x; + this.maxY += y; + this.maxZ += z; + } + + public GridAlignedBB intersect(GridAlignedBB other) { + int minX = Math.max(this.minX, other.minX); + int minY = Math.max(this.minY, other.minY); + int minZ = Math.max(this.minZ, other.minZ); + int maxX = Math.min(this.maxX, other.maxX); + int maxY = Math.min(this.maxY, other.maxY); + int maxZ = Math.min(this.maxZ, other.maxZ); + return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public void intersectAssign(GridAlignedBB other) { + this.minX = Math.max(this.minX, other.minX); + this.minY = Math.max(this.minY, other.minY); + this.minZ = Math.max(this.minZ, other.minZ); + this.maxX = Math.min(this.maxX, other.maxX); + this.maxY = Math.min(this.maxY, other.maxY); + this.maxZ = Math.min(this.maxZ, other.maxZ); + } + + public GridAlignedBB union(GridAlignedBB other) { + int minX = Math.min(this.minX, other.minX); + int minY = Math.min(this.minY, other.minY); + int minZ = Math.min(this.minZ, other.minZ); + int maxX = Math.max(this.maxX, other.maxX); + int maxY = Math.max(this.maxY, other.maxY); + int maxZ = Math.max(this.maxZ, other.maxZ); + return new GridAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); + } + + public void unionAssign(GridAlignedBB other) { + this.minX = Math.min(this.minX, other.minX); + this.minY = Math.min(this.minY, other.minY); + this.minZ = Math.min(this.minZ, other.minZ); + this.maxX = Math.max(this.maxX, other.maxX); + this.maxY = Math.max(this.maxY, other.maxY); + this.maxZ = Math.max(this.maxZ, other.maxZ); + } + + public boolean intersects(GridAlignedBB other) { + return this.intersects(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ); + } + + public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ; + } + + public void forEachContained(CoordinateConsumer func) { + if (empty()) return; + + for (int x = minX; x < maxX; x++) { + for (int y = Math.max(minY, 0); y < Math.min(maxY, 255); y++) { // clamp to world height limits + for (int z = minZ; z < maxZ; z++) { + func.consume(x, y, z); + } + } + } + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java b/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java new file mode 100644 index 000000000..7ab56ccd8 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java @@ -0,0 +1,204 @@ +package com.simibubi.create.foundation.render.light; + +import com.simibubi.create.foundation.render.RenderWork; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.SectionPos; +import net.minecraft.world.ILightReader; +import net.minecraft.world.LightType; +import org.lwjgl.opengl.*; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +public class LightVolume { + + private final GridAlignedBB volume; + private ByteBuffer lightData; + + private boolean bufferDirty; + + private int glTexture; + + public LightVolume(GridAlignedBB volume) { + // the gpu requires that all textures have power of 2 side lengths + if (!volume.hasPowerOf2Sides()) + throw new IllegalArgumentException("LightVolume must have power of 2 side lengths"); + + this.volume = volume; + + this.glTexture = GL11.glGenTextures(); + this.lightData = MemoryUtil.memAlloc(this.volume.volume() * 2); // TODO: maybe figure out how to pack light coords into a single byte + } + + public GridAlignedBB getBox() { + return GridAlignedBB.copy(volume); + } + + public int getMinX() { + return volume.minX; + } + + public int getMinY() { + return volume.minY; + } + + public int getMinZ() { + return volume.minZ; + } + + public int getMaxX() { + return volume.maxX; + } + + public int getMaxY() { + return volume.maxY; + } + + public int getMaxZ() { + return volume.maxZ; + } + + public int getSizeX() { + return volume.sizeX(); + } + + public int getSizeY() { + return volume.sizeY(); + } + + public int getSizeZ() { + return volume.sizeZ(); + } + + + public void notifyLightUpdate(ILightReader world, LightType type, SectionPos location) { + GridAlignedBB changedVolume = GridAlignedBB.fromSection(location); + changedVolume.intersectAssign(volume); // compute the region contained by us that has dirty lighting data. + + if (!changedVolume.empty()) { + if (type == LightType.BLOCK) copyBlock(world, changedVolume); + else if (type == LightType.SKY) copySky(world, changedVolume); + } + } + + /** + * Completely (re)populate this volume with block and sky lighting data. + * This is expensive and should be avoided. + */ + public void initialize(ILightReader world) { + BlockPos.Mutable pos = new BlockPos.Mutable(); + + int shiftX = volume.minX; + int shiftY = volume.minY; + int shiftZ = volume.minZ; + + volume.forEachContained((x, y, z) -> { + pos.setPos(x, y, z); + + int blockLight = world.getLightLevel(LightType.BLOCK, pos); + int skyLight = world.getLightLevel(LightType.SKY, pos); + + writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight); + }); + + bufferDirty = true; + } + + /** + * Copy block light from the world into this volume. + * @param worldVolume the region in the world to copy data from. + */ + public void copyBlock(ILightReader world, GridAlignedBB worldVolume) { + BlockPos.Mutable pos = new BlockPos.Mutable(); + + int xShift = volume.minX; + int yShift = volume.minY; + int zShift = volume.minZ; + + worldVolume.forEachContained((x, y, z) -> { + pos.setPos(x, y, z); + + int light = world.getLightLevel(LightType.BLOCK, pos); + + writeBlock(x - xShift, y - yShift, z - zShift, light); + }); + + bufferDirty = true; + } + + /** + * Copy sky light from the world into this volume. + * @param worldVolume the region in the world to copy data from. + */ + public void copySky(ILightReader world, GridAlignedBB worldVolume) { + BlockPos.Mutable pos = new BlockPos.Mutable(); + + int xShift = volume.minX; + int yShift = volume.minY; + int zShift = volume.minZ; + + worldVolume.forEachContained((x, y, z) -> { + pos.setPos(x, y, z); + + int light = world.getLightLevel(LightType.SKY, pos); + + writeSky(x - xShift, y - yShift, z - zShift, light); + }); + + bufferDirty = true; + } + + public void use() { + // just in case something goes wrong or we accidentally call this before this volume is properly disposed of. + if (glTexture == 0 || lightData == null) return; + + GL12.glBindTexture(GL12.GL_TEXTURE_3D, glTexture); + GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MIN_FILTER, GL13.GL_LINEAR); + GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_MAG_FILTER, GL13.GL_LINEAR); + GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_S, GL20.GL_MIRRORED_REPEAT); + GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_R, GL20.GL_MIRRORED_REPEAT); + GL11.glTexParameteri(GL13.GL_TEXTURE_3D, GL13.GL_TEXTURE_WRAP_T, GL20.GL_MIRRORED_REPEAT); + if (bufferDirty) { + GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL40.GL_RG8, volume.sizeX(), volume.sizeY(), volume.sizeZ(), 0, GL40.GL_RG, GL40.GL_UNSIGNED_BYTE, lightData); + bufferDirty = false; + } + } + + public void release() { + GL12.glBindTexture(GL12.GL_TEXTURE_3D, 0); + } + + public void delete() { + RenderWork.enqueue(() -> { + GL15.glDeleteTextures(glTexture); + glTexture = 0; + MemoryUtil.memFree(lightData); + lightData = null; + }); + } + + private void writeLight(int x, int y, int z, int block, int sky) { + byte b = (byte) ((block & 0xF) << 4); + byte s = (byte) ((sky & 0xF) << 4); + + int i = index(x, y, z); + lightData.put(i, b); + lightData.put(i + 1, s); + } + + private void writeBlock(int x, int y, int z, int block) { + byte b = (byte) ((block & 0xF) << 4); + + lightData.put(index(x, y, z), b); + } + + private void writeSky(int x, int y, int z, int sky) { + byte b = (byte) ((sky & 0xF) << 4); + + lightData.put(index(x, y, z) + 1, b); + } + + private int index(int x, int y, int z) { + return (x + volume.sizeX() * (y + z * volume.sizeY())) * 2; + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/light/LightVolumeDebugger.java b/src/main/java/com/simibubi/create/foundation/render/light/LightVolumeDebugger.java new file mode 100644 index 000000000..11991177c --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/light/LightVolumeDebugger.java @@ -0,0 +1,18 @@ +package com.simibubi.create.foundation.render.light; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.foundation.render.ContraptionRenderDispatcher; +import com.simibubi.create.foundation.render.RenderedContraption; +import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; +import com.simibubi.create.foundation.utility.outliner.AABBOutline; +import com.simibubi.create.foundation.utility.outliner.Outline; + +public class LightVolumeDebugger { + public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) { + ContraptionRenderDispatcher.renderers.values() + .stream() + .map(r -> r.getLighter().lightVolume.getBox()) + .map(volume -> new AABBOutline(GridAlignedBB.toAABB(volume))) + .forEach(outline -> outline.render(ms, buffer)); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/shader/ShaderHelper.java b/src/main/java/com/simibubi/create/foundation/render/shader/ShaderHelper.java index 4b6de432a..63e79bcf4 100644 --- a/src/main/java/com/simibubi/create/foundation/render/shader/ShaderHelper.java +++ b/src/main/java/com/simibubi/create/foundation/render/shader/ShaderHelper.java @@ -13,6 +13,8 @@ import net.minecraft.resources.IReloadableResourceManager; import net.minecraft.resources.IResourceManager; import net.minecraft.resources.IResourceManagerReloadListener; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.resource.ISelectiveResourceReloadListener; +import net.minecraftforge.resource.VanillaResourceType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lwjgl.system.MemoryUtil; @@ -41,11 +43,13 @@ public class ShaderHelper { if (Minecraft.getInstance() != null && Minecraft.getInstance().getResourceManager() instanceof IReloadableResourceManager) { ((IReloadableResourceManager) Minecraft.getInstance().getResourceManager()).addReloadListener( - (IResourceManagerReloadListener) manager -> { - PROGRAMS.values().forEach(ShaderLinkHelper::deleteShader); - PROGRAMS.clear(); - for (Shader shader : Shader.values()) { - createProgram(manager, shader); + (ISelectiveResourceReloadListener) (manager, predicate) -> { + if (predicate.test(VanillaResourceType.SHADERS)) { + PROGRAMS.values().forEach(ShaderLinkHelper::deleteShader); + PROGRAMS.clear(); + for (Shader shader : Shader.values()) { + createProgram(manager, shader); + } } }); } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 3b69e270c..a77dc0b61 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -14,7 +14,7 @@ Technology that empowers the player.''' [[dependencies.create]] modId="forge" mandatory=true - versionRange="[31.2.0,)" + versionRange="[31.2.44,)" ordering="NONE" side="BOTH" diff --git a/src/main/resources/assets/create/shader/belt.vert b/src/main/resources/assets/create/shader/belt.vert index 125835300..e1d1a1168 100644 --- a/src/main/resources/assets/create/shader/belt.vert +++ b/src/main/resources/assets/create/shader/belt.vert @@ -55,13 +55,17 @@ void main() { float scroll = fract(speed * time / (36 * 16.)) * scrollSize * scrollMult; - Diffuse = diffuse(normalize((rotation * vec4(aNormal, 0.)).xyz)); + vec3 norm = (rotation * vec4(aNormal, 0.)).xyz; + + Diffuse = diffuse(norm); Light = light; TexCoords = aTexCoords - sourceUV + scrollTexture.xy + vec2(0., scroll); gl_Position = projection * view * renderPos; if (debug == 1) { Color = vec4(networkTint, 1); + } else if (debug == 2) { + Color = vec4(norm, 1); } else { Color = vec4(1); }