DRY contraption rendering

- Different subclasses for flywheel rendering and sbb rendering
 - Select which to use on renderer reload, gather context, and when create buffers are invalidated
This commit is contained in:
Jozufozu 2021-07-28 19:25:25 -07:00
parent cd9f18a8c9
commit 7c202ed491
9 changed files with 295 additions and 275 deletions

View file

@ -86,7 +86,7 @@ public class CreateClient {
modEventBus.addListener(ClientEvents::loadCompleted);
modEventBus.addListener(CreateContexts::flwInit);
modEventBus.addListener(AllMaterialSpecs::flwInit);
modEventBus.addListener(ContraptionRenderDispatcher::invalidateOnGatherContext);
modEventBus.addListener(ContraptionRenderDispatcher::gatherContext);
ZAPPER_RENDER_HANDLER.register(forgeEventBus);
POTATO_CANNON_RENDER_HANDLER.register(forgeEventBus);
@ -207,7 +207,7 @@ public class CreateClient {
public static void invalidateRenderers() {
BUFFER_CACHE.invalidate();
ContraptionRenderDispatcher.invalidateAll();
ContraptionRenderDispatcher.reset();
}
public static void checkGraphicsFanciness() {

View file

@ -11,7 +11,10 @@ import net.minecraft.util.math.vector.Matrix4f;
public class ContraptionMatrices {
public static final ContraptionMatrices IDENTITY = new ContraptionMatrices();
/**
* The results from using this are undefined.
*/
public static final ContraptionMatrices EMPTY = new ContraptionMatrices();
public final MatrixStack entityStack;
public final MatrixStack contraptionStack;
@ -20,9 +23,7 @@ public class ContraptionMatrices {
public final Matrix4f lightMatrix;
private ContraptionMatrices() {
this.entityStack = new MatrixStack();
this.contraptionStack = new MatrixStack();
this.finalStack = new MatrixStack();
this.entityStack = this.contraptionStack = this.finalStack = new MatrixStack();
this.entityMatrix = new Matrix4f();
this.lightMatrix = new Matrix4f();
}
@ -81,4 +82,9 @@ public class ContraptionMatrices {
return cms;
}
public Matrix4f contraptionPose() {
return contraptionStack.last()
.pose();
}
}

View file

@ -6,6 +6,7 @@ import java.util.Random;
import org.apache.commons.lang3.tuple.Pair;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.GatherContextEvent;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -54,7 +55,7 @@ public class ContraptionRenderDispatcher {
private static final Lazy<BlockModelRenderer> MODEL_RENDERER = Lazy.of(() -> new BlockModelRenderer(Minecraft.getInstance().getBlockColors()));
private static final Lazy<BlockModelShapes> BLOCK_MODELS = Lazy.of(() -> Minecraft.getInstance().getModelManager().getBlockModelShaper());
private static final WorldAttached<WorldContraptions> WORLDS = new WorldAttached<>(WorldContraptions::new);
private static WorldAttached<ContraptionRenderManager<?>> WORLDS = new WorldAttached<>(SBBContraptionManager::new);
public static final Compartment<Pair<Contraption, RenderType>> CONTRAPTION = new Compartment<>();
@ -76,11 +77,11 @@ public class ContraptionRenderDispatcher {
@SubscribeEvent
public static void onRendererReload(ReloadRenderersEvent event) {
invalidateAll();
reset();
}
public static void invalidateOnGatherContext(GatherContextEvent e) {
invalidateAll();
public static void gatherContext(GatherContextEvent e) {
reset();
}
public static void render(AbstractContraptionEntity entity, Contraption contraption, IRenderTypeBuffer buffers) {
@ -202,7 +203,13 @@ public class ContraptionRenderDispatcher {
return WorldRenderer.getLightColor(renderWorld, context.localPos);
}
public static void invalidateAll() {
WORLDS.empty(WorldContraptions::invalidate);
public static void reset() {
WORLDS.empty(ContraptionRenderManager::delete);
if (Backend.getInstance().available()) {
WORLDS = new WorldAttached<>(FlwContraptionManager::new);
} else {
WORLDS = new WorldAttached<>(SBBContraptionManager::new);
}
}
}

View file

@ -1,19 +1,19 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.jozufozu.flywheel.event.BeginFrameEvent;
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;
import net.minecraft.util.math.vector.Vector3d;
public class ContraptionRenderInfo {
public final Contraption contraption;
public final PlacementSimulationWorld renderWorld;
private ContraptionMatrices matrices = ContraptionMatrices.IDENTITY;
private ContraptionMatrices matrices = ContraptionMatrices.EMPTY;
private boolean visible;
public ContraptionRenderInfo(Contraption contraption, PlacementSimulationWorld renderWorld) {
@ -29,22 +29,24 @@ public class ContraptionRenderInfo {
return !contraption.entity.isAlive();
}
public void beginFrame(ClippingHelper clippingHelper, MatrixStack mainStack, double camX, double camY, double camZ) {
public void beginFrame(BeginFrameEvent event) {
AbstractContraptionEntity entity = contraption.entity;
visible = clippingHelper.isVisible(entity.getBoundingBoxForCulling().inflate(2));
visible = event.getClippingHelper().isVisible(entity.getBoundingBoxForCulling().inflate(2));
mainStack.pushPose();
event.getStack().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;
Vector3d cameraPos = event.getInfo()
.getPosition();
double x = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.xOld, entity.getX()) - cameraPos.x;
double y = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.yOld, entity.getY()) - cameraPos.y;
double z = MathHelper.lerp(AnimationTickHolder.getPartialTicks(), entity.zOld, entity.getZ()) - cameraPos.z;
mainStack.translate(x, y, z);
event.getStack().translate(x, y, z);
matrices = new ContraptionMatrices(mainStack, entity);
matrices = new ContraptionMatrices(event.getStack(), entity);
mainStack.popPose();
event.getStack().popPose();
}
public boolean isVisible() {

View file

@ -0,0 +1,87 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import java.lang.ref.Reference;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
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 it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo> {
protected final World world;
private int removalTimer;
protected final Int2ObjectMap<C> renderInfos = new Int2ObjectOpenHashMap<>();
protected final List<C> visible = new ObjectArrayList<>();
public ContraptionRenderManager(IWorld world) {
this.world = (World) world;
}
public abstract void renderLayer(RenderLayerEvent event);
protected abstract C create(Contraption c);
public void tick() {
removalTimer++;
if (removalTimer >= 20) {
removeDeadRenderers();
removalTimer = 0;
}
ContraptionHandler.loadedContraptions.get(world)
.values()
.stream()
.map(Reference::get)
.filter(Objects::nonNull)
.map(AbstractContraptionEntity::getContraption)
.forEach(this::getRenderInfo);
}
public void beginFrame(BeginFrameEvent event) {
visible.clear();
renderInfos.int2ObjectEntrySet()
.stream()
.map(Map.Entry::getValue)
.forEach(renderInfo -> renderInfo.beginFrame(event));
renderInfos.int2ObjectEntrySet()
.stream()
.map(Map.Entry::getValue)
.filter(ContraptionRenderInfo::isVisible)
.forEach(visible::add);
}
public C getRenderInfo(Contraption c) {
int entityId = c.entity.getId();
C renderInfo = renderInfos.get(entityId);
if (renderInfo == null) {
renderInfo = create(c);
renderInfos.put(entityId, renderInfo);
}
return renderInfo;
}
public void delete() {
renderInfos.clear();
}
public void removeDeadRenderers() {
renderInfos.values().removeIf(ContraptionRenderInfo::isDead);
}
}

View file

@ -0,0 +1,99 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D;
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.RenderLayerEvent;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
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.utility.worldWrappers.PlacementSimulationWorld;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.IWorld;
public class FlwContraptionManager extends ContraptionRenderManager<RenderedContraption> {
public FlwContraptionManager(IWorld world) {
super(world);
}
@Override
public void tick() {
super.tick();
for (RenderedContraption contraption : visible) {
ContraptionLighter<?> lighter = contraption.getLighter();
if (lighter.getBounds().volume() < AllConfigs.CLIENT.maxContraptionLightVolume.get())
lighter.tick(contraption);
contraption.kinetics.tick();
}
}
@Override
public void renderLayer(RenderLayerEvent event) {
if (visible.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 : visible) {
renderedContraption.doRenderLayer(layer, structureShader);
}
if (Backend.getInstance().canUseInstancing()) {
RenderLayer renderLayer = event.getLayer();
if (renderLayer != null) {
for (RenderedContraption renderer : visible) {
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();
}
@Override
protected RenderedContraption create(Contraption c) {
PlacementSimulationWorld renderWorld = ContraptionRenderDispatcher.setupRenderWorld(world, c);
return new RenderedContraption(c, renderWorld);
}
@Override
public void removeDeadRenderers() {
renderInfos.values().removeIf(renderer -> {
if (renderer.isDead()) {
renderer.invalidate();
return true;
}
return false;
});
}
@Override
public void delete() {
for (RenderedContraption renderer : renderInfos.values()) {
renderer.invalidate();
}
renderInfos.clear();
}
}

View file

@ -18,9 +18,9 @@ import com.jozufozu.flywheel.backend.model.ArrayModelRenderer;
import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.backend.model.IndexedModel;
import com.jozufozu.flywheel.backend.model.ModelRenderer;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.util.BufferBuilderReader;
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.content.contraptions.components.structureMovement.ContraptionLighter;
@ -28,7 +28,6 @@ import com.simibubi.create.foundation.render.CreateContexts;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.tileentity.TileEntity;
@ -36,6 +35,7 @@ import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
public class RenderedContraption extends ContraptionRenderInfo {
@ -85,26 +85,28 @@ public class RenderedContraption extends ContraptionRenderInfo {
}
}
public void beginFrame(ActiveRenderInfo info, double camX, double camY, double camZ) {
kinetics.beginFrame(info);
public void beginFrame(BeginFrameEvent event) {
super.beginFrame(event);
if (!isVisible()) return;
kinetics.beginFrame(event.getInfo());
AbstractContraptionEntity entity = contraption.entity;
float pt = AnimationTickHolder.getPartialTicks();
MatrixStack stack = new MatrixStack();
double x = MathHelper.lerp(pt, entity.xOld, entity.getX()) - camX;
double y = MathHelper.lerp(pt, entity.yOld, entity.getY()) - camY;
double z = MathHelper.lerp(pt, entity.zOld, entity.getZ()) - camZ;
stack.translate(x, y, z);
entity.doLocalTransforms(pt, new MatrixStack[] { stack });
model = stack.last().pose();
AxisAlignedBB lightBox = GridAlignedBB.toAABB(lighter.lightVolume.getTextureVolume());
this.lightBox = lightBox.move(-camX, -camY, -camZ);
Vector3d cameraPos = event.getInfo()
.getPosition();
float x = (float) (MathHelper.lerp(pt, entity.xOld, entity.getX()) - cameraPos.x);
float y = (float) (MathHelper.lerp(pt, entity.yOld, entity.getY()) - cameraPos.y);
float z = (float) (MathHelper.lerp(pt, entity.zOld, entity.getZ()) - cameraPos.z);
model = Matrix4f.createTranslateMatrix(x, y, z);
model.multiply(getMatrices().contraptionPose());
this.lightBox = lightBox.move(-cameraPos.x, -cameraPos.y, -cameraPos.z);
}
void setup(ContraptionProgram shader) {

View file

@ -0,0 +1,51 @@
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 org.apache.commons.lang3.tuple.Pair;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.IWorld;
public class SBBContraptionManager extends ContraptionRenderManager<ContraptionRenderInfo> {
public SBBContraptionManager(IWorld world) {
super(world);
}
@Override
public void renderLayer(RenderLayerEvent event) {
visible.forEach(info -> renderContraptionLayerSBB(event, info));
}
@Override
protected ContraptionRenderInfo create(Contraption c) {
PlacementSimulationWorld renderWorld = ContraptionRenderDispatcher.setupRenderWorld(world, c);
return new ContraptionRenderInfo(c, renderWorld);
}
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));
}
}
}

View file

@ -1,234 +0,0 @@
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<RenderedContraption> flwRenderers = new Int2ObjectOpenHashMap<>();
public final Int2ObjectMap<ContraptionRenderInfo> renderInfos = new Int2ObjectOpenHashMap<>();
private final List<ContraptionRenderInfo> visible = new ObjectArrayList<>();
private final List<RenderedContraption> 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<Contraption> 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);
}
}