diff --git a/gradle.properties b/gradle.properties index d0c3d6c3c..5a937b953 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ cursegradle_version = 1.4.0 # dependency versions registrate_version = 1.0.4 -flywheel_version = 1.16-0.2.0.31 +flywheel_version = 1.16-0.2.0.32 jei_version = 7.7.1.110 # curseforge information diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java index fe20059fa..57e249dcf 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java @@ -37,10 +37,9 @@ public class ContraptionEntityRenderer exte int overlay) { super.render(entity, yaw, partialTicks, ms, buffers, overlay); - ContraptionMatrices matrices = new ContraptionMatrices(ms, entity); Contraption contraption = entity.getContraption(); if (contraption != null) { - ContraptionRenderDispatcher.render(entity, contraption, matrices, buffers); + ContraptionRenderDispatcher.render(entity, contraption, buffers); } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java index 199d07e48..6b383e933 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionMatrices.java @@ -10,41 +10,52 @@ import net.minecraft.util.math.vector.Matrix3f; import net.minecraft.util.math.vector.Matrix4f; public class ContraptionMatrices { + + public static final ContraptionMatrices IDENTITY = new ContraptionMatrices(); + public final MatrixStack entityStack; public final MatrixStack contraptionStack; + public final MatrixStack finalStack; public final Matrix4f entityMatrix; + public final Matrix4f lightMatrix; + + private ContraptionMatrices() { + this.entityStack = new MatrixStack(); + this.contraptionStack = new MatrixStack(); + this.finalStack = new MatrixStack(); + this.entityMatrix = new Matrix4f(); + this.lightMatrix = new Matrix4f(); + } public ContraptionMatrices(MatrixStack entityStack, AbstractContraptionEntity entity) { - this.entityStack = entityStack; + this.entityStack = copyStack(entityStack); this.contraptionStack = new MatrixStack(); float partialTicks = AnimationTickHolder.getPartialTicks(); entity.doLocalTransforms(partialTicks, new MatrixStack[] { this.contraptionStack }); + entityMatrix = translateTo(entity, partialTicks); + + lightMatrix = entityMatrix.copy(); + lightMatrix.multiply(contraptionStack.last().pose()); + + finalStack = copyStack(entityStack); + transform(finalStack, contraptionStack); } public MatrixStack getFinalStack() { - MatrixStack finalStack = new MatrixStack(); - transform(finalStack, entityStack); - transform(finalStack, contraptionStack); return finalStack; } public Matrix4f getFinalModel() { - Matrix4f finalModel = entityStack.last().pose().copy(); - finalModel.multiply(contraptionStack.last().pose()); - return finalModel; + return finalStack.last().pose(); } public Matrix3f getFinalNormal() { - Matrix3f finalNormal = entityStack.last().normal().copy(); - finalNormal.mul(contraptionStack.last().normal()); - return finalNormal; + return finalStack.last().normal(); } public Matrix4f getFinalLight() { - Matrix4f lightTransform = entityMatrix.copy(); - lightTransform.multiply(contraptionStack.last().pose()); - return lightTransform; + return lightMatrix; } public static Matrix4f translateTo(Entity entity, float partialTicks) { @@ -62,4 +73,12 @@ public class ContraptionMatrices { .mul(transform.last() .normal()); } + + public static MatrixStack copyStack(MatrixStack ms) { + MatrixStack cms = new MatrixStack(); + + transform(cms, ms); + + return cms; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java index d303281bc..f47cca99b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java @@ -1,47 +1,31 @@ package com.simibubi.create.content.contraptions.components.structureMovement.render; import static org.lwjgl.opengl.GL11.GL_QUADS; -import static org.lwjgl.opengl.GL11.glBindTexture; -import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D; -import java.lang.ref.Reference; -import java.util.Objects; import java.util.Random; import org.apache.commons.lang3.tuple.Pair; -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.GlTextureUnit; -import com.jozufozu.flywheel.backend.state.RenderLayer; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.GatherContextEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.jozufozu.flywheel.util.WorldAttached; import com.jozufozu.flywheel.util.transform.MatrixTransformStack; import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.AllMovementBehaviours; -import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; -import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler; -import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter; import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; -import com.simibubi.create.foundation.config.AllConfigs; -import com.simibubi.create.foundation.render.AllProgramSpecs; import com.simibubi.create.foundation.render.Compartment; -import com.simibubi.create.foundation.render.CreateContexts; import com.simibubi.create.foundation.render.SuperByteBuffer; import com.simibubi.create.foundation.render.TileEntityRenderHelper; -import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.BlockModelRenderer; import net.minecraft.client.renderer.BlockModelShapes; import net.minecraft.client.renderer.BufferBuilder; @@ -53,7 +37,6 @@ import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; import net.minecraft.world.LightType; import net.minecraft.world.World; import net.minecraft.world.gen.feature.template.Template; @@ -70,123 +53,25 @@ import net.minecraftforge.fml.common.Mod; public class ContraptionRenderDispatcher { private static final Lazy MODEL_RENDERER = Lazy.of(() -> new BlockModelRenderer(Minecraft.getInstance().getBlockColors())); private static final Lazy BLOCK_MODELS = Lazy.of(() -> Minecraft.getInstance().getModelManager().getBlockModelShaper()); - private static int worldHolderRefreshCounter; - public static final Int2ObjectMap RENDERERS = new Int2ObjectOpenHashMap<>(); - public static final Int2ObjectMap WORLD_HOLDERS = new Int2ObjectOpenHashMap<>(); + private static final WorldAttached WORLDS = new WorldAttached<>(WorldContraptions::new); + public static final Compartment> CONTRAPTION = new Compartment<>(); - public static void tick() { + public static void tick(World world) { if (Minecraft.getInstance().isPaused()) return; - for (RenderedContraption contraption : RENDERERS.values()) { - ContraptionLighter lighter = contraption.getLighter(); - if (lighter.getBounds().volume() < AllConfigs.CLIENT.maxContraptionLightVolume.get()) - lighter.tick(contraption); - - contraption.kinetics.tick(); - } - - worldHolderRefreshCounter++; - if (worldHolderRefreshCounter >= 20) { - removeDeadHolders(); - worldHolderRefreshCounter = 0; - } + WORLDS.get(world).tick(); } @SubscribeEvent public static void beginFrame(BeginFrameEvent event) { - ActiveRenderInfo info = event.getInfo(); - double camX = info.getPosition().x; - double camY = info.getPosition().y; - double camZ = info.getPosition().z; - for (RenderedContraption renderer : RENDERERS.values()) { - renderer.beginFrame(info, camX, camY, camZ); - } + WORLDS.get(event.getWorld()).beginFrame(event); } @SubscribeEvent public static void renderLayer(RenderLayerEvent event) { - if (Backend.getInstance().available()) { - renderLayerFlywheel(event); - } else { - renderLayerSBB(event); - } - } - - private static void renderLayerFlywheel(RenderLayerEvent event) { - removeDeadContraptions(); - - if (RENDERERS.isEmpty()) return; - - RenderType layer = event.getType(); - - layer.setupRenderState(); - GlTextureUnit.T4.makeActive(); // the shaders expect light volumes to be in texture 4 - - ContraptionProgram structureShader = CreateContexts.STRUCTURE.getProgram(AllProgramSpecs.STRUCTURE); - - structureShader.bind(); - structureShader.uploadViewProjection(event.viewProjection); - structureShader.uploadCameraPos(event.camX, event.camY, event.camZ); - - for (RenderedContraption renderer : RENDERERS.values()) { - renderer.doRenderLayer(layer, structureShader); - } - - if (Backend.getInstance().canUseInstancing()) { - RenderLayer renderLayer = event.getLayer(); - if (renderLayer != null) { - for (RenderedContraption renderer : RENDERERS.values()) { - renderer.materialManager.render(renderLayer, event.viewProjection, event.camX, event.camY, event.camZ); - } - } - } - - // clear the light volume state - GlTextureUnit.T4.makeActive(); - glBindTexture(GL_TEXTURE_3D, 0); - - layer.clearRenderState(); - GlTextureUnit.T0.makeActive(); - } - - private static void renderLayerSBB(RenderLayerEvent event) { - ContraptionHandler.loadedContraptions.get(event.getWorld()) - .values() - .stream() - .map(Reference::get) - .filter(Objects::nonNull) - .forEach(entity -> renderContraptionLayerSBB(event, entity)); - } - - private static void renderContraptionLayerSBB(RenderLayerEvent event, AbstractContraptionEntity entity) { - RenderType layer = event.getType(); - - Contraption contraption = entity.getContraption(); - Pair key = Pair.of(contraption, layer); - - SuperByteBuffer contraptionBuffer = CreateClient.BUFFER_CACHE.get(CONTRAPTION, key, () -> buildStructureBuffer(getWorldHolder(entity.level, contraption).renderWorld, contraption, layer)); - - if (!contraptionBuffer.isEmpty()) { - MatrixStack stack = event.stack; - - stack.pushPose(); - - double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - event.camX; - double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - event.camY; - double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - event.camZ; - - stack.translate(x, y, z); - ContraptionMatrices matrices = new ContraptionMatrices(stack, entity); - contraptionBuffer.transform(matrices.contraptionStack) - .light(matrices.entityMatrix) - .hybridLight() - .renderInto(matrices.entityStack, event.buffers.bufferSource().getBuffer(layer)); - - stack.popPose(); - } - + WORLDS.get(event.getWorld()).renderLayer(event); } @SubscribeEvent @@ -198,44 +83,13 @@ public class ContraptionRenderDispatcher { invalidateAll(); } - public static void render(AbstractContraptionEntity entity, Contraption contraption, - ContraptionMatrices matrices, IRenderTypeBuffer buffers) { + public static void render(AbstractContraptionEntity entity, Contraption contraption, IRenderTypeBuffer buffers) { World world = entity.level; - if (Backend.getInstance().available()) { - getRenderer(world, contraption); // hack to create the RenderedContraption when using Flywheel - } + ContraptionRenderInfo renderInfo = WORLDS.get(world) + .getRenderInfo(contraption); - PlacementSimulationWorld renderWorld = getWorldHolder(world, contraption).renderWorld; - - ContraptionRenderDispatcher.renderDynamic(world, renderWorld, contraption, matrices, buffers); - } - - private static RenderedContraption getRenderer(World world, Contraption c) { - int entityId = c.entity.getId(); - RenderedContraption contraption = RENDERERS.get(entityId); - - if (contraption == null) { - PlacementSimulationWorld renderWorld = setupRenderWorld(world, c); - contraption = new RenderedContraption(c, renderWorld); - RENDERERS.put(entityId, contraption); - WORLD_HOLDERS.put(entityId, contraption); - } - - return contraption; - } - - private static ContraptionWorldHolder getWorldHolder(World world, Contraption c) { - int entityId = c.entity.getId(); - ContraptionWorldHolder holder = WORLD_HOLDERS.get(entityId); - - if (holder == null) { - PlacementSimulationWorld renderWorld = setupRenderWorld(world, c); - holder = new ContraptionWorldHolder(c, renderWorld); - WORLD_HOLDERS.put(entityId, holder); - } - - return holder; + renderDynamic(world, renderInfo.renderWorld, contraption, renderInfo.getMatrices(), buffers); } public static PlacementSimulationWorld setupRenderWorld(World world, Contraption c) { @@ -291,7 +145,7 @@ public class ContraptionRenderDispatcher { } } - private static SuperByteBuffer buildStructureBuffer(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) { + public static SuperByteBuffer buildStructureBuffer(PlacementSimulationWorld renderWorld, Contraption c, RenderType layer) { BufferBuilder builder = buildStructure(renderWorld, c, layer); return new SuperByteBuffer(builder); } @@ -349,26 +203,6 @@ public class ContraptionRenderDispatcher { } public static void invalidateAll() { - for (RenderedContraption renderer : RENDERERS.values()) { - renderer.invalidate(); - } - - RENDERERS.clear(); - WORLD_HOLDERS.clear(); + WORLDS.empty(WorldContraptions::invalidate); } - - public static void removeDeadContraptions() { - RENDERERS.values().removeIf(renderer -> { - if (renderer.isDead()) { - renderer.invalidate(); - return true; - } - return false; - }); - } - - public static void removeDeadHolders() { - WORLD_HOLDERS.values().removeIf(ContraptionWorldHolder::isDead); - } - } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java new file mode 100644 index 000000000..d69e9c47f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java @@ -0,0 +1,57 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.render; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import com.simibubi.create.foundation.utility.AnimationTickHolder; +import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld; + +import net.minecraft.client.renderer.culling.ClippingHelper; +import net.minecraft.util.math.MathHelper; + +public class ContraptionRenderInfo { + public final Contraption contraption; + public final PlacementSimulationWorld renderWorld; + + private ContraptionMatrices matrices = ContraptionMatrices.IDENTITY; + private boolean visible; + + public ContraptionRenderInfo(Contraption contraption, PlacementSimulationWorld renderWorld) { + this.contraption = contraption; + this.renderWorld = renderWorld; + } + + public int getEntityId() { + return contraption.entity.getId(); + } + + public boolean isDead() { + return !contraption.entity.isAlive(); + } + + public void beginFrame(ClippingHelper clippingHelper, MatrixStack mainStack, double camX, double camY, double camZ) { + AbstractContraptionEntity entity = contraption.entity; + + visible = clippingHelper.isVisible(entity.getBoundingBoxForCulling().inflate(2)); + + mainStack.pushPose(); + + double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - camX; + double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - camY; + double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - camZ; + + mainStack.translate(x, y, z); + + matrices = new ContraptionMatrices(mainStack, entity); + + mainStack.popPose(); + } + + public boolean isVisible() { + return visible && contraption.entity.isAlive(); + } + + public ContraptionMatrices getMatrices() { + return matrices; + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionWorldHolder.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionWorldHolder.java deleted file mode 100644 index 0f3cc5ab7..000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionWorldHolder.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.simibubi.create.content.contraptions.components.structureMovement.render; - -import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; -import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld; - -public class ContraptionWorldHolder { - public final Contraption contraption; - public final PlacementSimulationWorld renderWorld; - - public ContraptionWorldHolder(Contraption contraption, PlacementSimulationWorld renderWorld) { - this.contraption = contraption; - this.renderWorld = renderWorld; - } - - public int getEntityId() { - return contraption.entity.getId(); - } - - public boolean isDead() { - return !contraption.entity.isAlive(); - } -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/LightVolumeDebugger.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/LightVolumeDebugger.java index 8645e4077..82f6e64e6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/LightVolumeDebugger.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/LightVolumeDebugger.java @@ -11,25 +11,25 @@ import com.simibubi.create.foundation.utility.outliner.AABBOutline; public class LightVolumeDebugger { public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) { - ContraptionRenderDispatcher.RENDERERS.values() - .stream() - .flatMap(r -> { - GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume(); - GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume(); - - ArrayList> pairs = new ArrayList<>(2); - - pairs.add(Pair.of(texture, 0xFFFFFF)); - pairs.add(Pair.of(sample, 0xFFFF00)); - - return pairs.stream(); - }) - .map(pair -> { - AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst())); - - outline.getParams().colored(pair.getSecond()); - return outline; - }) - .forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks())); +// ContraptionRenderDispatcher.RENDERERS.values() +// .stream() +// .flatMap(r -> { +// GridAlignedBB texture = r.getLighter().lightVolume.getTextureVolume(); +// GridAlignedBB sample = r.getLighter().lightVolume.getSampleVolume(); +// +// ArrayList> pairs = new ArrayList<>(2); +// +// pairs.add(Pair.of(texture, 0xFFFFFF)); +// pairs.add(Pair.of(sample, 0xFFFF00)); +// +// return pairs.stream(); +// }) +// .map(pair -> { +// AABBOutline outline = new AABBOutline(GridAlignedBB.toAABB(pair.getFirst())); +// +// outline.getParams().colored(pair.getSecond()); +// return outline; +// }) +// .forEach(outline -> outline.render(ms, buffer, AnimationTickHolder.getPartialTicks())); } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java index fdaa9098b..778102582 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java @@ -12,10 +12,7 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.backend.instancing.IInstanceRendered; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; -import com.jozufozu.flywheel.backend.material.MaterialGroup; import com.jozufozu.flywheel.backend.material.MaterialManager; import com.jozufozu.flywheel.backend.model.ArrayModelRenderer; import com.jozufozu.flywheel.backend.model.BufferedModel; @@ -41,7 +38,7 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.world.World; -public class RenderedContraption extends ContraptionWorldHolder { +public class RenderedContraption extends ContraptionRenderInfo { public static final VertexFormat FORMAT = VertexFormat.builder() .addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/WorldContraptions.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/WorldContraptions.java new file mode 100644 index 000000000..e0626744c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/WorldContraptions.java @@ -0,0 +1,234 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.render; + +import static com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher.CONTRAPTION; +import static com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher.buildStructureBuffer; +import static org.lwjgl.opengl.GL11.glBindTexture; +import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D; + +import java.lang.ref.Reference; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +import org.apache.commons.lang3.tuple.Pair; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.GlTextureUnit; +import com.jozufozu.flywheel.backend.state.RenderLayer; +import com.jozufozu.flywheel.event.BeginFrameEvent; +import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.simibubi.create.CreateClient; +import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.render.AllProgramSpecs; +import com.simibubi.create.foundation.render.CreateContexts; +import com.simibubi.create.foundation.render.SuperByteBuffer; +import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.renderer.ActiveRenderInfo; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.world.IWorld; +import net.minecraft.world.World; + +public class WorldContraptions { + private final World world; + + private int worldHolderRefreshCounter; + + public final Int2ObjectMap flwRenderers = new Int2ObjectOpenHashMap<>(); + public final Int2ObjectMap renderInfos = new Int2ObjectOpenHashMap<>(); + private final List visible = new ObjectArrayList<>(); + private final List flwVisible = new ObjectArrayList<>(); + + public WorldContraptions(IWorld world) { + this.world = (World) world; + } + + public void tick() { + for (RenderedContraption contraption : flwRenderers.values()) { + ContraptionLighter lighter = contraption.getLighter(); + if (lighter.getBounds().volume() < AllConfigs.CLIENT.maxContraptionLightVolume.get()) + lighter.tick(contraption); + + contraption.kinetics.tick(); + } + + worldHolderRefreshCounter++; + if (worldHolderRefreshCounter >= 20) { + removeDeadHolders(); + removeDeadContraptions(); + worldHolderRefreshCounter = 0; + } + + Consumer setup; + if (Backend.getInstance().available()) { + setup = this::createRenderer; + } else { + setup = this::getRenderInfo; + } + + ContraptionHandler.loadedContraptions.get(world) + .values() + .stream() + .map(Reference::get) + .filter(Objects::nonNull) + .map(AbstractContraptionEntity::getContraption) + .forEach(setup); + } + + public void beginFrame(BeginFrameEvent event) { + ActiveRenderInfo info = event.getInfo(); + double camX = info.getPosition().x; + double camY = info.getPosition().y; + double camZ = info.getPosition().z; + + visible.clear(); + flwVisible.clear(); + + renderInfos.int2ObjectEntrySet() + .stream() + .map(Map.Entry::getValue) + .forEach(renderInfo -> { + renderInfo.beginFrame(event.getClippingHelper(), event.getStack(), camX, camY, camZ); + }); + + if (Backend.getInstance() + .available()) { + flwRenderers.int2ObjectEntrySet() + .stream() + .map(Map.Entry::getValue) + .forEach(flwVisible::add); + + for (RenderedContraption renderer : flwVisible) { + renderer.beginFrame(info, camX, camY, camZ); + } + } else { + renderInfos.int2ObjectEntrySet() + .stream() + .map(Map.Entry::getValue) + .forEach(visible::add); + } + } + + public void renderLayer(RenderLayerEvent event) { + if (Backend.getInstance().available()) { + renderLayerFlywheel(event); + } else { + renderLayerSBB(event); + } + } + + private void renderLayerFlywheel(RenderLayerEvent event) { + if (flwVisible.isEmpty()) return; + + RenderType layer = event.getType(); + + layer.setupRenderState(); + GlTextureUnit.T4.makeActive(); // the shaders expect light volumes to be in texture 4 + + ContraptionProgram structureShader = CreateContexts.STRUCTURE.getProgram(AllProgramSpecs.STRUCTURE); + + structureShader.bind(); + structureShader.uploadViewProjection(event.viewProjection); + structureShader.uploadCameraPos(event.camX, event.camY, event.camZ); + + for (RenderedContraption renderedContraption : flwVisible) { + renderedContraption.doRenderLayer(layer, structureShader); + } + + if (Backend.getInstance().canUseInstancing()) { + RenderLayer renderLayer = event.getLayer(); + if (renderLayer != null) { + for (RenderedContraption renderer : flwVisible) { + renderer.materialManager.render(renderLayer, event.viewProjection, event.camX, event.camY, event.camZ); + } + } + } + + // clear the light volume state + GlTextureUnit.T4.makeActive(); + glBindTexture(GL_TEXTURE_3D, 0); + + layer.clearRenderState(); + GlTextureUnit.T0.makeActive(); + } + + private void renderLayerSBB(RenderLayerEvent event) { + visible.forEach(info -> renderContraptionLayerSBB(event, info)); + } + + private void renderContraptionLayerSBB(RenderLayerEvent event, ContraptionRenderInfo renderInfo) { + RenderType layer = event.getType(); + + if (!renderInfo.isVisible()) return; + + SuperByteBuffer contraptionBuffer = CreateClient.BUFFER_CACHE.get(CONTRAPTION, Pair.of(renderInfo.contraption, layer), + () -> buildStructureBuffer(renderInfo.renderWorld, renderInfo.contraption, layer)); + + if (!contraptionBuffer.isEmpty()) { + + ContraptionMatrices matrices = renderInfo.getMatrices(); + contraptionBuffer.transform(matrices.contraptionStack) + .light(matrices.entityMatrix) + .hybridLight() + .renderInto(matrices.entityStack, event.buffers.bufferSource().getBuffer(layer)); + } + + } + + private void createRenderer(Contraption c) { + int entityId = c.entity.getId(); + RenderedContraption contraption = flwRenderers.get(entityId); + + if (contraption == null) { + PlacementSimulationWorld renderWorld = ContraptionRenderDispatcher.setupRenderWorld(world, c); + contraption = new RenderedContraption(c, renderWorld); + flwRenderers.put(entityId, contraption); + renderInfos.put(entityId, contraption); + } + + } + + public ContraptionRenderInfo getRenderInfo(Contraption c) { + int entityId = c.entity.getId(); + ContraptionRenderInfo renderInfo = renderInfos.get(entityId); + + if (renderInfo == null) { + PlacementSimulationWorld renderWorld = ContraptionRenderDispatcher.setupRenderWorld(world, c); + renderInfo = new ContraptionRenderInfo(c, renderWorld); + renderInfos.put(entityId, renderInfo); + } + + return renderInfo; + } + + public void invalidate() { + for (RenderedContraption renderer : flwRenderers.values()) { + renderer.invalidate(); + } + + flwRenderers.clear(); + renderInfos.clear(); + } + + public void removeDeadContraptions() { + flwRenderers.values().removeIf(renderer -> { + if (renderer.isDead()) { + renderer.invalidate(); + return true; + } + return false; + }); + } + + public void removeDeadHolders() { + renderInfos.values().removeIf(ContraptionRenderInfo::isDead); + } +} diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 7021f2dfd..8e666c35b 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -141,7 +141,7 @@ public class ClientEvents { PlacementHelpers.tick(); CreateClient.OUTLINER.tickOutlines(); CreateClient.GHOST_BLOCKS.tickGhosts(); - ContraptionRenderDispatcher.tick(); + ContraptionRenderDispatcher.tick(world); BlueprintOverlayRenderer.tick(); } diff --git a/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java b/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java index d8dc4e987..04c4645e6 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java +++ b/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java @@ -6,17 +6,19 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.minecraft.world.IWorld; +import net.minecraftforge.common.util.NonNullFunction; public class WorldAttached { static List> allMaps = new ArrayList<>(); Map attached; - private Function factory; + private final NonNullFunction factory; - public WorldAttached(Function factory) { + public WorldAttached(NonNullFunction factory) { this.factory = factory; attached = new HashMap<>(); allMaps.add(attached); @@ -26,7 +28,7 @@ public class WorldAttached { allMaps.forEach(m -> m.remove(world)); } - @Nullable + @Nonnull public T get(IWorld world) { T t = attached.get(world); if (t != null)