From 962975a09d00cd6b825bd039181633fe45fb448c Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sat, 5 Feb 2022 20:05:05 -0800 Subject: [PATCH] Beziterating - Instanced tracks - Cleaner bezier connection iteration --- gradle.properties | 2 +- .../com/simibubi/create/AllTileEntities.java | 6 +- .../logistics/trains/BezierConnection.java | 79 +++++++- .../logistics/trains/track/TrackInstance.java | 187 ++++++++++++++++++ .../logistics/trains/track/TrackRenderer.java | 56 ++---- .../trains/track/TrackTileEntity.java | 7 +- .../foundation/render/SuperByteBuffer.java | 22 ++- .../create/foundation/utility/Couple.java | 6 +- 8 files changed, 308 insertions(+), 57 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/track/TrackInstance.java diff --git a/gradle.properties b/gradle.properties index 17d861a56..11a681ea9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ parchment_version = 2022.01.23 # dependency versions registrate_version = MC1.18-1.0.21 -flywheel_version = 1.18-0.6.1.56 +flywheel_version = 1.18-0.6.1.57 jei_minecraft_version = 1.18.1 jei_version = 9.2.1.69 diff --git a/src/main/java/com/simibubi/create/AllTileEntities.java b/src/main/java/com/simibubi/create/AllTileEntities.java index 9e1dc3d54..5db6ed851 100644 --- a/src/main/java/com/simibubi/create/AllTileEntities.java +++ b/src/main/java/com/simibubi/create/AllTileEntities.java @@ -171,6 +171,7 @@ import com.simibubi.create.content.logistics.trains.IBogeyTileEntityRenderer; import com.simibubi.create.content.logistics.trains.management.StationRenderer; import com.simibubi.create.content.logistics.trains.management.StationTileEntity; import com.simibubi.create.content.logistics.trains.track.StandardBogeyTileEntity; +import com.simibubi.create.content.logistics.trains.track.TrackInstance; import com.simibubi.create.content.logistics.trains.track.TrackRenderer; import com.simibubi.create.content.logistics.trains.track.TrackTileEntity; import com.simibubi.create.content.schematics.block.SchematicTableTileEntity; @@ -730,16 +731,17 @@ public class AllTileEntities { public static final BlockEntityEntry TRACK = Create.registrate() .tileEntity("track", TrackTileEntity::new) + .instance(() -> TrackInstance::new) .renderer(() -> TrackRenderer::new) .validBlocks(AllBlocks.TRACK) .register(); - + public static final BlockEntityEntry BOGEY = Create.registrate() .tileEntity("bogey", StandardBogeyTileEntity::new) .renderer(() -> IBogeyTileEntityRenderer::new) .validBlocks(AllBlocks.SMALL_BOGEY, AllBlocks.LARGE_BOGEY) .register(); - + public static final BlockEntityEntry TRACK_STATION = Create.registrate() .tileEntity("track_station", StationTileEntity::new) .renderer(() -> StationRenderer::new) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java b/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java index f030c5182..a2f3bc9aa 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java @@ -1,5 +1,7 @@ package com.simibubi.create.content.logistics.trains; +import java.util.Iterator; + import com.jozufozu.flywheel.repack.joml.Math; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.VecHelper; @@ -13,7 +15,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; -public class BezierConnection { +public class BezierConnection implements Iterable { public Couple tePositions; public Couple trackEnds; @@ -126,6 +128,10 @@ public class BezierConnection { return handleLength; } + public float getSegmentT(int index) { + return index == segments ? 1 : index * stepLUT[index] / segments; + } + public double incrementT(double currentT, double distance) { resolve(); double dx = @@ -250,4 +256,73 @@ public class BezierConnection { handleLength = 1; } -} \ No newline at end of file + @Override + public Iterator iterator() { + resolve(); + var offset = Vec3.atLowerCornerOf(tePositions.getFirst()) + .scale(-1) + .add(0, 3 / 16f, 0); + return new Bezierator(this, offset); + } + + public static class Segment { + public int index; + public Vec3 position; + public Vec3 derivative; + public Vec3 faceNormal; + public Vec3 normal; + } + + private static class Bezierator implements Iterator { + + private final BezierConnection bc; + private final Segment segment; + private final Vec3 end1; + private final Vec3 end2; + private final Vec3 finish1; + private final Vec3 finish2; + private final Vec3 faceNormal1; + private final Vec3 faceNormal2; + + private Bezierator(BezierConnection bc, Vec3 offset) { + bc.resolve(); + this.bc = bc; + + end1 = bc.starts.getFirst() + .add(offset); + end2 = bc.starts.getSecond() + .add(offset); + + finish1 = bc.axes.getFirst() + .scale(bc.handleLength) + .add(end1); + finish2 = bc.axes.getSecond() + .scale(bc.handleLength) + .add(end2); + + faceNormal1 = bc.normals.getFirst(); + faceNormal2 = bc.normals.getSecond(); + segment = new Segment(); + segment.index = -1; // will get incremented to 0 in #next() + } + + @Override + public boolean hasNext() { + return segment.index + 1 <= bc.segments; + } + + @Override + public Segment next() { + segment.index++; + float t = this.bc.getSegmentT(segment.index); + segment.position = VecHelper.bezier(end1, end2, finish1, finish2, t); + segment.derivative = VecHelper.bezierDerivative(end1, end2, finish1, finish2, t) + .normalize(); + segment.faceNormal = faceNormal1.equals(faceNormal2) ? faceNormal1 : VecHelper.slerp(t, faceNormal1, faceNormal2); + segment.normal = segment.faceNormal.cross(segment.derivative) + .normalize(); + return segment; + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackInstance.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackInstance.java new file mode 100644 index 000000000..83710e190 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackInstance.java @@ -0,0 +1,187 @@ +package com.simibubi.create.content.logistics.trains.track; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.api.MaterialManager; +import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; +import com.jozufozu.flywheel.core.Materials; +import com.jozufozu.flywheel.core.materials.model.ModelData; +import com.jozufozu.flywheel.light.LightUpdater; +import com.jozufozu.flywheel.util.FlwUtil; +import com.jozufozu.flywheel.util.box.GridAlignedBB; +import com.jozufozu.flywheel.util.box.ImmutableBox; +import com.jozufozu.flywheel.util.transform.TransformStack; +import com.mojang.blaze3d.vertex.PoseStack; +import com.simibubi.create.AllBlockPartials; +import com.simibubi.create.content.logistics.trains.BezierConnection; +import com.simibubi.create.foundation.utility.Iterate; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; + +public class TrackInstance extends BlockEntityInstance { + + private List instances; + + public TrackInstance(MaterialManager materialManager, TrackTileEntity track) { + super(materialManager, track); + + update(); + } + + @Override + public void update() { + if (blockEntity.connections.stream().allMatch(Map::isEmpty)) { + return; + } + + instances = blockEntity.connections.stream() + .flatMap(FlwUtil::mapValues) + .map(this::createInstance) + .filter(Objects::nonNull) + .toList(); + LightUpdater.get(world).addListener(this); + } + + @Override + public ImmutableBox getVolume() { + List out = new ArrayList<>(); + out.addAll(blockEntity.connections.getFirst() + .keySet()); + out.addAll(blockEntity.connections.getSecond() + .keySet()); + return GridAlignedBB.containingAll(out); + } + + @Override + public void updateLight() { + if (instances == null) return; + instances.forEach(BezierInstance::updateLight); + } + + @Nullable + private BezierInstance createInstance(BezierConnection bc) { + if (!bc.isPrimary()) return null; + return new BezierInstance(bc); + } + + @Override + public void remove() { + if (instances == null) return; + instances.forEach(BezierInstance::delete); + } + + private class BezierInstance { + + private final ModelData[] ties; + private final ModelData[] left; + private final ModelData[] right; + private final BlockPos[] tiesLightPos; + private final BlockPos[] leftLightPos; + private final BlockPos[] rightLightPos; + + private BezierInstance(BezierConnection bc) { + BlockPos tePosition = bc.tePositions.getFirst(); + + PoseStack pose = new PoseStack(); + TransformStack.cast(pose) + .translate(getInstancePosition()) + .nudge((int) bc.tePositions.getFirst() + .asLong()); + + var mat = materialManager.defaultSolid() + .material(Materials.TRANSFORMED); + + int segCount = bc.getSegmentCount(); + ties = new ModelData[segCount]; + left = new ModelData[segCount]; + right = new ModelData[segCount]; + tiesLightPos = new BlockPos[segCount]; + leftLightPos = new BlockPos[segCount]; + rightLightPos = new BlockPos[segCount]; + + mat.getModel(AllBlockPartials.TRACK_TIE) + .createInstances(ties); + mat.getModel(AllBlockPartials.TRACK_SEGMENT_LEFT) + .createInstances(left); + mat.getModel(AllBlockPartials.TRACK_SEGMENT_RIGHT) + .createInstances(right); + + Vec3 leftPrevious = null; + Vec3 rightPrevious = null; + + for (BezierConnection.Segment segment : bc) { + Vec3 left = segment.position.add(segment.normal.scale(.97f)); + Vec3 right = segment.position.subtract(segment.normal.scale(.97f)); + + if (leftPrevious != null) { + var modelIndex = segment.index - 1; + { + // Tie + Vec3 railMiddle = left.add(right) + .scale(.5); + Vec3 prevMiddle = leftPrevious.add(rightPrevious) + .scale(.5); + + var tie = ties[modelIndex].setTransform(pose); + Vec3 diff = railMiddle.subtract(prevMiddle); + Vec3 angles = TrackRenderer.getModelAngles(segment.normal, diff); + + tie.translate(prevMiddle) + .rotateYRadians(angles.y) + .rotateXRadians(angles.x) + .rotateZRadians(angles.z) + .translate(-1 / 2f, -2 / 16f - 1 / 1024f, 0); + tiesLightPos[modelIndex] = new BlockPos(railMiddle).offset(tePosition); + } + + // Rails + for (boolean first : Iterate.trueAndFalse) { + Vec3 railI = first ? left : right; + Vec3 prevI = first ? leftPrevious : rightPrevious; + + var rail = (first ? this.left : this.right)[modelIndex].setTransform(pose); + Vec3 diff = railI.subtract(prevI); + Vec3 angles = TrackRenderer.getModelAngles(segment.normal, diff); + + rail.translate(prevI) + .rotateYRadians(angles.y) + .rotateXRadians(angles.x) + .rotateZRadians(angles.z) + .translate(0, -2 / 16f + (segment.index % 2 == 0 ? 1 : -1) / 2048f - 1 / 1024f, 0) + .scale(1, 1, (float) diff.length() * 2.1f); + (first ? leftLightPos : rightLightPos)[modelIndex] = new BlockPos(prevI).offset(tePosition); + } + } + + leftPrevious = left; + rightPrevious = right; + } + + updateLight(); + } + + void delete() { + for (ModelData d : ties) d.delete(); + for (ModelData d : left) d.delete(); + for (ModelData d : right) d.delete(); + } + + void updateLight() { + for (int i = 0; i < ties.length; i++) { + ties[i].updateLight(world, tiesLightPos[i]); + } + for (int i = 0; i < left.length; i++) { + left[i].updateLight(world, leftLightPos[i]); + } + for (int i = 0; i < right.length; i++) { + right[i].updateLight(world, rightLightPos[i]); + } + } + } +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java index 367acb5b7..e01de35ee 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRenderer.java @@ -1,7 +1,8 @@ package com.simibubi.create.content.logistics.trains.track; +import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.repack.joml.Math; -import com.jozufozu.flywheel.util.transform.MatrixTransformStack; +import com.jozufozu.flywheel.util.transform.TransformStack; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.simibubi.create.AllBlockPartials; @@ -31,6 +32,8 @@ public class TrackRenderer extends SafeTileEntityRenderer { @Override protected void renderSafe(TrackTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light, int overlay) { + if (Backend.isOn()) return; + VertexConsumer vb = buffer.getBuffer(RenderType.solid()); te.connections.forEach(map -> map.values() .forEach(bc -> renderBezierTurn(bc, ms, vb))); @@ -41,49 +44,19 @@ public class TrackRenderer extends SafeTileEntityRenderer { return; ms.pushPose(); - new MatrixTransformStack(ms).nudge((int) bc.tePositions.getFirst() - .asLong()); - BlockPos tePosition = bc.tePositions.getFirst(); - Vec3 end1 = bc.starts.getFirst() - .subtract(Vec3.atLowerCornerOf(tePosition)) - .add(0, 3 / 16f, 0); - Vec3 end2 = bc.starts.getSecond() - .subtract(Vec3.atLowerCornerOf(tePosition)) - .add(0, 3 / 16f, 0); - Vec3 axis1 = bc.axes.getFirst(); - Vec3 axis2 = bc.axes.getSecond(); - double handleLength = bc.getHandleLength(); + TransformStack.cast(ms) + .nudge((int) tePosition.asLong()); - Vec3 finish1 = axis1.scale(handleLength) - .add(end1); - Vec3 finish2 = axis2.scale(handleLength) - .add(end2); - - Vec3 faceNormal1 = bc.normals.getFirst(); - Vec3 faceNormal2 = bc.normals.getSecond(); Vec3 previous1 = null; Vec3 previous2 = null; - int segCount = bc.getSegmentCount(); - float[] lut = bc.getStepLUT(); - - for (int i = 0; i <= segCount; i++) { - float t = i == segCount ? 1 : i * lut[i] / segCount; - - Vec3 result = VecHelper.bezier(end1, end2, finish1, finish2, t); - Vec3 derivative = VecHelper.bezierDerivative(end1, end2, finish1, finish2, t) - .normalize(); - Vec3 faceNormal = - faceNormal1.equals(faceNormal2) ? faceNormal1 : VecHelper.slerp(t, faceNormal1, faceNormal2); - Vec3 normal = faceNormal.cross(derivative) - .normalize(); - Vec3 rail1 = result.add(normal.scale(.97f)); - Vec3 rail2 = result.subtract(normal.scale(.97f)); + for (BezierConnection.Segment segment : bc) { + Vec3 rail1 = segment.position.add(segment.normal.scale(.97f)); + Vec3 rail2 = segment.position.subtract(segment.normal.scale(.97f)); if (previous1 != null) { - ms.pushPose(); { // Tie Vec3 railMiddle = rail1.add(rail2) @@ -91,7 +64,7 @@ public class TrackRenderer extends SafeTileEntityRenderer { Vec3 prevMiddle = previous1.add(previous2) .scale(.5); Vec3 diff = railMiddle.subtract(prevMiddle); - Vec3 angles = getModelAngles(normal, diff); + Vec3 angles = getModelAngles(segment.normal, diff); SuperByteBuffer sbb = CachedBufferer.partial(AllBlockPartials.TRACK_TIE, Blocks.AIR.defaultBlockState()); @@ -106,16 +79,13 @@ public class TrackRenderer extends SafeTileEntityRenderer { new BlockPos(railMiddle).offset(tePosition))); sbb.renderInto(ms, vb); } - ms.popPose(); // Rails for (boolean first : Iterate.trueAndFalse) { - ms.pushPose(); - Vec3 railI = first ? rail1 : rail2; Vec3 prevI = first ? previous1 : previous2; Vec3 diff = railI.subtract(prevI); - Vec3 angles = getModelAngles(normal, diff); + Vec3 angles = getModelAngles(segment.normal, diff); SuperByteBuffer sbb = CachedBufferer.partial( first ? AllBlockPartials.TRACK_SEGMENT_LEFT : AllBlockPartials.TRACK_SEGMENT_RIGHT, @@ -125,14 +95,12 @@ public class TrackRenderer extends SafeTileEntityRenderer { .rotateYRadians(angles.y) .rotateXRadians(angles.x) .rotateZRadians(angles.z) - .translate(0, -2 / 16f + (i % 2 == 0 ? 1 : -1) / 2048f - 1 / 1024f, 0) + .translate(0, -2 / 16f + (segment.index % 2 == 0 ? 1 : -1) / 2048f - 1 / 1024f, 0) .scale(1, 1, (float) diff.length() * 2.1f); sbb.light(LevelRenderer.getLightColor(Minecraft.getInstance().level, new BlockPos(prevI).offset(tePosition))); sbb.renderInto(ms, vb); - - ms.popPose(); } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java index de09acd17..d83a73721 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; @@ -16,6 +17,8 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; public class TrackTileEntity extends SmartTileEntity { @@ -29,7 +32,7 @@ public class TrackTileEntity extends SmartTileEntity { public Couple> getConnections() { return connections; } - + public void addConnection(boolean front, BezierConnection connection) { connections.get(front) .put(connection.getKey(), connection); @@ -94,6 +97,8 @@ public class TrackTileEntity extends SmartTileEntity { BezierConnection connection = new BezierConnection((CompoundTag) t); map.put(connection.getKey(), connection); })); + + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> InstancedRenderDispatcher.enqueueUpdate(this)); } @Override diff --git a/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java b/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java index 6f47ade73..156a198d8 100644 --- a/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java +++ b/src/main/java/com/simibubi/create/foundation/render/SuperByteBuffer.java @@ -3,10 +3,8 @@ package com.simibubi.create.foundation.render; import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.core.vertex.BlockVertexList; -import com.jozufozu.flywheel.util.transform.Rotate; -import com.jozufozu.flywheel.util.transform.Scale; import com.jozufozu.flywheel.util.transform.TStack; -import com.jozufozu.flywheel.util.transform.Translate; +import com.jozufozu.flywheel.util.transform.Transform; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; @@ -31,7 +29,7 @@ import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraftforge.client.model.pipeline.LightUtil; -public class SuperByteBuffer implements Scale, Translate, Rotate, TStack { +public class SuperByteBuffer implements Transform, TStack { private final VertexList template; @@ -241,6 +239,22 @@ public class SuperByteBuffer implements Scale, Translate extends Pair implements Iterable { - private static Couple TRUE_AND_FALSE = Couple.create(true, false); + private static final Couple TRUE_AND_FALSE = Couple.create(true, false); protected Couple(T first, T second) { super(first, second); @@ -33,7 +33,7 @@ public class Couple extends Pair implements Iterable { public static Couple createWithContext(Function factory) { return new Couple<>(factory.apply(true), factory.apply(false)); } - + public T get(boolean first) { return first ? getFirst() : getSecond(); }