proper hat rendering

This commit is contained in:
2025-03-28 03:32:19 +01:00
parent b482670b0f
commit 9174c83a33
7 changed files with 142 additions and 22 deletions
@@ -0,0 +1,65 @@
package org.saturnclient.saturnclient.cosmetics;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.PlayerEntityModel;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.ModelTransformationMode;
import net.minecraft.util.Identifier;
public class CustomHeadFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
public CustomHeadFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context) {
super(context);
}
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
PlayerEntityRenderState state, float limbAngle, float limbDistance) {
ItemStack headItem = state.equippedHeadStack;
if (headItem.isEmpty()) {
matrices.push();
// Apply the head's rotations to align with the player's head orientation
this.getContextModel().head.rotate(matrices);
// Scale the item to fit properly
matrices.scale(0.5f, .5f, 0.5f);
// Position the item properly
matrices.translate(0.0f, -0.8f, 0.0f);
// Rotate the item 180 degrees around the X-axis to fix the upside-down issue
matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_X.rotationDegrees(180.0f));
// Create an ItemStack for the custom head item
ItemStack customHat = new ItemStack(Items.STICK);
customHat.set(DataComponentTypes.ITEM_MODEL, Identifier.of("saturnclient:hat"));
// Get the ItemRenderer instance
ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
// Render the item as headwear
itemRenderer.renderItem(
customHat,
ModelTransformationMode.HEAD,
light,
OverlayTexture.DEFAULT_UV,
matrices,
vertexConsumers,
null, // World can be null for entity rendering
0 // Seed for randomness, not needed here
);
// Pop the matrix stack to restore previous transformations
matrices.pop();
}
}
}
@@ -1,21 +0,0 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
import net.minecraft.client.render.entity.state.BipedEntityRenderState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ArmorFeatureRenderer.class)
public class ArmorFeatureRendererMixin {
@Redirect(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/entity/state/BipedEntityRenderState;equippedHeadStack:Lnet/minecraft/item/ItemStack;", opcode = org.objectweb.asm.Opcodes.GETFIELD))
private ItemStack redirectEquippedHeadStack(BipedEntityRenderState state) {
ItemStack original = state.equippedHeadStack;
if (original.isEmpty()) {
return new ItemStack(Items.DIAMOND_HELMET);
}
return original;
}
}
@@ -0,0 +1,28 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.PlayerEntityRenderer;
import net.minecraft.client.render.entity.model.PlayerEntityModel;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.entity.player.PlayerEntity;
import org.saturnclient.saturnclient.cosmetics.CustomHeadFeatureRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerEntityRenderer.class)
public abstract class PlayerEntityRendererMixin
extends LivingEntityRenderer<PlayerEntity, PlayerEntityRenderState, PlayerEntityModel> {
protected PlayerEntityRendererMixin() {
super(null, null, 0.0f);
}
@Inject(method = "<init>", at = @At("TAIL"))
private void addCustomFeatureRenderer(EntityRendererFactory.Context context, boolean slim, CallbackInfo ci) {
this.addFeature(new CustomHeadFeatureRenderer(this));
}
}
@@ -0,0 +1,6 @@
{
"model": {
"type": "minecraft:model",
"model": "saturnclient:item/hat"
}
}
@@ -0,0 +1,42 @@
{
"credit": "Made with Blockbench",
"texture_size": [32, 32],
"textures": {
"0": "saturnclient:item/hat",
"particle": "saturnclient:item/hat"
},
"elements": [
{
"from": [3, 0, 3],
"to": [13, 1, 13],
"rotation": {"angle": 0, "axis": "x", "origin": [4, 0, 4]},
"faces": {
"north": {"uv": [8, 0, 16, 1], "texture": "#0"},
"east": {"uv": [8, 1, 16, 2], "texture": "#0"},
"south": {"uv": [8, 2, 16, 3], "texture": "#0"},
"west": {"uv": [8, 3, 16, 4], "texture": "#0"},
"up": {"uv": [8, 8, 0, 0], "texture": "#0"},
"down": {"uv": [8, 8, 0, 16], "texture": "#0"}
}
},
{
"from": [4, 1, 4],
"to": [12, 8, 12],
"rotation": {"angle": 0, "axis": "x", "origin": [4, 1, 4]},
"faces": {
"north": {"uv": [8, 0, 16, 1], "texture": "#0"},
"east": {"uv": [8, 1, 16, 2], "texture": "#0"},
"south": {"uv": [8, 2, 16, 3], "texture": "#0"},
"west": {"uv": [8, 3, 16, 4], "texture": "#0"},
"up": {"uv": [8, 8, 0, 0], "texture": "#0"},
"down": {"uv": [8, 8, 0, 16], "texture": "#0"}
}
}
],
"display": {
"head": {
"translation": [0, 18.25, 0],
"scale": [1.5, 1.5, 1.5]
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

+1 -1
View File
@@ -7,7 +7,7 @@
"CapeRenderer", "CapeRenderer",
"TabListMixin", "TabListMixin",
"NameTagMixin", "NameTagMixin",
"ArmorFeatureRendererMixin" "PlayerEntityRendererMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1