From 00cefe14f71effe085d6281287a2bb214ff10fda Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 9 Apr 2026 22:03:28 +0200 Subject: [PATCH] Refactoring cloak physics --- .../impl/cosmetics/CloakFeatureRenderer.java | 221 +++++++++++++----- 1 file changed, 161 insertions(+), 60 deletions(-) diff --git a/versions/1.21.11/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.11/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index ef33340..ca6bb92 100644 --- a/versions/1.21.11/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.11/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -29,6 +29,7 @@ import net.minecraft.component.type.EquippableComponent; import net.minecraft.item.ItemStack; import net.minecraft.item.equipment.EquipmentAsset; import net.minecraft.registry.RegistryKey; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.Vec3d; @@ -43,6 +44,8 @@ public class CloakFeatureRenderer extends FeatureRenderer 0) ? (2.0f - segmentValues[i - 1]) * ((float) Math.PI / 2f) : angle; - float startAvgAngle = (angle + prevAngle) / 2.0f; - float thickYStart = (float) Math.sin(startAvgAngle) * capeDepth; - float thickZStart = (float) Math.cos(startAvgAngle) * capeDepth; + spineY[i + 1] = spineY[i] + dirY * partHeight; + spineZ[i + 1] = spineZ[i] + dirZ * partHeight; + } - // 2. Thickness at the END of this segment (Must match NEXT segment's start) - float nextAngle = (i < PARTS - 1) ? (2.0f - segmentValues[i + 1]) * ((float) Math.PI / 2f) : angle; - float endAvgAngle = (angle + nextAngle) / 2.0f; - float thickYEnd = (float) Math.sin(endAvgAngle) * capeDepth; - float thickZEnd = (float) Math.cos(endAvgAngle) * capeDepth; + // ============================= + // 2. Grid + // ============================= - float nextY = curY + dirY * capePartHeight; - float nextZ = curZ + dirZ * capePartHeight; + Vec3d[][] inner = new Vec3d[H_PARTS + 1][PARTS + 1]; + Vec3d[][] outer = new Vec3d[H_PARTS + 1][PARTS + 1]; - // Inner Vertices (The spine of the cape) - Vec3d innerTopLeft = new Vec3d(x2, curY, curZ); - Vec3d innerTopRight = new Vec3d(x1, curY, curZ); - Vec3d innerBotLeft = new Vec3d(x2, nextY, nextZ); - Vec3d innerBotRight = new Vec3d(x1, nextY, nextZ); + for (int x = 0; x <= H_PARTS; x++) { - // Outer Vertices (Offset by the specific joint thickness) - Vec3d outerTopLeft = new Vec3d(x2, curY - thickYStart, curZ - thickZStart); - Vec3d outerTopRight = new Vec3d(x1, curY - thickYStart, curZ - thickZStart); - Vec3d outerBotLeft = new Vec3d(x2, nextY - thickYEnd, nextZ - thickZEnd); - Vec3d outerBotRight = new Vec3d(x1, nextY - thickYEnd, nextZ - thickZEnd); + float t = (float) x / H_PARTS; + float xPos = -capeWidth / 2.0f + capeWidth * t; - // FRONT (facing camera) - this.renderCapeQuad(vertexConsumer, entry, - outerBotRight, outerBotLeft, outerTopLeft, outerTopRight, - frontU2, frontV1 + (frontPartV * i), frontU1, frontV1 + (frontPartV * (i + 1)), - light, overlay, 0, 0, 1, false); + float centered = t - 0.5f; + float curveFactor = centered * 2.0f; + float falloff = Math.abs(curveFactor); + float zCurve = curveFactor * falloff * horizontalCurve * 0.3f; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, entry, - innerBotLeft, innerBotRight, innerTopRight, innerTopLeft, - backU2, backV1 + (backPartV * i), backU1, backV1 + (backPartV * (i + 1)), - light, overlay, 0, 0, -1, false); + for (int i = 0; i <= PARTS; i++) { - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, entry, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), + float y = spineY[i]; + float z = spineZ[i]; + + int idx = Math.min(i, PARTS - 1); + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); + + float thickY = (float) Math.sin(angle) * capeDepth; + float thickZ = (float) Math.cos(angle) * capeDepth; + + inner[x][i] = new Vec3d(xPos, y, z + zCurve); + outer[x][i] = new Vec3d(xPos, y - thickY, z - thickZ + zCurve); + } + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * ((float) x / H_PARTS); + float uEnd = frontU1 + (frontU2 - frontU1) * ((float) (x + 1) / H_PARTS); + + float buStart = backU1 + (backU2 - backU1) * ((float) x / H_PARTS); + float buEnd = backU1 + (backU2 - backU1) * ((float) (x + 1) / H_PARTS); + + for (int i = 0; i < PARTS; i++) { + + float vStart = frontV1 + frontPartV * i; + float vEnd = frontV1 + frontPartV * (i + 1); + + float bvStart = backV1 + backPartV * i; + float bvEnd = backV1 + backPartV * (i + 1); + + Vec3d innerTL = inner[x + 1][i]; + Vec3d innerTR = inner[x][i]; + Vec3d innerBL = inner[x + 1][i + 1]; + Vec3d innerBR = inner[x][i + 1]; + + Vec3d outerTL = outer[x + 1][i]; + Vec3d outerTR = outer[x][i]; + Vec3d outerBL = outer[x + 1][i + 1]; + Vec3d outerBR = outer[x][i + 1]; + + // FRONT + renderCapeQuad(vertexConsumer, entry, + outerBR, outerBL, outerTL, outerTR, + uEnd, vStart, uStart, vEnd, + light, overlay, 0, 0, 1, false); + + // BACK + renderCapeQuad(vertexConsumer, entry, + innerBL, innerBR, innerTR, innerTL, + buEnd, bvStart, buStart, bvEnd, + light, overlay, 0, 0, -1, false); + } + } + + // ============================= + // 4. LEFT SIDE (x = 0) + // ============================= + + for (int i = 0; i < PARTS; i++) { + + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + renderCapeQuad(vertexConsumer, entry, + innerBot, outerBot, outerTop, innerTop, + leftU1, leftV1 + leftPartV * i, + leftU2, leftV1 + leftPartV * (i + 1), light, overlay, 1, 0, 0, false); + } - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, entry, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + // ============================= + // 5. RIGHT SIDE (x = H_PARTS) + // ============================= + + for (int i = 0; i < PARTS; i++) { + + Vec3d innerTop = inner[H_PARTS][i]; + Vec3d innerBot = inner[H_PARTS][i + 1]; + Vec3d outerTop = outer[H_PARTS][i]; + Vec3d outerBot = outer[H_PARTS][i + 1]; + + renderCapeQuad(vertexConsumer, entry, + outerBot, innerBot, innerTop, outerTop, + rightU1, rightV1 + rightPartV * i, + rightU2, rightV1 + rightPartV * (i + 1), light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, entry, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 6. TOP (i = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, entry, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int x = 0; x < H_PARTS; x++) { - curY = nextY; - curZ = nextZ; + Vec3d outerL = outer[x][0]; + Vec3d outerR = outer[x + 1][0]; + Vec3d innerR = inner[x + 1][0]; + Vec3d innerL = inner[x][0]; + + float uStart = topU1 + (topU2 - topU1) * ((float) x / H_PARTS); + float uEnd = topU1 + (topU2 - topU1) * ((float) (x + 1) / H_PARTS); + + renderCapeQuad(vertexConsumer, entry, + outerL, outerR, innerR, innerL, + uStart, topV1, uEnd, topV2, + light, overlay, 0, 1, 0, false); + } + + // ============================= + // 7. BOTTOM (i = PARTS) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + Vec3d innerL = inner[x][PARTS]; + Vec3d innerR = inner[x + 1][PARTS]; + Vec3d outerR = outer[x + 1][PARTS]; + Vec3d outerL = outer[x][PARTS]; + + float uStart = bottomU1 + (bottomU2 - bottomU1) * ((float) x / H_PARTS); + float uEnd = bottomU1 + (bottomU2 - bottomU1) * ((float) (x + 1) / H_PARTS); + + renderCapeQuad(vertexConsumer, entry, + innerL, innerR, outerR, outerL, + uStart, bottomV1, uEnd, bottomV2, + light, overlay, 0, -1, 0, false); } } @@ -336,6 +435,8 @@ public class CloakFeatureRenderer extends FeatureRenderer { renderCape(entry, vertexConsumer, state, l, OverlayTexture.DEFAULT_UV); });