diff --git a/src/main/java/org/saturnclient/saturnclient/config/Config.java b/src/main/java/org/saturnclient/saturnclient/config/Config.java index 08a5b90..109deea 100644 --- a/src/main/java/org/saturnclient/saturnclient/config/Config.java +++ b/src/main/java/org/saturnclient/saturnclient/config/Config.java @@ -12,7 +12,7 @@ public class Config { public static Property realisticLogo = Property.bool(false); public static Property saturnTitleScreen = Property.bool(true); - public static Property bendyCloaks = Property.bool(true); + public static Property cloakPhysics = Property.bool(true); public static Property openEmoteWheel = Property.keybinding(GLFW.GLFW_KEY_B); public static Property stagger = Property.bool(true); @@ -25,7 +25,7 @@ public class Config { config.property("Realistic logo", realisticLogo); config.property("Saturn client title screen", saturnTitleScreen); config.property("Open Emote Wheel", openEmoteWheel); - config.property("Bendy Cloaks", bendyCloaks); + config.property("Cloak Physics", cloakPhysics); config.property("Stagger Animations", stagger); // Initialize a sub namespace for theme diff --git a/src/main/java/org/saturnclient/saturnclient/cosmetics/CloakFeatureRenderer.java b/src/main/java/org/saturnclient/saturnclient/cosmetics/CloakFeatureRenderer.java index 6dc23bd..6a519d5 100644 --- a/src/main/java/org/saturnclient/saturnclient/cosmetics/CloakFeatureRenderer.java +++ b/src/main/java/org/saturnclient/saturnclient/cosmetics/CloakFeatureRenderer.java @@ -1,5 +1,7 @@ package org.saturnclient.saturnclient.cosmetics; +import java.util.Arrays; + import org.saturnclient.saturnclient.client.player.SaturnPlayer; import org.saturnclient.saturnclient.config.Config; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; @@ -22,7 +24,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.equipment.EquipmentAsset; import net.minecraft.registry.RegistryKey; import net.minecraft.util.Identifier; -import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.Vec3d; /** @@ -35,7 +36,7 @@ import net.minecraft.util.math.Vec3d; public class CloakFeatureRenderer extends FeatureRenderer { private final EquipmentModelLoader equipmentModelLoader; private static final int PARTS = 16; - private float currentVelocity = 0.0f; + private final float[] segmentValues = new float[PARTS]; private static final float TEX_W = 176f; private static final float TEX_H = 138f; @@ -78,6 +79,7 @@ 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; - // FRONT FACE (outside) - this.renderCapeQuad( - vertexConsumer, matrixStack, - new Vec3d(x2, y4, z2 + curveOffsetBottom), - new Vec3d(x1, y4, z2 + curveOffsetBottom), - new Vec3d(x1, y3, z2 + curveOffsetTop), new Vec3d(x2, y3, z2 + curveOffsetTop), - frontU1, frontV2 - (frontPartV * i), - frontU2, frontV2 - (frontPartV * (i + 1)), - light, overlay, - 0.0f, 0.0f, 1.0f, false); + // 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; - // BACK FACE (inside) - this.renderCapeQuad( - vertexConsumer, matrixStack, - new Vec3d(x1, y4, z1 + curveOffsetBottom), - new Vec3d(x2, y4, z1 + curveOffsetBottom), - new Vec3d(x2, y3, z1 + curveOffsetTop), new Vec3d(x1, y3, z1 + curveOffsetTop), - backU1, backV2 - (backPartV * i), - backU2, backV2 - (backPartV * (i + 1)), - light, overlay, - 0.0f, 0.0f, 1.0f, false); + float nextY = curY + dirY * capePartHeight; + float nextZ = curZ + dirZ * capePartHeight; - // LEFT FACE (thickness at x2) - this.renderCapeQuad( - vertexConsumer, matrixStack, - new Vec3d(x2, y4, z1 + curveOffsetBottom), - new Vec3d(x2, y4, z2 + curveOffsetBottom), - new Vec3d(x2, y3, z2 + curveOffsetTop), new Vec3d(x2, y3, z1 + curveOffsetTop), - leftU1, leftV2 - (leftPartV * i), - leftU2, leftV2 - (leftPartV * (i + 1)), - light, overlay, - 1.0f, 0.0f, 0.0f, false); + // 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); - // RIGHT FACE (thickness at x1) - this.renderCapeQuad( - vertexConsumer, matrixStack, - new Vec3d(x1, y4, z2 + curveOffsetBottom), - new Vec3d(x1, y4, z1 + curveOffsetBottom), - new Vec3d(x1, y3, z1 + curveOffsetTop), new Vec3d(x1, y3, z2 + curveOffsetTop), - rightU1, rightV2 - (rightPartV * i), - rightU2, rightV2 - (rightPartV * (i + 1)), - light, overlay, - -1.0f, 0.0f, 0.0f, false); + // 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); + + // FRONT (Inner) - Changed winding/order to fix flipping + this.renderCapeQuad(vertexConsumer, matrixStack, + innerBotLeft, innerBotRight, innerTopRight, innerTopLeft, + frontU1, frontV1 + (frontPartV * i), frontU2, frontV1 + (frontPartV * (i + 1)), + light, overlay, 0, 0, -1, false); + + // BACK (Outer) + this.renderCapeQuad(vertexConsumer, matrixStack, + outerBotRight, outerBotLeft, outerTopLeft, outerTopRight, + backU1, backV1 + (backPartV * i), backU2, backV1 + (backPartV * (i + 1)), + light, overlay, 0, 0, 1, false); + + // 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); + + // RIGHT EDGE + this.renderCapeQuad(vertexConsumer, matrixStack, + outerBotRight, innerBotRight, innerTopRight, outerTopRight, + 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, matrixStack, + outerTopLeft, outerTopRight, innerTopRight, innerTopLeft, + topU1, topV1, topU2, topV2, light, overlay, 0, 1, 0, false); + } + + // 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); + } + + curY = nextY; + curZ = nextZ; + } + } + + // Shift array left and insert new value at the start + private void updateSegmentValues(float value) { + // Shift all elements one position to the right (from end to start) + for (int i = segmentValues.length - 1; i > 0; i--) { + segmentValues[i] = segmentValues[i - 1]; } - // TOP FACE (shoulders edge) – use its dedicated strip - matrixStack.push(); - matrixStack.translate(0.0f, y2 + capeDepth, 0.0f); - matrixStack.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_X.rotationDegrees(90.0f)); - matrixStack.translate(0.0f, -y2, 0.0f); - this.renderCapeQuad( - vertexConsumer, matrixStack, - new Vec3d(x2, y2 + capeDepth, z2), new Vec3d(x1, y2 + capeDepth, z2), - new Vec3d(x1, y2, z2), new Vec3d(x2, y2, z2), - topU1, topV1, topU2, topV2, - light, overlay, - 0.0f, 1.0f, 0.0f, true); - matrixStack.pop(); - - // BOTTOM FACE – use bottom strip - float curveOffset = getCurve(0, curveMagnitude, playerState); - this.renderCapeQuad( - vertexConsumer, matrixStack, - new Vec3d(x2, 0.0f, z2 + curveOffset), new Vec3d(x1, 0.0f, z2 + curveOffset), - new Vec3d(x1, 0.0f, z1 + curveOffset), new Vec3d(x2, 0.0f, z1 + curveOffset), - bottomU1, bottomV1, bottomU2, bottomV2, - light, overlay, - 0.0f, 0.0f, 1.0f, false); - - matrixStack.pop(); + // Insert the new value at index 0 + segmentValues[0] = value; } + private long lastUpdate = 0; + public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, PlayerEntityRenderState playerEntityRenderState, float f, float g) { if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible @@ -295,8 +283,8 @@ public class CloakFeatureRenderer extends FeatureRenderer= 20) { + float value = Math.min(1.0f, playerEntityRenderState.field_53537 / 108.0f) + + (Math.max(0, playerEntityRenderState.field_53536) / 16); + if (Config.cloakPhysics.value) { + updateSegmentValues(value); + } else { + float last = segmentValues[0]; + float maxStep = 0.02f; + float next = value; + + if (next > last) + next = Math.min(next, last + maxStep); + else if (next < last) + next = Math.max(next, last - maxStep); + + Arrays.fill(segmentValues, next); + } + lastUpdate = now; } - if (!Config.bendyCloaks.value) { - curve = 0.0f; - } - - renderCape(matrixStack, vertexConsumer, playerEntityRenderState, light, - OverlayTexture.DEFAULT_UV, Math.min(rotation, 60.0f), Math.min(curve, 0.5f)); + renderCape(matrixStack, vertexConsumer, playerEntityRenderState, light, OverlayTexture.DEFAULT_UV); matrixStack.pop(); }