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 # dependency versions
registrate_version = MC1.18-1.0.21 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_minecraft_version = 1.18.1
jei_version = 9.2.1.69 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.StationRenderer;
import com.simibubi.create.content.logistics.trains.management.StationTileEntity; 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.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.TrackRenderer;
import com.simibubi.create.content.logistics.trains.track.TrackTileEntity; import com.simibubi.create.content.logistics.trains.track.TrackTileEntity;
import com.simibubi.create.content.schematics.block.SchematicTableTileEntity; import com.simibubi.create.content.schematics.block.SchematicTableTileEntity;
@ -730,16 +731,17 @@ public class AllTileEntities {
public static final BlockEntityEntry<TrackTileEntity> TRACK = Create.registrate() public static final BlockEntityEntry<TrackTileEntity> TRACK = Create.registrate()
.tileEntity("track", TrackTileEntity::new) .tileEntity("track", TrackTileEntity::new)
.instance(() -> TrackInstance::new)
.renderer(() -> TrackRenderer::new) .renderer(() -> TrackRenderer::new)
.validBlocks(AllBlocks.TRACK) .validBlocks(AllBlocks.TRACK)
.register(); .register();
public static final BlockEntityEntry<StandardBogeyTileEntity> BOGEY = Create.registrate() public static final BlockEntityEntry<StandardBogeyTileEntity> BOGEY = Create.registrate()
.tileEntity("bogey", StandardBogeyTileEntity::new) .tileEntity("bogey", StandardBogeyTileEntity::new)
.renderer(() -> IBogeyTileEntityRenderer::new) .renderer(() -> IBogeyTileEntityRenderer::new)
.validBlocks(AllBlocks.SMALL_BOGEY, AllBlocks.LARGE_BOGEY) .validBlocks(AllBlocks.SMALL_BOGEY, AllBlocks.LARGE_BOGEY)
.register(); .register();
public static final BlockEntityEntry<StationTileEntity> TRACK_STATION = Create.registrate() public static final BlockEntityEntry<StationTileEntity> TRACK_STATION = Create.registrate()
.tileEntity("track_station", StationTileEntity::new) .tileEntity("track_station", StationTileEntity::new)
.renderer(() -> StationRenderer::new) .renderer(() -> StationRenderer::new)

View file

@ -1,5 +1,7 @@
package com.simibubi.create.content.logistics.trains; package com.simibubi.create.content.logistics.trains;
import java.util.Iterator;
import com.jozufozu.flywheel.repack.joml.Math; import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
@ -13,7 +15,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class BezierConnection { public class BezierConnection implements Iterable<BezierConnection.Segment> {
public Couple<BlockPos> tePositions; public Couple<BlockPos> tePositions;
public Couple<Boolean> trackEnds; public Couple<Boolean> trackEnds;
@ -126,6 +128,10 @@ public class BezierConnection {
return handleLength; return handleLength;
} }
public float getSegmentT(int index) {
return index == segments ? 1 : index * stepLUT[index] / segments;
}
public double incrementT(double currentT, double distance) { public double incrementT(double currentT, double distance) {
resolve(); resolve();
double dx = double dx =
@ -250,4 +256,73 @@ public class BezierConnection {
handleLength = 1; 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; 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.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.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlockPartials; import com.simibubi.create.AllBlockPartials;
@ -31,6 +32,8 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
@Override @Override
protected void renderSafe(TrackTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light, protected void renderSafe(TrackTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
int overlay) { int overlay) {
if (Backend.isOn()) return;
VertexConsumer vb = buffer.getBuffer(RenderType.solid()); VertexConsumer vb = buffer.getBuffer(RenderType.solid());
te.connections.forEach(map -> map.values() te.connections.forEach(map -> map.values()
.forEach(bc -> renderBezierTurn(bc, ms, vb))); .forEach(bc -> renderBezierTurn(bc, ms, vb)));
@ -41,49 +44,19 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
return; return;
ms.pushPose(); ms.pushPose();
new MatrixTransformStack(ms).nudge((int) bc.tePositions.getFirst()
.asLong());
BlockPos tePosition = bc.tePositions.getFirst(); 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 previous1 = null;
Vec3 previous2 = null; Vec3 previous2 = null;
int segCount = bc.getSegmentCount(); for (BezierConnection.Segment segment : bc) {
float[] lut = bc.getStepLUT(); Vec3 rail1 = segment.position.add(segment.normal.scale(.97f));
Vec3 rail2 = segment.position.subtract(segment.normal.scale(.97f));
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));
if (previous1 != null) { if (previous1 != null) {
ms.pushPose();
{ {
// Tie // Tie
Vec3 railMiddle = rail1.add(rail2) Vec3 railMiddle = rail1.add(rail2)
@ -91,7 +64,7 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
Vec3 prevMiddle = previous1.add(previous2) Vec3 prevMiddle = previous1.add(previous2)
.scale(.5); .scale(.5);
Vec3 diff = railMiddle.subtract(prevMiddle); Vec3 diff = railMiddle.subtract(prevMiddle);
Vec3 angles = getModelAngles(normal, diff); Vec3 angles = getModelAngles(segment.normal, diff);
SuperByteBuffer sbb = SuperByteBuffer sbb =
CachedBufferer.partial(AllBlockPartials.TRACK_TIE, Blocks.AIR.defaultBlockState()); CachedBufferer.partial(AllBlockPartials.TRACK_TIE, Blocks.AIR.defaultBlockState());
@ -106,16 +79,13 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
new BlockPos(railMiddle).offset(tePosition))); new BlockPos(railMiddle).offset(tePosition)));
sbb.renderInto(ms, vb); sbb.renderInto(ms, vb);
} }
ms.popPose();
// Rails // Rails
for (boolean first : Iterate.trueAndFalse) { for (boolean first : Iterate.trueAndFalse) {
ms.pushPose();
Vec3 railI = first ? rail1 : rail2; Vec3 railI = first ? rail1 : rail2;
Vec3 prevI = first ? previous1 : previous2; Vec3 prevI = first ? previous1 : previous2;
Vec3 diff = railI.subtract(prevI); Vec3 diff = railI.subtract(prevI);
Vec3 angles = getModelAngles(normal, diff); Vec3 angles = getModelAngles(segment.normal, diff);
SuperByteBuffer sbb = CachedBufferer.partial( SuperByteBuffer sbb = CachedBufferer.partial(
first ? AllBlockPartials.TRACK_SEGMENT_LEFT : AllBlockPartials.TRACK_SEGMENT_RIGHT, first ? AllBlockPartials.TRACK_SEGMENT_LEFT : AllBlockPartials.TRACK_SEGMENT_RIGHT,
@ -125,14 +95,12 @@ public class TrackRenderer extends SafeTileEntityRenderer<TrackTileEntity> {
.rotateYRadians(angles.y) .rotateYRadians(angles.y)
.rotateXRadians(angles.x) .rotateXRadians(angles.x)
.rotateZRadians(angles.z) .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); .scale(1, 1, (float) diff.length() * 2.1f);
sbb.light(LevelRenderer.getLightColor(Minecraft.getInstance().level, sbb.light(LevelRenderer.getLightColor(Minecraft.getInstance().level,
new BlockPos(prevI).offset(tePosition))); new BlockPos(prevI).offset(tePosition)));
sbb.renderInto(ms, vb); sbb.renderInto(ms, vb);
ms.popPose();
} }
} }

View file

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; 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.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
public class TrackTileEntity extends SmartTileEntity { public class TrackTileEntity extends SmartTileEntity {
@ -29,7 +32,7 @@ public class TrackTileEntity extends SmartTileEntity {
public Couple<Map<BlockPos, BezierConnection>> getConnections() { public Couple<Map<BlockPos, BezierConnection>> getConnections() {
return connections; return connections;
} }
public void addConnection(boolean front, BezierConnection connection) { public void addConnection(boolean front, BezierConnection connection) {
connections.get(front) connections.get(front)
.put(connection.getKey(), connection); .put(connection.getKey(), connection);
@ -94,6 +97,8 @@ public class TrackTileEntity extends SmartTileEntity {
BezierConnection connection = new BezierConnection((CompoundTag) t); BezierConnection connection = new BezierConnection((CompoundTag) t);
map.put(connection.getKey(), connection); map.put(connection.getKey(), connection);
})); }));
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> InstancedRenderDispatcher.enqueueUpdate(this));
} }
@Override @Override

View file

@ -3,10 +3,8 @@ package com.simibubi.create.foundation.render;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.core.vertex.BlockVertexList; 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.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.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
@ -31,7 +29,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraftforge.client.model.pipeline.LightUtil; 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; private final VertexList template;
@ -241,6 +239,22 @@ public class SuperByteBuffer implements Scale<SuperByteBuffer>, Translate<SuperB
return this; 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) { public SuperByteBuffer transform(PoseStack stack) {
transforms.last() transforms.last()
.pose() .pose()

View file

@ -6,9 +6,9 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import net.minecraft.nbt.CompoundTag; 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> { 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) { protected Couple(T first, T second) {
super(first, second); super(first, second);
@ -33,7 +33,7 @@ public class Couple<T> extends Pair<T, T> implements Iterable<T> {
public static <T> Couple<T> createWithContext(Function<Boolean, T> factory) { public static <T> Couple<T> createWithContext(Function<Boolean, T> factory) {
return new Couple<>(factory.apply(true), factory.apply(false)); return new Couple<>(factory.apply(true), factory.apply(false));
} }
public T get(boolean first) { public T get(boolean first) {
return first ? getFirst() : getSecond(); return first ? getFirst() : getSecond();
} }