From 00cefe14f71effe085d6281287a2bb214ff10fda Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 9 Apr 2026 22:03:28 +0200 Subject: [PATCH 01/12] 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); }); From 76b842e6a092d849f1a49b47b8232a9266319f42 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Thu, 9 Apr 2026 22:31:38 +0200 Subject: [PATCH 02/12] Refactored cloak physics --- .../impl/cosmetics/CloakFeatureRenderer.java | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 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 ca6bb92..8b73ec4 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 @@ -225,7 +225,7 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Thu, 9 Apr 2026 23:06:19 +0200 Subject: [PATCH 03/12] Improved physics --- .../impl/cosmetics/CloakFeatureRenderer.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 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 8b73ec4..6cd1bb6 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 @@ -223,24 +223,34 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, state.field_53537 / 108.0f); + float velX = Math.min(0.8f, state.field_53537 / 108.0f); float rawVelY = state.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; From 6466d8d8fd1d18e62d0f30ee8e37e70e2d09a8cc Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Fri, 10 Apr 2026 04:00:58 +0200 Subject: [PATCH 04/12] Improved cloak physics --- .../impl/cosmetics/CloakFeatureRenderer.java | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 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 6cd1bb6..b13e942 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 @@ -262,8 +262,8 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Fri, 10 Apr 2026 04:02:08 +0200 Subject: [PATCH 05/12] Improved cloak physics 1.21.10 --- .../impl/cosmetics/CloakFeatureRenderer.java | 391 ++++++++++-------- .../impl/cosmetics/CloakFeatureRenderer.java | 1 + 2 files changed, 227 insertions(+), 165 deletions(-) diff --git a/versions/1.21.10/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.10/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index f2e74c8..b13e942 100644 --- a/versions/1.21.10/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.10/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,15 +2,19 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; import org.saturnclient.cosmetics.Cloaks; import org.saturnclient.impl.cosmetics.utils.ShaderUtils; +import org.saturnclient.saturnclient.SaturnRenderState; import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.equipment.EquipmentModel; import net.minecraft.client.render.entity.equipment.EquipmentModelLoader; @@ -25,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; @@ -39,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -271,78 +393,15 @@ public class CloakFeatureRenderer extends FeatureRenderer> 4) & 0xF; - int skyLight = (light >> 20) & 0xF; - blockLight = Math.max(blockLight, minBrightness); - skyLight = Math.max(skyLight, minBrightness); - light = (skyLight << 20) | (blockLight << 4); - - VertexConsumer vertexConsumer = vertexConsumerProvider - .getBuffer(ShaderUtils.getRenderLayer(customCape)); - - matrixStack.push(); - - matrixStack.translate(0.0f, 0.0f, 0.12f); - - if (this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack, LayerType.HUMANOID)) { - matrixStack.translate(0.0F, -0.053125F, 0.06875F); - } - - if (playerEntityRenderState.isInSneakingPose) { - matrixStack.translate(0, 0.16f, 0.0f); - matrixStack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(29.0f)); - } - - long now = System.currentTimeMillis(); - if (now - lastUpdate >= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); - float rawVelY = playerEntityRenderState.field_53536; - float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; - - float value = Math.max(0.0f, Math.min(2.0f, playerEntityRenderState.isSwimming ? 0.0f : velX + velY)); - - if (Config.cloakPhysics.value) { - updateSegmentValues(segmentValues[0] + (value - segmentValues[0]) * 0.3f); - } else { - Arrays.fill(segmentValues, segmentValues[0] + (value - segmentValues[0]) * 0.05f); - } - lastUpdate = now; - } - - renderCape(matrixStack, vertexConsumer, playerEntityRenderState, light, OverlayTexture.DEFAULT_UV); - - matrixStack.pop(); - } - @Override public void render(MatrixStack matrices, OrderedRenderCommandQueue queue, int light, PlayerEntityRenderState state, float limbAngle, float limbDistance) { - if (state.displayName == null || state.invisible || !state.capeVisible + if (state.invisible || !state.capeVisible || state.skinTextures.cape() != null) { return; } - SaturnPlayer player = SaturnPlayer.get(state.displayName.getString()); + SaturnPlayer player = state.getData(SaturnRenderState.saturnDataKey); if (player == null) { return; @@ -376,7 +435,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, state.field_53537 / 108.0f); + float velX = Math.min(0.8f, state.field_53537 / 108.0f); float rawVelY = state.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; @@ -392,8 +451,10 @@ public class CloakFeatureRenderer extends FeatureRenderer { - renderCape(matrices, vertexConsumer, state, l, OverlayTexture.DEFAULT_UV); + renderCape(entry, vertexConsumer, state, l, OverlayTexture.DEFAULT_UV); }); matrices.pop(); 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 b13e942..8b3f462 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 @@ -417,6 +417,7 @@ public class CloakFeatureRenderer extends FeatureRenderer> 4) & 0xF; int skyLight = (light >> 20) & 0xF; blockLight = Math.max(blockLight, minBrightness); + skyLight = Math.max(skyLight, minBrightness); light = (skyLight << 20) | (blockLight << 4); From c2d20492c30e29d176beadb92516dc0d5eba06a8 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Fri, 10 Apr 2026 04:02:28 +0200 Subject: [PATCH 06/12] Improved cloak physics 1.21.9 --- .../impl/cosmetics/CloakFeatureRenderer.java | 391 ++++++++++-------- 1 file changed, 226 insertions(+), 165 deletions(-) diff --git a/versions/1.21.9/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.9/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index f2e74c8..b13e942 100644 --- a/versions/1.21.9/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.9/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,15 +2,19 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; import org.saturnclient.cosmetics.Cloaks; import org.saturnclient.impl.cosmetics.utils.ShaderUtils; +import org.saturnclient.saturnclient.SaturnRenderState; import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.command.OrderedRenderCommandQueue; import net.minecraft.client.render.entity.equipment.EquipmentModel; import net.minecraft.client.render.entity.equipment.EquipmentModelLoader; @@ -25,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; @@ -39,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -271,78 +393,15 @@ public class CloakFeatureRenderer extends FeatureRenderer> 4) & 0xF; - int skyLight = (light >> 20) & 0xF; - blockLight = Math.max(blockLight, minBrightness); - skyLight = Math.max(skyLight, minBrightness); - light = (skyLight << 20) | (blockLight << 4); - - VertexConsumer vertexConsumer = vertexConsumerProvider - .getBuffer(ShaderUtils.getRenderLayer(customCape)); - - matrixStack.push(); - - matrixStack.translate(0.0f, 0.0f, 0.12f); - - if (this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack, LayerType.HUMANOID)) { - matrixStack.translate(0.0F, -0.053125F, 0.06875F); - } - - if (playerEntityRenderState.isInSneakingPose) { - matrixStack.translate(0, 0.16f, 0.0f); - matrixStack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(29.0f)); - } - - long now = System.currentTimeMillis(); - if (now - lastUpdate >= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); - float rawVelY = playerEntityRenderState.field_53536; - float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; - - float value = Math.max(0.0f, Math.min(2.0f, playerEntityRenderState.isSwimming ? 0.0f : velX + velY)); - - if (Config.cloakPhysics.value) { - updateSegmentValues(segmentValues[0] + (value - segmentValues[0]) * 0.3f); - } else { - Arrays.fill(segmentValues, segmentValues[0] + (value - segmentValues[0]) * 0.05f); - } - lastUpdate = now; - } - - renderCape(matrixStack, vertexConsumer, playerEntityRenderState, light, OverlayTexture.DEFAULT_UV); - - matrixStack.pop(); - } - @Override public void render(MatrixStack matrices, OrderedRenderCommandQueue queue, int light, PlayerEntityRenderState state, float limbAngle, float limbDistance) { - if (state.displayName == null || state.invisible || !state.capeVisible + if (state.invisible || !state.capeVisible || state.skinTextures.cape() != null) { return; } - SaturnPlayer player = SaturnPlayer.get(state.displayName.getString()); + SaturnPlayer player = state.getData(SaturnRenderState.saturnDataKey); if (player == null) { return; @@ -376,7 +435,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, state.field_53537 / 108.0f); + float velX = Math.min(0.8f, state.field_53537 / 108.0f); float rawVelY = state.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; @@ -392,8 +451,10 @@ public class CloakFeatureRenderer extends FeatureRenderer { - renderCape(matrices, vertexConsumer, state, l, OverlayTexture.DEFAULT_UV); + renderCape(entry, vertexConsumer, state, l, OverlayTexture.DEFAULT_UV); }); matrices.pop(); From 8f8fec08160819b4a2177bd4c811dbfe395ceda9 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Fri, 10 Apr 2026 04:04:36 +0200 Subject: [PATCH 07/12] Improved cloak physics 1.21.8 --- .../impl/cosmetics/CloakFeatureRenderer.java | 320 ++++++++++++------ 1 file changed, 222 insertions(+), 98 deletions(-) diff --git a/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index 54ce803..e549057 100644 --- a/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,6 +2,10 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; @@ -24,6 +28,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; @@ -38,6 +43,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -328,7 +450,9 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Fri, 10 Apr 2026 04:06:02 +0200 Subject: [PATCH 08/12] Improved cloak physics 1.21.7 --- .../impl/cosmetics/CloakFeatureRenderer.java | 320 ++++++++++++------ 1 file changed, 222 insertions(+), 98 deletions(-) diff --git a/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index 54ce803..e549057 100644 --- a/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,6 +2,10 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; @@ -24,6 +28,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; @@ -38,6 +43,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -328,7 +450,9 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Fri, 10 Apr 2026 04:06:33 +0200 Subject: [PATCH 09/12] Improved cloak physics 1.21.6 --- .../impl/cosmetics/CloakFeatureRenderer.java | 320 ++++++++++++------ 1 file changed, 222 insertions(+), 98 deletions(-) diff --git a/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index 54ce803..e549057 100644 --- a/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,6 +2,10 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; @@ -24,6 +28,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; @@ -38,6 +43,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -328,7 +450,9 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Fri, 10 Apr 2026 04:07:12 +0200 Subject: [PATCH 10/12] Improved cloak physics 1.21.5 --- .../impl/cosmetics/CloakFeatureRenderer.java | 320 ++++++++++++------ 1 file changed, 222 insertions(+), 98 deletions(-) diff --git a/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index 54ce803..e549057 100644 --- a/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,6 +2,10 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; @@ -24,6 +28,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; @@ -38,6 +43,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -328,7 +450,9 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Fri, 10 Apr 2026 04:07:26 +0200 Subject: [PATCH 11/12] Improved cloak physics 1.21.4 --- .../impl/cosmetics/CloakFeatureRenderer.java | 322 ++++++++++++------ 1 file changed, 223 insertions(+), 99 deletions(-) diff --git a/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index e9fb196..e549057 100644 --- a/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -2,6 +2,10 @@ package org.saturnclient.impl.cosmetics; import java.util.Arrays; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; import org.saturnclient.client.player.SaturnPlayer; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Config; @@ -24,6 +28,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; @@ -38,6 +43,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, matrixStack, - 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 curve = (centered * centered) * 0.3f; + float zCurve = (curve * horizontalCurve) / PARTS; - // BACK (Facing player) - this.renderCapeQuad(vertexConsumer, matrixStack, - 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++) { + float y = spineY[i]; + float z = spineZ[i]; - // LEFT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, outerBotLeft, outerTopLeft, innerTopLeft, - leftU1, leftV1 + (leftPartV * i), leftU2, leftV1 + (leftPartV * (i + 1)), - light, overlay, 1, 0, 0, false); + int idx = Math.min(i, PARTS - 1); + // angle: 0 is horizontal (y-flat), PI/2 is vertical (z-flat) + float angle = (2.0f - segmentValues[idx]) * ((float) Math.PI / 2f); - // RIGHT EDGE - this.renderCapeQuad(vertexConsumer, matrixStack, - outerBotRight, innerBotRight, innerTopRight, outerTopRight, - rightU1, rightV1 + (rightPartV * i), rightU2, rightV1 + (rightPartV * (i + 1)), + float cosA = (float) Math.cos(angle); + float sinA = (float) Math.sin(angle); + + // Calculate how much the horizontal curve shifts the vertex + float currentCurve = zCurve * i; + + // Rotate the curve offset so it aligns with the cape's tilt + float curveY = sinA * currentCurve; + float curveZ = cosA * currentCurve; + + float thickY = sinA * capeDepth; + float thickZ = cosA * capeDepth; + + inner[x][i] = new Vec3d(xPos, y - curveY, z - curveZ); + outer[x][i] = new Vec3d(xPos, (y - curveY) - thickY, (z - curveZ) - thickZ); + } + + } + + // ============================= + // 3. Front + Back + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + float uStart = frontU1 + (frontU2 - frontU1) * (1.0f - ((float) x / H_PARTS)); + float uEnd = frontU1 + (frontU2 - frontU1) * (1.0f - ((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, + uStart, vStart, uEnd, 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 = 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]; + + float vStart = leftV1 + leftPartV * i; + float vEnd = leftV1 + leftPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + innerTop, outerTop, outerBot, innerBot, + leftU1, vEnd, + leftU2, vStart, light, overlay, -1, 0, 0, false); + } - // TOP FACE (Only on first segment) - if (i == 0) { - this.renderCapeQuad(vertexConsumer, matrixStack, - outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, - topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); - } + // ============================= + // 5. RIGHT SIDE (x = 0) + // ============================= - // BOTTOM FACE (Only on last segment) - if (i == PARTS - 1) { - this.renderCapeQuad(vertexConsumer, matrixStack, - innerBotLeft, innerBotRight, outerBotRight, outerBotLeft, - bottomU1, bottomV1, bottomU2, bottomV2, light, overlay, 0, -1, 0, false); - } + for (int i = 0; i < PARTS; i++) { - curY = nextY; - curZ = nextZ; + Vec3d innerTop = inner[0][i]; + Vec3d innerBot = inner[0][i + 1]; + Vec3d outerTop = outer[0][i]; + Vec3d outerBot = outer[0][i + 1]; + + float vStart = rightV1 + rightPartV * i; + float vEnd = rightV1 + rightPartV * (i + 1); + + renderCapeQuad(vertexConsumer, entry, + outerTop, innerTop, innerBot, outerBot, + rightU1, vEnd, + rightU2, vStart, + light, overlay, 1, 0, 0, false); + } + + // ============================= + // 6. TOP (i = 0) + // ============================= + + for (int x = 0; x < H_PARTS; x++) { + + 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); } } @@ -279,7 +401,7 @@ public class CloakFeatureRenderer extends FeatureRenderer Date: Fri, 10 Apr 2026 04:10:57 +0200 Subject: [PATCH 12/12] Improved cloak max x speed --- .../org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java | 2 +- .../org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java | 2 +- .../org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java | 2 +- .../org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java | 2 +- .../org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index e549057..2725e32 100644 --- a/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.4/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -436,7 +436,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); + float velX = Math.min(0.8f, playerEntityRenderState.field_53537 / 108.0f); float rawVelY = playerEntityRenderState.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; diff --git a/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index e549057..2725e32 100644 --- a/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.5/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -436,7 +436,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); + float velX = Math.min(0.8f, playerEntityRenderState.field_53537 / 108.0f); float rawVelY = playerEntityRenderState.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; diff --git a/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index e549057..2725e32 100644 --- a/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.6/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -436,7 +436,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); + float velX = Math.min(0.8f, playerEntityRenderState.field_53537 / 108.0f); float rawVelY = playerEntityRenderState.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; diff --git a/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index e549057..2725e32 100644 --- a/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.7/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -436,7 +436,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); + float velX = Math.min(0.8f, playerEntityRenderState.field_53537 / 108.0f); float rawVelY = playerEntityRenderState.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16; diff --git a/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java b/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java index e549057..2725e32 100644 --- a/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java +++ b/versions/1.21.8/src/main/java/org/saturnclient/impl/cosmetics/CloakFeatureRenderer.java @@ -436,7 +436,7 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { - float velX = Math.min(0.6f, playerEntityRenderState.field_53537 / 108.0f); + float velX = Math.min(0.8f, playerEntityRenderState.field_53537 / 108.0f); float rawVelY = playerEntityRenderState.field_53536; float velY = (rawVelY > 3.4f ? rawVelY : rawVelY < -3.4f ? rawVelY : 0.0f) / 16;