Beziterating

- Instanced tracks
 - Cleaner bezier connection iteration
This commit is contained in:
Jozufozu 2022-02-05 20:05:05 -08:00
parent 87bdd8d586
commit 962975a09d
8 changed files with 308 additions and 57 deletions

View file

@ -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

View file

@ -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,6 +731,7 @@ public class AllTileEntities {
public static final BlockEntityEntry<TrackTileEntity> TRACK = Create.registrate()
.tileEntity("track", TrackTileEntity::new)
.instance(() -> TrackInstance::new)
.renderer(() -> TrackRenderer::new)
.validBlocks(AllBlocks.TRACK)
.register();

View file

@ -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<BezierConnection.Segment> {
public Couple<BlockPos> tePositions;
public Couple<Boolean> 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;
}
@Override
public Iterator<Segment> 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<Segment> {
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;
}
}
}

View file

@ -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<TrackTileEntity> {
private List<BezierInstance> 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<BlockPos> 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]);
}
}
}
}

View file

@ -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<TrackTileEntity> {
@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<TrackTileEntity> {
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<TrackTileEntity> {
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<TrackTileEntity> {
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<TrackTileEntity> {
.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();
}
}

View file

@ -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 {
@ -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

View file

@ -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<SuperByteBuffer>, Translate<SuperByteBuffer>, Rotate<SuperByteBuffer>, TStack<SuperByteBuffer> {
public class SuperByteBuffer implements Transform<SuperByteBuffer>, TStack<SuperByteBuffer> {
private final VertexList template;
@ -241,6 +239,22 @@ public class SuperByteBuffer implements Scale<SuperByteBuffer>, Translate<SuperB
return this;
}
@Override
public SuperByteBuffer mulPose(Matrix4f pose) {
transforms.last()
.pose()
.multiply(pose);
return this;
}
@Override
public SuperByteBuffer mulNormal(Matrix3f normal) {
transforms.last()
.normal()
.mul(normal);
return this;
}
public SuperByteBuffer transform(PoseStack stack) {
transforms.last()
.pose()

View file

@ -6,9 +6,9 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import net.minecraft.nbt.CompoundTag;
@ -16,7 +16,7 @@ import net.minecraft.nbt.ListTag;
public class Couple<T> extends Pair<T, T> implements Iterable<T> {
private static Couple<Boolean> TRUE_AND_FALSE = Couple.create(true, false);
private static final Couple<Boolean> TRUE_AND_FALSE = Couple.create(true, false);
protected Couple(T first, T second) {
super(first, second);