From beb55f065fdfab3dd2687eeec85da8fb9cc6f6cf Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Fri, 4 Aug 2023 16:20:27 -0400 Subject: [PATCH] Lazy VBO generation --- .../entity/AbstractOutputBlockEntity.java | 22 ++ .../entity/ElectronicJukeboxBlockEntity.java | 22 ++ .../client/render/BlockEntityClientState.java | 41 ++++ .../phonos/client/render/CableRenderer.java | 199 +++++++++--------- .../block/CableOutputBlockEntityRenderer.java | 55 ++++- .../phonos/world/sound/CableConnection.java | 15 ++ .../sound/block/BlockConnectionLayout.java | 27 +++ .../world/sound/block/OutputBlockEntity.java | 3 + 8 files changed, 281 insertions(+), 103 deletions(-) create mode 100644 src/main/java/io/github/foundationgames/phonos/client/render/BlockEntityClientState.java diff --git a/src/main/java/io/github/foundationgames/phonos/block/entity/AbstractOutputBlockEntity.java b/src/main/java/io/github/foundationgames/phonos/block/entity/AbstractOutputBlockEntity.java index fd5245b..bb7bfc9 100644 --- a/src/main/java/io/github/foundationgames/phonos/block/entity/AbstractOutputBlockEntity.java +++ b/src/main/java/io/github/foundationgames/phonos/block/entity/AbstractOutputBlockEntity.java @@ -1,5 +1,6 @@ package io.github.foundationgames.phonos.block.entity; +import io.github.foundationgames.phonos.client.render.BlockEntityClientState; import io.github.foundationgames.phonos.sound.emitter.SoundSource; import io.github.foundationgames.phonos.util.UniqueId; import io.github.foundationgames.phonos.world.sound.block.BlockConnectionLayout; @@ -22,12 +23,14 @@ public abstract class AbstractOutputBlockEntity extends BlockEntity implements S public final BlockEntityOutputs outputs; protected @Nullable NbtCompound pendingNbt = null; protected final long emitterId; + private BlockEntityClientState clientState; public AbstractOutputBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, BlockConnectionLayout outputLayout) { super(type, pos, state); this.emitterId = UniqueId.ofBlock(pos); this.outputs = new BlockEntityOutputs(outputLayout, this); + this.clientState = null; } @Override @@ -115,4 +118,23 @@ public void forEachChild(LongConsumer action) { public BlockEntityOutputs getOutputs() { return this.outputs; } + + @Override + public BlockEntityClientState getClientState() { + if (this.clientState == null) { + this.clientState = new BlockEntityClientState(); + } + + this.clientState.genState(this.outputs); + return this.clientState; + } + + @Override + public void markRemoved() { + if (this.hasWorld() && this.world.isClient && this.clientState != null) { + this.clientState.dirty = true; + this.clientState.close(); + } + super.markRemoved(); + } } diff --git a/src/main/java/io/github/foundationgames/phonos/block/entity/ElectronicJukeboxBlockEntity.java b/src/main/java/io/github/foundationgames/phonos/block/entity/ElectronicJukeboxBlockEntity.java index 2375232..d0f43c5 100644 --- a/src/main/java/io/github/foundationgames/phonos/block/entity/ElectronicJukeboxBlockEntity.java +++ b/src/main/java/io/github/foundationgames/phonos/block/entity/ElectronicJukeboxBlockEntity.java @@ -1,6 +1,7 @@ package io.github.foundationgames.phonos.block.entity; import io.github.foundationgames.phonos.block.PhonosBlocks; +import io.github.foundationgames.phonos.client.render.BlockEntityClientState; import io.github.foundationgames.phonos.network.PayloadPackets; import io.github.foundationgames.phonos.sound.SoundStorage; import io.github.foundationgames.phonos.sound.emitter.SoundEmitterTree; @@ -46,6 +47,7 @@ public class ElectronicJukeboxBlockEntity extends JukeboxBlockEntity implements private final BlockEntityType type; private @Nullable NbtCompound pendingNbt = null; private final long emitterId; + private BlockEntityClientState clientState; private @Nullable SoundEmitterTree playingSound = null; @@ -55,6 +57,7 @@ public ElectronicJukeboxBlockEntity(BlockEntityType type, BlockPos pos, Block this.emitterId = UniqueId.ofBlock(pos); this.outputs = new BlockEntityOutputs(OUTPUT_LAYOUT, this); + this.clientState = null; } public ElectronicJukeboxBlockEntity(BlockPos pos, BlockState state) { @@ -197,6 +200,25 @@ public BlockEntityOutputs getOutputs() { return this.outputs; } + @Override + public BlockEntityClientState getClientState() { + if (this.clientState == null) { + this.clientState = new BlockEntityClientState(); + } + + this.clientState.genState(this.outputs); + return this.clientState; + } + + @Override + public void markRemoved() { + if (this.hasWorld() && this.world.isClient && this.clientState != null) { + this.clientState.dirty = true; + this.clientState.close(); + } + super.markRemoved(); + } + @Override public long emitterId() { return emitterId; diff --git a/src/main/java/io/github/foundationgames/phonos/client/render/BlockEntityClientState.java b/src/main/java/io/github/foundationgames/phonos/client/render/BlockEntityClientState.java new file mode 100644 index 0000000..71cdaa4 --- /dev/null +++ b/src/main/java/io/github/foundationgames/phonos/client/render/BlockEntityClientState.java @@ -0,0 +1,41 @@ +package io.github.foundationgames.phonos.client.render; + +import io.github.foundationgames.phonos.world.sound.CableConnection; +import io.github.foundationgames.phonos.world.sound.block.BlockEntityOutputs; +import net.minecraft.client.gl.VertexBuffer; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class BlockEntityClientState { + public boolean dirty; + public @Nullable VertexBuffer buffer = null; + private List cachedCons = new ArrayList<>(); + + public void genState(BlockEntityOutputs outs) { + List connections = new ArrayList<>(); + outs.forEach((i, o) -> connections.add(o)); + + if (!cachedCons.equals(connections) || this.buffer == null) { + // Mark ourselves as needing re-rendering + dirty = true; + + // Close the existing buffer + if (buffer != null) { + buffer.close(); + } + + buffer = null; + cachedCons = connections; + } + } + + public void close() { + if (buffer != null) { + buffer.close(); + } + + buffer = null; + } +} diff --git a/src/main/java/io/github/foundationgames/phonos/client/render/CableRenderer.java b/src/main/java/io/github/foundationgames/phonos/client/render/CableRenderer.java index e69f580..0b4fc50 100644 --- a/src/main/java/io/github/foundationgames/phonos/client/render/CableRenderer.java +++ b/src/main/java/io/github/foundationgames/phonos/client/render/CableRenderer.java @@ -1,15 +1,16 @@ package io.github.foundationgames.phonos.client.render; +import com.mojang.blaze3d.systems.RenderSystem; import io.github.foundationgames.phonos.config.PhonosClientConfig; -import io.github.foundationgames.phonos.mixin.WorldRendererAccess; import io.github.foundationgames.phonos.util.PhonosUtil; import io.github.foundationgames.phonos.util.Pose3f; import io.github.foundationgames.phonos.world.sound.CableConnection; import io.github.foundationgames.phonos.world.sound.CablePlugPoint; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.model.Model; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.*; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; @@ -65,9 +66,11 @@ private static void lerpCableEnd(Vector4f[] out, Vector4f[] begin, Vector4f[] en } } - public static void renderConnection(PhonosClientConfig config, World world, CableConnection conn, MatrixStack matrices, VertexConsumer buffer, Model cableEndModel, int overlay, float tickDelta) { + public static void renderConnection(BlockEntityClientState clientState, BlockEntity e, PhonosClientConfig config, World world, CableConnection conn, MatrixStack matrices, VertexConsumer buffer, Model cableEndModel, int overlay, float tickDelta) { int startLight, endLight; + // Connection points are always rendered immediate + matrices.push(); transformConnPoint(world, conn.start, matrices, cableStPt, tickDelta); @@ -87,105 +90,101 @@ public static void renderConnection(PhonosClientConfig config, World world, Cabl cableEndModel.render(matrices, buffer, endLight, overlay, 1, 1, 1, 1); matrices.pop(); - if (config.cableCulling) { - var frustum = ((WorldRendererAccess) MinecraftClient.getInstance().worldRenderer).phonos$getFrustum(); - - if (!frustum.isVisible( - Math.min(cableStPt.x, cableEnPt.x), Math.min(cableStPt.y, cableEnPt.y), Math.min(cableStPt.z, cableEnPt.z), - Math.max(cableStPt.x, cableEnPt.x), Math.max(cableStPt.y, cableEnPt.y), Math.max(cableStPt.z, cableEnPt.z) - )) { - return; + // Rendering for the first time? Generate VBOs now. + if (clientState.dirty) { + BufferBuilder builder = Tessellator.getInstance().getBuffer(); + + matrices = new MatrixStack(); + matrices.push(); + + float vOffset = conn.color != null ? 0.125f : 0; + float r = conn.color != null ? conn.color.getColorComponents()[0] : 1; + float g = conn.color != null ? conn.color.getColorComponents()[1] : 1; + float b = conn.color != null ? conn.color.getColorComponents()[2] : 1; + float length = cableStPt.distance(cableEnPt); + + double detail = config.cableLODNearDetail; + int segments = Math.max((int) Math.ceil(4 * length * detail), 1); + + // TODO: baking does not support LODs +// if (config.cableLODs) { +// float cx = (cableStPt.x + cableEnPt.x) * 0.5f; +// float cy = (cableStPt.y + cableEnPt.y) * 0.5f; +// float cz = (cableStPt.z + cableEnPt.z) * 0.5f; +// +// double sqDist = MinecraftClient.getInstance().gameRenderer.getCamera().getPos() +// .squaredDistanceTo(cx, cy, cz); +// double delta = MathHelper.clamp(sqDist / (length * length * 4), 0, 1); +// detail = MathHelper.lerp(delta, config.cableLODNearDetail, config.cableLODFarDetail); +// +// segments = Math.max((int) Math.ceil(4 * length * detail), Math.min(3, segments)); +// } + + final float texUWid = (float) (0.25 / detail); + + cableRotAxis.set(cableEnPt.z - cableStPt.z, 0, cableEnPt.x - cableStPt.x); + + float cablePitch = (float) Math.atan2(cableEnPt.y - cableStPt.y, cableRotAxis.length()); + float cableYaw = (float) Math.atan2(cableRotAxis.z, cableRotAxis.x); + cableRotAxis.normalize(); + + loadCableEnd(cableStart); + loadCableEnd(cableEnd); + cableNormal[0].set(-PhonosUtil.SQRT2DIV2, -PhonosUtil.SQRT2DIV2, 0); + cableNormal[1].set(PhonosUtil.SQRT2DIV2, -PhonosUtil.SQRT2DIV2, 0); + cableNormal[2].set(PhonosUtil.SQRT2DIV2, PhonosUtil.SQRT2DIV2, 0); + cableNormal[3].set(-PhonosUtil.SQRT2DIV2, PhonosUtil.SQRT2DIV2, 0); + + rotationCache.setAngleAxis(cablePitch, 1, 0, 0); + transformCableEnd(cableStart, vec -> vec.rotate(rotationCache)); + transformCableEnd(cableEnd, vec -> vec.rotate(rotationCache)); + transformCableNormal(cableNormal, vec -> vec.rotate(rotationCache)); + + rotationCache.setAngleAxis(Math.PI + cableYaw, 0, 1, 0); + transformCableEnd(cableStart, vec -> vec.rotate(rotationCache)); + transformCableEnd(cableEnd, vec -> vec.rotate(rotationCache)); + transformCableNormal(cableNormal, vec -> vec.rotate(rotationCache)); + + transformCableEnd(cableStart, vec -> vec.set(vec.x + cableStPt.x, vec.y + cableStPt.y, vec.z + cableStPt.z, 1)); + transformCableEnd(cableEnd, vec -> vec.set(vec.x + cableEnPt.x, vec.y + cableEnPt.y, vec.z + cableEnPt.z, 1)); + + transformCableNormal(cableNormal, matrices.peek().getNormalMatrix()::transform); + + for (int s = 0; s < segments; s++) { + float startDelta = (float) s / segments; + float endDelta = (float) (s + 1) / segments; + float startYOffset = length * 0.15f * (0.25f - (float) Math.pow(startDelta - 0.5, 2)); + float endYOffset = length * 0.15f * (0.25f - (float) Math.pow(endDelta - 0.5, 2)); + int segStartLight = PhonosUtil.lerpLight(startDelta, startLight, endLight); + int segEndLight = PhonosUtil.lerpLight(endDelta, startLight, endLight); + + lerpCableEnd(currCableStart, cableStart, cableEnd, startDelta); + lerpCableEnd(currCableEnd, cableStart, cableEnd, endDelta); + + transformCableEnd(currCableStart, vec -> vec.add(0, -startYOffset, 0, 0)); + transformCableEnd(currCableEnd, vec -> vec.add(0, -endYOffset, 0, 0)); + + transformCableEnd(currCableStart, matrices.peek().getPositionMatrix()::transform); + transformCableEnd(currCableEnd, matrices.peek().getPositionMatrix()::transform); + + for (int i = 0; i < 4; i++) { + float vOffset2 = i % 2 == 0 ? vOffset + 0.0625f : vOffset; + int next = (i + 1) % 4; + var nml = cableNormal[i]; + + builder.vertex(currCableStart[i].x, currCableStart[i].y, currCableStart[i].z).color(r, g, b, 1) + .texture(texUWid, 0.3125f + vOffset2).overlay(overlay).light(segStartLight).normal(nml.x, nml.y, nml.z).next(); + builder.vertex(currCableEnd[i].x, currCableEnd[i].y, currCableEnd[i].z).color(r, g, b, 1) + .texture(0, 0.3125f + vOffset2).overlay(overlay).light(segEndLight).normal(nml.x, nml.y, nml.z).next(); + builder.vertex(currCableEnd[next].x, currCableEnd[next].y, currCableEnd[next].z).color(r, g, b, 1) + .texture(0, 0.375f + vOffset2).overlay(overlay).light(segEndLight).normal(nml.x, nml.y, nml.z).next(); + builder.vertex(currCableStart[next].x, currCableStart[next].y, currCableStart[next].z).color(r, g, b, 1) + .texture(texUWid, 0.375f + vOffset2).overlay(overlay).light(segStartLight).normal(nml.x, nml.y, nml.z).next(); + } } - } - - matrices.push(); - - float vOffset = conn.color != null ? 0.125f : 0; - float r = conn.color != null ? conn.color.getColorComponents()[0] : 1; - float g = conn.color != null ? conn.color.getColorComponents()[1] : 1; - float b = conn.color != null ? conn.color.getColorComponents()[2] : 1; - float length = cableStPt.distance(cableEnPt); - - double detail = config.cableLODNearDetail; - int segments = Math.max((int) Math.ceil(4 * length * detail), 1); - if (config.cableLODs) { - float cx = (cableStPt.x + cableEnPt.x) * 0.5f; - float cy = (cableStPt.y + cableEnPt.y) * 0.5f; - float cz = (cableStPt.z + cableEnPt.z) * 0.5f; - - double sqDist = MinecraftClient.getInstance().gameRenderer.getCamera().getPos() - .squaredDistanceTo(cx, cy, cz); - double delta = MathHelper.clamp(sqDist / (length * length * 4), 0, 1); - detail = MathHelper.lerp(delta, config.cableLODNearDetail, config.cableLODFarDetail); - - segments = Math.max((int) Math.ceil(4 * length * detail), Math.min(3, segments)); + matrices.pop(); } - - final float texUWid = (float) Math.ceil(length / segments) * 0.25f; - - cableRotAxis.set(cableEnPt.z - cableStPt.z, 0, cableEnPt.x - cableStPt.x); - - float cablePitch = (float) Math.atan2(cableEnPt.y - cableStPt.y, cableRotAxis.length()); - float cableYaw = (float) Math.atan2(cableRotAxis.z, cableRotAxis.x); - cableRotAxis.normalize(); - - loadCableEnd(cableStart); - loadCableEnd(cableEnd); - cableNormal[0].set(-PhonosUtil.SQRT2DIV2, -PhonosUtil.SQRT2DIV2, 0); - cableNormal[1].set(PhonosUtil.SQRT2DIV2, -PhonosUtil.SQRT2DIV2, 0); - cableNormal[2].set(PhonosUtil.SQRT2DIV2, PhonosUtil.SQRT2DIV2, 0); - cableNormal[3].set(-PhonosUtil.SQRT2DIV2, PhonosUtil.SQRT2DIV2, 0); - - rotationCache.setAngleAxis(cablePitch, 1, 0, 0); - transformCableEnd(cableStart, vec -> vec.rotate(rotationCache)); - transformCableEnd(cableEnd, vec -> vec.rotate(rotationCache)); - transformCableNormal(cableNormal, vec -> vec.rotate(rotationCache)); - - rotationCache.setAngleAxis(Math.PI + cableYaw, 0, 1, 0); - transformCableEnd(cableStart, vec -> vec.rotate(rotationCache)); - transformCableEnd(cableEnd, vec -> vec.rotate(rotationCache)); - transformCableNormal(cableNormal, vec -> vec.rotate(rotationCache)); - - transformCableEnd(cableStart, vec -> vec.set(vec.x + cableStPt.x, vec.y + cableStPt.y, vec.z + cableStPt.z, 1)); - transformCableEnd(cableEnd, vec -> vec.set(vec.x + cableEnPt.x, vec.y + cableEnPt.y, vec.z + cableEnPt.z, 1)); - - transformCableNormal(cableNormal, matrices.peek().getNormalMatrix()::transform); - - for (int s = 0; s < segments; s++) { - float startDelta = (float)s / segments; - float endDelta = (float)(s + 1) / segments; - float startYOffset = length * 0.15f * (0.25f - (float) Math.pow(startDelta - 0.5, 2)); - float endYOffset = length * 0.15f * (0.25f - (float) Math.pow(endDelta - 0.5, 2)); - int segStartLight = PhonosUtil.lerpLight(startDelta, startLight, endLight); - int segEndLight = PhonosUtil.lerpLight(endDelta, startLight, endLight); - - lerpCableEnd(currCableStart, cableStart, cableEnd, startDelta); - lerpCableEnd(currCableEnd, cableStart, cableEnd, endDelta); - - transformCableEnd(currCableStart, vec -> vec.add(0, -startYOffset, 0, 0)); - transformCableEnd(currCableEnd, vec -> vec.add(0, -endYOffset, 0, 0)); - - transformCableEnd(currCableStart, matrices.peek().getPositionMatrix()::transform); - transformCableEnd(currCableEnd, matrices.peek().getPositionMatrix()::transform); - - for (int i = 0; i < 4; i++) { - float vOffset2 = i % 2 == 0 ? vOffset + 0.0625f : vOffset; - int next = (i + 1) % 4; - var nml = cableNormal[i]; - - buffer.vertex(currCableStart[i].x, currCableStart[i].y, currCableStart[i].z).color(r, g, b, 1) - .texture(texUWid, 0.3125f + vOffset2).overlay(overlay).light(segStartLight).normal(nml.x, nml.y, nml.z).next(); - buffer.vertex(currCableEnd[i].x, currCableEnd[i].y, currCableEnd[i].z).color(r, g, b, 1) - .texture(0, 0.3125f + vOffset2).overlay(overlay).light(segEndLight).normal(nml.x, nml.y, nml.z).next(); - buffer.vertex(currCableEnd[next].x, currCableEnd[next].y, currCableEnd[next].z).color(r, g, b, 1) - .texture(0, 0.375f + vOffset2).overlay(overlay).light(segEndLight).normal(nml.x, nml.y, nml.z).next(); - buffer.vertex(currCableStart[next].x, currCableStart[next].y, currCableStart[next].z).color(r, g, b, 1) - .texture(texUWid, 0.375f + vOffset2).overlay(overlay).light(segStartLight).normal(nml.x, nml.y, nml.z).next(); - } - } - - matrices.pop(); } diff --git a/src/main/java/io/github/foundationgames/phonos/client/render/block/CableOutputBlockEntityRenderer.java b/src/main/java/io/github/foundationgames/phonos/client/render/block/CableOutputBlockEntityRenderer.java index ffa0238..ddbc6b4 100644 --- a/src/main/java/io/github/foundationgames/phonos/client/render/block/CableOutputBlockEntityRenderer.java +++ b/src/main/java/io/github/foundationgames/phonos/client/render/block/CableOutputBlockEntityRenderer.java @@ -1,17 +1,22 @@ package io.github.foundationgames.phonos.client.render.block; +import com.mojang.blaze3d.systems.RenderSystem; import io.github.foundationgames.phonos.Phonos; import io.github.foundationgames.phonos.PhonosClient; import io.github.foundationgames.phonos.client.model.BasicModel; +import io.github.foundationgames.phonos.client.render.BlockEntityClientState; import io.github.foundationgames.phonos.client.render.CableRenderer; import io.github.foundationgames.phonos.config.PhonosClientConfig; import io.github.foundationgames.phonos.world.sound.block.OutputBlockEntity; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.VertexBuffer; +import net.minecraft.client.render.*; import net.minecraft.client.render.block.entity.BlockEntityRenderer; import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3d; public class CableOutputBlockEntityRenderer implements BlockEntityRenderer { public static final Identifier TEXTURE = Phonos.id("textures/entity/audio_cable.png"); @@ -23,14 +28,58 @@ public CableOutputBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) { @Override public void render(E entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) { - var buffer = vertexConsumers.getBuffer(cableEndModel.getLayer(TEXTURE)); + RenderLayer layer = cableEndModel.getLayer(TEXTURE); + var buffer = vertexConsumers.getBuffer(layer); var config = PhonosClientConfig.get(); matrices.push(); matrices.translate(-entity.getPos().getX(), -entity.getPos().getY(), -entity.getPos().getZ()); + BlockEntityClientState clientState = entity.getClientState(); + + boolean rerender = clientState.buffer == null || clientState.dirty; + + if (rerender) { + // If we're re-rendering into the vertexbuffer, create a new VBO, grab the tessellator and start tessellating with our vertex format + VertexBuffer vbo = new VertexBuffer(VertexBuffer.Usage.STATIC); + BufferBuilder builder = Tessellator.getInstance().getBuffer(); + builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL); + clientState.buffer = vbo; + } + + // Render each connection point in immediate mode, and render cables into the given vertex buffer entity.getOutputs().forEach((i, conn) -> - CableRenderer.renderConnection(config, entity.getWorld(), conn, matrices, buffer, cableEndModel, overlay, tickDelta)); + CableRenderer.renderConnection(clientState, entity, config, entity.getWorld(), conn, matrices, buffer, cableEndModel, overlay, tickDelta)); + + VertexBuffer vbo = clientState.buffer; + + if (rerender) { + // If we rerendered, upload the buffer to the GPU and mark ourselves as not dirty + vbo.bind(); + vbo.upload(Tessellator.getInstance().getBuffer().end()); + VertexBuffer.unbind(); + clientState.dirty = false; + } + + // Setup the render state for this render phase (and texture) + layer.startDrawing(); + + // Grab fog and set to an extravagant value + // TODO: this is a total hack, but it's needed to convince the shader to not apply fog everywhere + float realEnd = RenderSystem.getShaderFogEnd(); + RenderSystem.setShaderFogEnd(9999999); + + matrices.push(); + // Render the buffer, which contains all the cables connected to this + vbo.bind(); + vbo.draw(matrices.peek().getPositionMatrix(), RenderSystem.getProjectionMatrix(), GameRenderer.getRenderTypeEntitySolidProgram()); + VertexBuffer.unbind(); + + matrices.pop(); + + // Reset the render state + RenderSystem.setShaderFogEnd(realEnd); + layer.endDrawing(); matrices.pop(); } diff --git a/src/main/java/io/github/foundationgames/phonos/world/sound/CableConnection.java b/src/main/java/io/github/foundationgames/phonos/world/sound/CableConnection.java index 081587b..f3b69b3 100644 --- a/src/main/java/io/github/foundationgames/phonos/world/sound/CableConnection.java +++ b/src/main/java/io/github/foundationgames/phonos/world/sound/CableConnection.java @@ -6,6 +6,8 @@ import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; +import java.util.Objects; + public class CableConnection { public final CablePlugPoint start; public final InputPlugPoint end; @@ -60,4 +62,17 @@ public static CableConnection readNbt(World world, CablePlugPoint start, NbtComp return new CableConnection(start, end, color, cable); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CableConnection that = (CableConnection) o; + return Objects.equals(start, that.start) && Objects.equals(end, that.end) && color == that.color && Objects.equals(drop, that.drop); + } + + @Override + public int hashCode() { + return Objects.hash(start, end, color, drop); + } } diff --git a/src/main/java/io/github/foundationgames/phonos/world/sound/block/BlockConnectionLayout.java b/src/main/java/io/github/foundationgames/phonos/world/sound/block/BlockConnectionLayout.java index b012bb0..88f894d 100644 --- a/src/main/java/io/github/foundationgames/phonos/world/sound/block/BlockConnectionLayout.java +++ b/src/main/java/io/github/foundationgames/phonos/world/sound/block/BlockConnectionLayout.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; public class BlockConnectionLayout { private final List connectionPoints = new ArrayList<>(); @@ -192,6 +193,19 @@ public double y() { public double z() { return this.blockPos.getZ() + 0.5; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockInput that = (BlockInput) o; + return Objects.equals(blockPos, that.blockPos); + } + + @Override + public int hashCode() { + return Objects.hash(blockPos); + } } public static class BlockOutput implements CablePlugPoint { @@ -224,5 +238,18 @@ public void writeOriginPose(World world, float delta, Pose3f out) { } out.rotation().set(RotationAxis.POSITIVE_Y.rotation(0)); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockOutput that = (BlockOutput) o; + return Objects.equals(blockPos, that.blockPos); + } + + @Override + public int hashCode() { + return Objects.hash(blockPos); + } } } diff --git a/src/main/java/io/github/foundationgames/phonos/world/sound/block/OutputBlockEntity.java b/src/main/java/io/github/foundationgames/phonos/world/sound/block/OutputBlockEntity.java index b8bac6b..630b0fd 100644 --- a/src/main/java/io/github/foundationgames/phonos/world/sound/block/OutputBlockEntity.java +++ b/src/main/java/io/github/foundationgames/phonos/world/sound/block/OutputBlockEntity.java @@ -1,5 +1,6 @@ package io.github.foundationgames.phonos.world.sound.block; +import io.github.foundationgames.phonos.client.render.BlockEntityClientState; import io.github.foundationgames.phonos.sound.emitter.SoundEmitter; import io.github.foundationgames.phonos.world.sound.InputPlugPoint; import net.minecraft.item.ItemStack; @@ -18,6 +19,8 @@ public interface OutputBlockEntity extends SoundEmitter { BlockEntityOutputs getOutputs(); + BlockEntityClientState getClientState(); + default Direction getRotation() { return Direction.NORTH; }