Skip to content

Commit accee4a

Browse files
committed
Update to lastest chain renderer
1 parent 06a0261 commit accee4a

File tree

2 files changed

+129
-30
lines changed

2 files changed

+129
-30
lines changed

src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorBlockEntity.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import java.util.Set;
1111
import java.util.function.Consumer;
1212

13-
import org.jetbrains.annotations.Nullable;
13+
import javax.annotation.Nullable;
1414

1515
import com.simibubi.create.api.contraption.transformable.TransformableBlockEntity;
1616
import com.simibubi.create.content.contraptions.StructureTransform;
@@ -88,7 +88,30 @@ public ChainConveyorBlockEntity(BlockEntityType<?> typeIn, BlockPos pos, BlockSt
8888

8989
@Override
9090
protected AABB createRenderBoundingBox() {
91-
return AABB.INFINITE;
91+
if (connections.isEmpty()) {
92+
return new AABB(worldPosition).inflate(3);
93+
} else {
94+
Vec3 mid = Vec3.atLowerCornerOf(worldPosition);
95+
double minX = mid.x;
96+
double minY = mid.y;
97+
double minZ = mid.z;
98+
double maxX = mid.x;
99+
double maxY = mid.y;
100+
double maxZ = mid.z;
101+
102+
for (BlockPos connection : connections) {
103+
ConnectionStats stats = connectionStats.get(connection);
104+
if (stats == null)
105+
continue;
106+
minX = Math.min(minX, Math.min(stats.start.x, stats.end.x));
107+
minY = Math.min(minY, Math.min(stats.start.y, stats.end.y));
108+
minZ = Math.min(minZ, Math.min(stats.start.z, stats.end.z));
109+
maxX = Math.max(maxX, Math.max(stats.start.x, stats.end.x));
110+
maxY = Math.max(maxY, Math.max(stats.start.y, stats.end.y));
111+
maxZ = Math.max(maxZ, Math.max(stats.start.z, stats.end.z));
112+
}
113+
return new AABB(minX, minY, minZ, maxX, maxY, maxZ).inflate(3);
114+
}
92115
}
93116

94117
@Override
@@ -668,7 +691,7 @@ public float propagateRotationTo(KineticBlockEntity target, BlockState stateFrom
668691
@Override
669692
public void writeSafe(CompoundTag tag, HolderLookup.Provider registries) {
670693
super.writeSafe(tag, registries);
671-
tag.put("Connections", CatnipCodecUtils.encode(CatnipCodecs.set(BlockPos.CODEC), registries, connections).orElseThrow());
694+
tag.put("Connections", CatnipCodecUtils.encode(CatnipCodecs.set(BlockPos.CODEC), connections).orElseThrow());
672695
}
673696

674697
@Override
@@ -679,7 +702,7 @@ protected void write(CompoundTag compound, HolderLookup.Provider registries, boo
679702
chainDestroyedEffectToSend = null;
680703
}
681704

682-
compound.put("Connections", CatnipCodecUtils.encode(CatnipCodecs.set(BlockPos.CODEC), registries, connections).orElseThrow());
705+
compound.put("Connections", CatnipCodecUtils.encode(CatnipCodecs.set(BlockPos.CODEC), connections).orElseThrow());
683706
compound.put("TravellingPackages", NBTHelper.writeCompoundList(travellingPackages.entrySet(), entry -> {
684707
CompoundTag compoundTag = new CompoundTag();
685708
compoundTag.put("Target", NbtUtils.writeBlockPos(entry.getKey()));
@@ -699,7 +722,7 @@ protected void read(CompoundTag compound, HolderLookup.Provider registries, bool
699722

700723
int sizeBefore = connections.size();
701724
connections.clear();
702-
CatnipCodecUtils.decode(CatnipCodecs.set(BlockPos.CODEC), registries, compound.get("Connections")).ifPresent(connections::addAll);
725+
CatnipCodecUtils.decode(CatnipCodecs.set(BlockPos.CODEC), compound.get("Connections")).ifPresent(connections::addAll);
703726
travellingPackages.clear();
704727
NBTHelper.iterateCompoundList(compound.getList("TravellingPackages", Tag.TAG_COMPOUND),
705728
c -> travellingPackages.put(NBTHelper.readBlockPos(c, "Target"),

src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorRenderer.java

Lines changed: 101 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import net.minecraft.world.level.block.state.BlockState;
3939
import net.minecraft.world.phys.Vec3;
4040

41+
import org.joml.Vector3f;
42+
4143
public class ChainConveyorRenderer extends KineticBlockEntityRenderer<ChainConveyorBlockEntity> {
4244

4345
public static final ResourceLocation CHAIN_LOCATION = ResourceLocation.withDefaultNamespace("textures/block/chain.png");
@@ -55,10 +57,9 @@ protected void renderSafe(ChainConveyorBlockEntity be, float partialTicks, PoseS
5557

5658
FrustumIntersection frustum = null;
5759
Vec3 camPos = null;
58-
if(Minecraft.getInstance().level == be.getLevel())
59-
{
60+
if (Minecraft.getInstance().level == be.getLevel()) {
6061
frustum = getFrustumIntersection();
61-
camPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
62+
camPos = Minecraft.getInstance().getBlockEntityRenderDispatcher().camera.getPosition();
6263
}
6364
boolean renderCentre = frustum == null || frustum.testAab(pos.getX() - 2 - (float) camPos.x, pos.getY() - (float) camPos.y, pos.getZ() - 2 - (float) camPos.z, pos.getX() + 2 - (float) camPos.x, pos.getY() + 1 - (float) camPos.y, pos.getZ() + 2 - (float) camPos.z);
6465
renderChains(be, ms, buffer, light, overlay, frustum, camPos, renderCentre);
@@ -144,13 +145,79 @@ private void renderBox(ChainConveyorBlockEntity be, PoseStack ms, MultiBufferSou
144145
}
145146
}
146147

147-
private static Vec3 getClosestPointOnChain(Vec3 cam, Vec3 start, Vec3 end) {
148-
Vec3 seg = end.subtract(start);
149-
Vec3 start2cam = cam.subtract(start);
148+
/**
149+
* Calculate the intersection points between a line segment and a circle centered at cameraPos with radius LODDistance.
150+
* The intersections array is used to store up to 2 intersection points.
151+
* Returns the number of intersection points (0, 1, or 2).
152+
*/
153+
private static int calculateLineCircleIntersection(Vec3 start, Vec3 end, Vec3 cameraPos, Vec3[] intersections) {
154+
Vec3 ab = end.subtract(start);
155+
Vec3 ac = start.subtract(cameraPos);
156+
float a = (float) ab.lengthSqr();
157+
float b = 2 * (float) ac.dot(ab);
158+
float c = (float) ac.lengthSqr() - MIP_DISTANCE_SQR;
159+
float discriminant = b * b - 4 * a * c;
160+
161+
if (discriminant < 0) {
162+
return 0; // No intersection
163+
}
150164

151-
double t = Mth.clamp(start2cam.dot(seg) / seg.lengthSqr(), 0.0, 1.0);
165+
float sqrtDisc = Mth.sqrt(discriminant);
166+
float t1 = (-b - sqrtDisc) / (2 * a);
167+
float t2 = (-b + sqrtDisc) / (2 * a);
168+
int count = 0;
169+
if (t1 >= 0 && t1 <= 1) {
170+
intersections[count++] = start.add(ab.scale(t1));
171+
}
172+
// Avoid duplicate calculations (when t1 and t2 are almost equal)
173+
if (t2 >= 0 && t2 <= 1 && Math.abs(t2 - t1) > 1e-6f) {
174+
intersections[count++] = start.add(ab.scale(t2));
175+
}
176+
return count;
177+
}
152178

153-
return start.add(seg.scale(t));
179+
/**
180+
* Cut the line segment based on the intersection points with the circle centered at the camera position.
181+
* The output Vector3f contains:
182+
* x: The distance from the start of the line segment to the intersection point (outside the LOD);
183+
* y: The length of the part of the line segment LOD0;
184+
* z: The distance from the intersection point to the end of the line segment (outside the LOD).
185+
*/
186+
public static Vector3f calculateLODCut(Vec3 start, Vec3 end, Vec3 cameraPos) {
187+
Vec3[] intersections = new Vec3[2];
188+
int intersectionCount = calculateLineCircleIntersection(start, end, cameraPos, intersections);
189+
float totalLength = (float) start.distanceTo(end);
190+
float x = 0, y = 0, z = 0;
191+
192+
if (intersectionCount == 0) {
193+
// No intersection: Determine if the line segment is entirely inside or outside the circle
194+
if (start.distanceToSqr(cameraPos) < MIP_DISTANCE_SQR && end.distanceToSqr(cameraPos) < MIP_DISTANCE_SQR) {
195+
// Both ends are inside the circle
196+
y = totalLength;
197+
} else {
198+
// The line segment is entirely outside the circle
199+
x = totalLength;
200+
}
201+
} else if (intersectionCount == 1) {
202+
// Only one intersection point, determine which end is inside the circle
203+
// one end must be inside and the other outside
204+
boolean endInside = end.distanceToSqr(cameraPos) < MIP_DISTANCE_SQR;
205+
if (endInside) {
206+
x = (float) start.distanceTo(intersections[0]);
207+
y = (float) intersections[0].distanceTo(end);
208+
z = 0;
209+
} else {
210+
x = 0;
211+
y = (float) start.distanceTo(intersections[0]);
212+
z = (float) intersections[0].distanceTo(end);
213+
}
214+
} else if (intersectionCount == 2) {
215+
x = (float) start.distanceTo(intersections[0]);
216+
y = (float) intersections[0].distanceTo(intersections[1]);
217+
z = (float) end.distanceTo(intersections[1]);
218+
}
219+
220+
return new Vector3f(x, y, z);
154221
}
155222

156223
private void renderChains(ChainConveyorBlockEntity be, PoseStack ms, MultiBufferSource buffer, int light,
@@ -191,8 +258,13 @@ private void renderChains(ChainConveyorBlockEntity be, PoseStack ms, MultiBuffer
191258

192259
Level level = be.getLevel();
193260
BlockPos tilePos = be.getBlockPos();
194-
Vec3 startOffset = stats.start()
195-
.subtract(Vec3.atCenterOf(tilePos));
261+
262+
int light1 = LightTexture.pack(level.getBrightness(LightLayer.BLOCK, tilePos),
263+
level.getBrightness(LightLayer.SKY, tilePos));
264+
int light2 = LightTexture.pack(level.getBrightness(LightLayer.BLOCK, tilePos.offset(blockPos)),
265+
level.getBrightness(LightLayer.SKY, tilePos.offset(blockPos)));
266+
267+
Vec3 startOffset = stats.start().subtract(Vec3.atCenterOf(tilePos));
196268

197269
ms.pushPose();
198270
var chain = TransformStack.of(ms);
@@ -204,29 +276,34 @@ private void renderChains(ChainConveyorBlockEntity be, PoseStack ms, MultiBuffer
204276
chain.translate(0, 8 / 16f, 0);
205277
chain.uncenter();
206278

207-
int light1 = LightTexture.pack(level.getBrightness(LightLayer.BLOCK, tilePos),
208-
level.getBrightness(LightLayer.SKY, tilePos));
209-
int light2 = LightTexture.pack(level.getBrightness(LightLayer.BLOCK, tilePos.offset(blockPos)),
210-
level.getBrightness(LightLayer.SKY, tilePos.offset(blockPos)));
211-
212-
boolean far = false;
213279
if (frustum != null) {
214-
Vec3 closest = getClosestPointOnChain(camPos, stats.start(), stats.end());
215-
if (closest.distanceToSqr(camPos) > MIP_DISTANCE_SQR)
216-
far = true;
280+
Vector3f length = calculateLODCut(stats.start(), stats.end(), camPos);
281+
if (length.x > 1e-6f) {
282+
renderChain(ms, buffer, animation, 0, length.x, light1, light2, true);
283+
}
284+
285+
if (length.y > 1e-6f) {
286+
chain.translate(0, length.x, 0);
287+
renderChain(ms, buffer, animation, length.x, length.y, light1, light2, false);
288+
}
289+
290+
if (length.z > 1e-6f) {
291+
chain.translate(0, length.y, 0);
292+
renderChain(ms, buffer, animation, 0, length.z, light1, light2, true);
293+
}
294+
} else {
295+
renderChain(ms, buffer, animation, 0, stats.chainLength(), light1, light2, false);
217296
}
218297

219-
renderChain(ms, buffer, animation, stats.chainLength(), light1, light2, far);
220-
221298
ms.popPose();
222299
}
223300
}
224301

225-
public static void renderChain(PoseStack ms, MultiBufferSource buffer, float animation, float length, int light1,
302+
public static void renderChain(PoseStack ms, MultiBufferSource buffer, float animation, float start, float length, int light1,
226303
int light2, boolean far) {
227304
float radius = far ? 1f / 16f : 1.5f / 16f;
228-
float minV = far ? 0 : animation;
229-
float maxV = far ? 1 / 16f : length + minV;
305+
float maxV = far ? 0 : animation - start;
306+
float minV = far ? 1 / 16f : maxV - length;
230307
float minU = far ? 3 / 16f : 0;
231308
float maxU = far ? 4 / 16f : 3 / 16f;
232309

@@ -236,7 +313,6 @@ public static void renderChain(PoseStack ms, MultiBufferSource buffer, float ani
236313
VertexConsumer vc = buffer.getBuffer(RenderTypes.chain(CHAIN_LOCATION));
237314
renderPart(ms, vc, length, 0.0F, radius, radius, 0.0F, -radius, 0.0F, 0.0F, -radius, minU, maxU, minV, maxV,
238315
light1, light2, far);
239-
240316
ms.popPose();
241317
}
242318

0 commit comments

Comments
 (0)