package com.tiedup.remake.client; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.model.PlayerModel; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.player.PlayerRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.HumanoidArm; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.RenderArmEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; /** * Renders mittens on the player's arms in first-person view. * * Uses RenderArmEvent which fires specifically when a player's arm * is being rendered in first person. This is more targeted than RenderHandEvent. * * @see RenderArmEvent Documentation */ @OnlyIn(Dist.CLIENT) @Mod.EventBusSubscriber( modid = TiedUpMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT ) public class FirstPersonMittensRenderer { private static final ResourceLocation MITTENS_TEXTURE = ResourceLocation.fromNamespaceAndPath( TiedUpMod.MOD_ID, "textures/models/bondage/mittens/mittens.png" ); /** * Render mittens overlay on the player's arm in first-person view. * * This event fires after the arm is set up for rendering but we can add * our own rendering on top of it. */ @SubscribeEvent public static void onRenderArm(RenderArmEvent event) { AbstractClientPlayer player = event.getPlayer(); // Get player's bind state PlayerBindState state = PlayerBindState.getInstance(player); if (state == null) return; // If tied up, arms are hidden by FirstPersonHandHideHandler - don't render mittens if (state.isTiedUp()) return; // Check if player has mittens if (!state.hasMittens()) return; // Hide mittens when player is in a wrap or latex sack (hands are covered) if (isBindHidingMittens(player)) return; // Render mittens on this arm renderMittensOnArm(event); } /** * Render the mittens overlay on the arm. */ private static void renderMittensOnArm(RenderArmEvent event) { PoseStack poseStack = event.getPoseStack(); MultiBufferSource buffer = event.getMultiBufferSource(); int packedLight = event.getPackedLight(); AbstractClientPlayer player = event.getPlayer(); HumanoidArm arm = event.getArm(); // Get the player's model to access the arm ModelPart Minecraft mc = Minecraft.getInstance(); var renderer = mc.getEntityRenderDispatcher().getRenderer(player); if (!(renderer instanceof PlayerRenderer playerRenderer)) return; PlayerModel playerModel = playerRenderer.getModel(); poseStack.pushPose(); // Get the appropriate arm from the player model ModelPart armPart = (arm == HumanoidArm.RIGHT) ? playerModel.rightArm : playerModel.leftArm; ModelPart sleevePart = (arm == HumanoidArm.RIGHT) ? playerModel.rightSleeve : playerModel.leftSleeve; // The arm is already positioned by the game's first-person renderer // We just need to render our mittens texture on top // Use a slightly scaled version to appear on top (avoid z-fighting) poseStack.scale(1.001F, 1.001F, 1.001F); // Render the arm with mittens texture VertexConsumer vertexConsumer = buffer.getBuffer( RenderType.entitySolid(MITTENS_TEXTURE) ); // Render the arm part with mittens texture armPart.render( poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY ); // Also render the sleeve part if visible if (sleevePart.visible) { sleevePart.render( poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY ); } poseStack.popPose(); } /** * Check if the player's current bind variant hides mittens. * WRAP and LATEX_SACK cover the entire body including hands. */ private static boolean isBindHidingMittens(AbstractClientPlayer player) { net.minecraft.world.item.ItemStack bindStack = V2EquipmentHelper.getInRegion(player, BodyRegionV2.ARMS); if (bindStack.isEmpty()) return false; PoseType poseType = PoseTypeHelper.getPoseType(bindStack); return poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK; } }