Arch the twerk's lower back instead of hunching it

The torso tips forward around the hips and bends backwards, so the lower
back curves in and the chest comes up. The head and arms get the tilt
themselves, since they only follow the torso's bend.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-26 20:04:08 +02:00
co-authored by claude
parent 34aba293b6
commit 09cfd0cc10
@@ -72,8 +72,9 @@ final class TestAnimations {
}
/**
* Squatting with knees out, hips pushed back and the chest leaning forward, bouncing the hips
* about 2.5 times a second. The torso and knees bend with the bounce, so the back arches.
* Squatting with knees out and hips pushed back, bouncing the hips about 2.5 times a second. The
* torso tips forward around the hips and bends backwards, so the lower back arches in (not a
* hunched back) and the chest comes up. The arch deepens at the top of each bounce.
*/
private static Pose twerk(float t) {
// 0 at the bottom of the bounce, 1 at the top.
@@ -82,22 +83,39 @@ final class TestAnimations {
float thighPitch = -1.1f + 0.15f * up;
float knee = 1.75f - 0.3f * up;
float splay = 0.55f;
float lean = 1.25f - 0.2f * up;
// How far the torso tips forward at the hips, and how much of that the arch takes back by the
// chest. The chest ends up leaning forward by tilt - arch.
float tilt = 1.4f;
float arch = 0.4f + 0.3f * up;
// Lower everything by as much as the bent legs got shorter, and push the hips back so the feet
// stay under where they were (a rigid thigh + shin approximation of the curved leg).
float drop = 12 - 6 * (float) (Math.cos(thighPitch) + Math.cos(thighPitch + knee)) * (float) Math.cos(splay);
float back = -6 * (float) (Math.sin(thighPitch) + Math.sin(thighPitch + knee));
BoneTransform shift = new BoneTransform(0, drop, back, 0, 0, 0, 0, 0);
// The arms ride on the chest, which would swing them back as it leans. Tilt them forward by a
// bit more than the lean so they hang down towards the knees, a little outside them.
float armPitch = -lean * 1.1f;
// The body pivots at the neck, so tipping it forward swings the hips back. Move the pivot so
// the hips stay on the legs.
float cos = (float) Math.cos(tilt);
float sin = (float) Math.sin(tilt);
float bodyY = drop + 12 - 12 * cos;
float bodyZ = back - 12 * sin;
// The head and arms only turn with the torso's bend, not its rotation, so they get the tilt
// themselves (tilt plus the bend's turn is the chest's lean) and sit where the tipped torso puts
// them: the neck at the body's pivot, the shoulders 2 pixels down it.
float chest = tilt - arch;
float shoulderY = bodyY + 2 * cos - 2;
float shoulderZ = bodyZ + 2 * sin;
// Arms riding on a leaning chest would swing back, so tilt them forward to hang to the knees.
float armPitch = tilt - chest * 1.1f;
// Follow the chest, then look up by half its lean.
float headPitch = tilt - chest * 0.5f;
return Pose.builder()
.set(Bone.BODY, shift.withBend(lean, 0))
.set(Bone.HEAD, new BoneTransform(0, drop, back, -lean * 0.5f, 0, 0, 0, 0))
.set(Bone.RIGHT_ARM, new BoneTransform(0, drop, back, armPitch, 0, 0.3f, 0.5f, 0))
.set(Bone.LEFT_ARM, new BoneTransform(0, drop, back, armPitch, 0, -0.3f, 0.5f, 0))
.set(Bone.BODY, new BoneTransform(0, bodyY, bodyZ, tilt, 0, 0, arch, (float) Math.PI))
.set(Bone.HEAD, new BoneTransform(0, bodyY, bodyZ, headPitch, 0, 0, 0, 0))
.set(Bone.RIGHT_ARM, new BoneTransform(0, shoulderY, shoulderZ, armPitch, 0, 0.3f, 0.5f, 0))
.set(Bone.LEFT_ARM, new BoneTransform(0, shoulderY, shoulderZ, armPitch, 0, -0.3f, 0.5f, 0))
// Knees fold back and a little inwards, so the thighs splay out and the feet come in.
.set(Bone.RIGHT_LEG, new BoneTransform(0, drop, back, thighPitch, 0, splay, knee,
(float) Math.PI - 0.35f))