Working custom renderer for cloaks

This commit is contained in:
2025-09-03 17:50:57 +02:00
parent db27de497a
commit 40b0177cf2
3 changed files with 164 additions and 14 deletions
@@ -12,10 +12,6 @@ import net.minecraft.client.render.entity.equipment.EquipmentModelLoader;
import net.minecraft.client.render.entity.equipment.EquipmentModel.LayerType;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModelLayers;
import net.minecraft.client.render.entity.model.LoadedEntityModels;
import net.minecraft.client.render.entity.model.PlayerCapeModel;
import net.minecraft.client.render.entity.model.PlayerEntityModel;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.client.util.SkinTextures;
@@ -28,14 +24,11 @@ import net.minecraft.registry.RegistryKey;
import net.minecraft.util.Identifier;
public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState, PlayerEntityModel> {
private final BipedEntityModel<PlayerEntityRenderState> model;
private final EquipmentModelLoader equipmentModelLoader;
public CloakFeatureRenderer(FeatureRendererContext<PlayerEntityRenderState, PlayerEntityModel> context,
LoadedEntityModels modelLoader, EquipmentModelLoader equipmentModelLoader) {
EquipmentModelLoader equipmentModelLoader) {
super(context);
this.model = new PlayerCapeModel<PlayerEntityRenderState>(
modelLoader.getModelPart(EntityModelLayers.PLAYER_CAPE));
this.equipmentModelLoader = equipmentModelLoader;
}
@@ -50,6 +43,159 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
}
}
// Render quad with correct winding order (counter-clockwise for front face)
private void renderCapeQuad(VertexConsumer vertexConsumer, MatrixStack matrixStack,
float x1, float y1, float z1, float x2, float y2, float z2,
float u1, float v1, float u2, float v2, int light, int overlay,
float normalX, float normalY, float normalZ, boolean flipWinding) {
MatrixStack.Entry entry = matrixStack.peek();
if (!flipWinding) {
// Counter-clockwise winding (front face)
// Vertex 1 (bottom-left)
vertexConsumer.vertex(entry.getPositionMatrix(), x1, y2, z1)
.color(255, 255, 255, 255)
.texture(u1, v2)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
// Vertex 2 (bottom-right)
vertexConsumer.vertex(entry.getPositionMatrix(), x2, y2, z2)
.color(255, 255, 255, 255)
.texture(u2, v2)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
// Vertex 3 (top-right)
vertexConsumer.vertex(entry.getPositionMatrix(), x2, y1, z2)
.color(255, 255, 255, 255)
.texture(u2, v1)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
// Vertex 4 (top-left)
vertexConsumer.vertex(entry.getPositionMatrix(), x1, y1, z1)
.color(255, 255, 255, 255)
.texture(u1, v1)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
} else {
// Clockwise winding (back face) - reverse vertex order
// Vertex 4 (top-left)
vertexConsumer.vertex(entry.getPositionMatrix(), x1, y1, z1)
.color(255, 255, 255, 255)
.texture(u1, v1)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
// Vertex 3 (top-right)
vertexConsumer.vertex(entry.getPositionMatrix(), x2, y1, z2)
.color(255, 255, 255, 255)
.texture(u2, v1)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
// Vertex 2 (bottom-right)
vertexConsumer.vertex(entry.getPositionMatrix(), x2, y2, z2)
.color(255, 255, 255, 255)
.texture(u2, v2)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
// Vertex 1 (bottom-left)
vertexConsumer.vertex(entry.getPositionMatrix(), x1, y2, z1)
.color(255, 255, 255, 255)
.texture(u1, v2)
.overlay(overlay)
.light(light)
.normal(normalX, normalY, normalZ);
}
}
private void renderCape(MatrixStack matrixStack, VertexConsumer vertexConsumer,
PlayerEntityRenderState playerState, int light, int overlay) {
// Cape dimensions (similar to vanilla cape model)
float capeWidth = 10.0f / 16.0f; // 10 pixels wide
float capeHeight = 16.0f / 16.0f; // 16 pixels tall
float capeDepth = 1.0f / 16.0f; // 1 pixel deep
// Position the cape behind the player
matrixStack.push();
// Apply player body rotation
matrixStack.translate(0.0f, 0.0f, 0.125f); // Move cape slightly behind player
// Render the main cape body
float x1 = -capeWidth / 2.0f;
float x2 = capeWidth / 2.0f;
float y1 = 0.0f;
float y2 = -capeHeight;
float z1 = 0.0f;
float z2 = capeDepth;
// UV coordinates for cape texture (assuming 64x32 texture)
// Front face UV (outside of cape)
float frontU1 = 1.0f / 64.0f; // Start at pixel 1
float frontU2 = 11.0f / 64.0f; // End at pixel 11 (10 pixels wide)
float frontV1 = 1.0f / 32.0f; // Start at pixel 1
float frontV2 = 17.0f / 32.0f; // End at pixel 17 (16 pixels tall)
// Back face UV (inside of cape) - typically offset in cape texture
float backU1 = 12.0f / 64.0f; // Back texture starts after front
float backU2 = 22.0f / 64.0f; // 10 pixels wide
float backV1 = 1.0f / 32.0f;
float backV2 = 17.0f / 32.0f;
// Bottom face UV (lower edge of cape)
float bottomU1 = 1.0f / 64.0f; // Start at pixel 1
float bottomU2 = 11.0f / 64.0f; // End at pixel 11
float bottomV1 = 0.0f / 32.0f; // Start at pixel 0
float bottomV2 = 1.0f / 32.0f; // End at pixel 1
// Top face UV (edge along shoulders)
float topU1 = 1.0f / 64.0f; // Start at pixel 1
float topU2 = 11.0f / 64.0f; // End at pixel 11
float topV1 = 17.0f / 32.0f; // Start at pixel 17
float topV2 = 18.0f / 32.0f; // End at pixel 18
// FRONT FACE
renderCapeQuad(vertexConsumer, matrixStack,
x2, y2, z2, x1, y1, z2,
frontU1, frontV1, frontU2, frontV2,
light, overlay,
0.0f, 0.0f, 1.0f, false);
// BACK FACE
renderCapeQuad(vertexConsumer, matrixStack,
x1, y2, z1, x2, y1, z1,
backU1, backV1, backU2, backV2,
light, overlay,
0.0f, 0.0f, 1.0f, false);
// LEFT SIDE
renderCapeQuad(vertexConsumer, matrixStack,
x2, y2, z1, x2, y1, z2,
0.0f, frontV1, 1.0f / 64.0f, frontV2,
light, overlay,
1.0f, 0.0f, 0.0f, false);
// RIGHT SIDE
renderCapeQuad(vertexConsumer, matrixStack,
x1, y2, z2, x1, y1, z1,
frontU2, frontV1, frontU2 + 1.0f / 64.0f, frontV2,
light, overlay,
-1.0f, 0.0f, 0.0f, false);
matrixStack.pop();
}
public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light,
PlayerEntityRenderState playerEntityRenderState, float f, float g) {
if (!playerEntityRenderState.invisible && playerEntityRenderState.capeVisible) {
@@ -67,12 +213,15 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
if (customCape != null
&& !this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack, LayerType.WINGS)) {
matrixStack.push();
// Position cape at player's shoulders
matrixStack.translate(0.0f, -0.25f, 0.0f);
if (this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack, LayerType.HUMANOID)) {
matrixStack.translate(0.0F, -0.053125F, 0.06875F);
}
int minBrightness = 7;
int blockLight = (light >> 4) & 0xF;
int skyLight = (light >> 20) & 0xF;
@@ -85,12 +234,13 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
VertexConsumer vertexConsumer = vertexConsumerProvider
.getBuffer(RenderLayer.getEntitySolid(customCape));
((PlayerEntityModel) this.getContextModel()).copyTransforms(this.model);
this.model.setAngles(playerEntityRenderState);
this.model.render(matrixStack, vertexConsumer, light, OverlayTexture.DEFAULT_UV);
// Render the cape manually
renderCape(matrixStack, vertexConsumer, playerEntityRenderState, light, OverlayTexture.DEFAULT_UV);
matrixStack.pop();
}
}
}
}
}
}
@@ -25,6 +25,6 @@ public abstract class PlayerEntityRendererMixin
@Inject(method = "<init>", at = @At("TAIL"))
private void addCustomFeatureRenderer(EntityRendererFactory.Context ctx, boolean slim, CallbackInfo ci) {
this.addFeature(new HatFeatureRenderer(this));
this.addFeature(new CloakFeatureRenderer(this, ctx.getEntityModels(), ctx.getEquipmentModelLoader()));
this.addFeature(new CloakFeatureRenderer(this, ctx.getEquipmentModelLoader()));
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 148 KiB