package com.simibubi.create.content.logistics.trains.entity; import java.util.Collection; import java.util.List; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.CreateClient; import com.simibubi.create.foundation.render.CachedBufferer; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AnimationTickHolder; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; public class CarriageCouplingRenderer { public static void renderAll(PoseStack ms, MultiBufferSource buffer, Vec3 camera) { Collection trains = CreateClient.RAILWAYS.trains.values(); VertexConsumer vb = buffer.getBuffer(RenderType.solid()); BlockState air = Blocks.AIR.defaultBlockState(); float partialTicks = AnimationTickHolder.getPartialTicks(); Level level = Minecraft.getInstance().level; for (Train train : trains) { List carriages = train.carriages; for (int i = 0; i < carriages.size() - 1; i++) { Carriage carriage = carriages.get(i); CarriageContraptionEntity entity = carriage.getDimensional(level).entity.get(); Carriage carriage2 = carriages.get(i + 1); CarriageContraptionEntity entity2 = carriage.getDimensional(level).entity.get(); if (entity == null || entity2 == null) continue; CarriageBogey bogey1 = carriage.trailingBogey(); CarriageBogey bogey2 = carriage2.leadingBogey(); Vec3 anchor = bogey1.couplingAnchors.getSecond(); Vec3 anchor2 = bogey2.couplingAnchors.getFirst(); if (anchor == null || anchor2 == null) continue; if (!anchor.closerThan(camera, 64)) continue; int lightCoords = getPackedLightCoords(entity, partialTicks); int lightCoords2 = getPackedLightCoords(entity2, partialTicks); double diffX = anchor2.x - anchor.x; double diffY = anchor2.y - anchor.y; double diffZ = anchor2.z - anchor.z; float yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90; float xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ))); Vec3 position = entity.getPosition(partialTicks); Vec3 position2 = entity2.getPosition(partialTicks); ms.pushPose(); { ms.pushPose(); ms.translate(anchor.x - camera.x, anchor.y - camera.y, anchor.z - camera.z); CachedBufferer.partial(AllBlockPartials.TRAIN_COUPLING_HEAD, air) .rotateY(-yRot) .rotateX(xRot) .light(lightCoords) .renderInto(ms, vb); float margin = 3 / 16f; double couplingDistance = train.carriageSpacing.get(i) - 2 * margin - bogey1.type.getConnectorAnchorOffset(bogey1.isUpsideDown()).z - bogey2.type.getConnectorAnchorOffset(bogey2.isUpsideDown()).z; int couplingSegments = (int) Math.round(couplingDistance * 4); double stretch = ((anchor2.distanceTo(anchor) - 2 * margin) * 4) / couplingSegments; for (int j = 0; j < couplingSegments; j++) { CachedBufferer.partial(AllBlockPartials.TRAIN_COUPLING_CABLE, air) .rotateY(-yRot + 180) .rotateX(-xRot) .translate(0, 0, margin + 2 / 16f) .scale(1, 1, (float) stretch) .translate(0, 0, j / 4f) .light(lightCoords) .renderInto(ms, vb); } ms.popPose(); } { ms.pushPose(); Vec3 translation = position2.subtract(position) .add(anchor2) .subtract(camera); ms.translate(translation.x, translation.y, translation.z); CachedBufferer.partial(AllBlockPartials.TRAIN_COUPLING_HEAD, air) .rotateY(-yRot + 180) .rotateX(-xRot) .light(lightCoords2) .renderInto(ms, vb); ms.popPose(); } ms.popPose(); } } } public static int getPackedLightCoords(Entity pEntity, float pPartialTicks) { BlockPos blockpos = new BlockPos(pEntity.getLightProbePosition(pPartialTicks)); return LightTexture.pack(getBlockLightLevel(pEntity, blockpos), getSkyLightLevel(pEntity, blockpos)); } protected static int getSkyLightLevel(Entity pEntity, BlockPos pPos) { return pEntity.level.getBrightness(LightLayer.SKY, pPos); } protected static int getBlockLightLevel(Entity pEntity, BlockPos pPos) { return pEntity.isOnFire() ? 15 : pEntity.level.getBrightness(LightLayer.BLOCK, pPos); } }