package com.tiedup.remake.client.gltf; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.model.PlayerModel; import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.RenderLayerParent; import net.minecraft.client.renderer.entity.layers.RenderLayer; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.joml.Matrix4f; /** * RenderLayer that renders the glTF mesh (handcuffs) on the player. * Only active when enabled and only renders on the local player. *

* Uses the live skinning path: reads live skeleton from HumanoidModel * via {@link GltfLiveBoneReader}, following PlayerAnimator + bendy-lib rotations. * Falls back to GLB-internal skinning via {@link GltfSkinningEngine} if live reading fails. */ @OnlyIn(Dist.CLIENT) public class GltfRenderLayer extends RenderLayer> { private static final Logger LOGGER = LogManager.getLogger("GltfPipeline"); private static final ResourceLocation CUFFS_MODEL = ResourceLocation.fromNamespaceAndPath( "tiedup", "models/gltf/v2/handcuffs/cuffs_prototype.glb" ); public GltfRenderLayer( RenderLayerParent< AbstractClientPlayer, PlayerModel > renderer ) { super(renderer); } /** * The Y translate offset to place the glTF mesh in the MC PoseStack. *

* After LivingEntityRenderer's scale(-1,-1,1) + translate(0,-1.501,0), * the PoseStack origin is at the model top (1.501 blocks above feet), Y-down. * The glTF mesh (MC-converted) has feet at Y=0 and head at Y≈-1.5. * Translating by 1.501 maps glTF feet to PoseStack feet and head to top. */ private static final float ALIGNMENT_Y = 1.501f; @Override public void render( PoseStack poseStack, MultiBufferSource buffer, int packedLight, AbstractClientPlayer entity, float limbSwing, float limbSwingAmount, float partialTick, float ageInTicks, float netHeadYaw, float headPitch ) { if (!GltfAnimationApplier.isEnabled()) return; if (entity != Minecraft.getInstance().player) return; GltfData data = GltfCache.get(CUFFS_MODEL); if (data == null) return; // Live path: read skeleton from HumanoidModel (after PlayerAnimator) PlayerModel parentModel = this.getParentModel(); Matrix4f[] joints = GltfLiveBoneReader.computeJointMatricesFromModel( parentModel, data, entity ); if (joints == null) { // Fallback to GLB-internal path if live reading fails joints = GltfSkinningEngine.computeJointMatrices(data); } poseStack.pushPose(); // Align glTF mesh with MC model (feet-to-feet alignment) poseStack.translate(0, ALIGNMENT_Y, 0); GltfMeshRenderer.renderSkinned( data, joints, poseStack, buffer, packedLight, net.minecraft.client.renderer.entity.LivingEntityRenderer.getOverlayCoords( entity, 0.0f ) ); poseStack.popPose(); } }