selimaj-devandclaude 8bac5eef64 Bend limbs along a single axis with a Bézier spine
- core: Bezier (any degree, De Casteljau), LimbBend (cubic spine through
  the rigid upper/joint/lower shape, one ring per texture row spaced by
  arc length, rigid rings so pieces share vertices) and QuadSplitter
  (cuts faces at the rings, interpolating UVs).
- fabric: ModelPartMixin draws parts that have a bend with
  BentCubeRenderer, which deforms vanilla's own cubes. Overlays and armor
  use the limb's spine so they stay attached. Bends are reset every
  setupAnim because models are shared between entities.
- testmod: flap and squat animations, and a client gametest that
  screenshots each animation from the front and side
  (runClientGameTest). The tester runs with the full Fabric API because
  single modules don't bring their version-specific dependencies.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-26 19:43:57 +02:00
2026-09-26 16:17:34 +00:00

emotes

A player emote library for Saturn Client, with limb bending.

Layout

  • core/: Minecraft-independent animation model (Bone, BoneTransform, Pose, Animation, AnimationPlayer). Plain Java 21 with JUnit tests. Published as org.saturnclient:emotes-core.
  • src/main/: the Fabric mod that puts poses on player models. It's built once per Minecraft version with Stonecutter and published as org.saturnclient:emotes-fabric:<version>+<mc>, with core bundled inside the jar.
  • src/testmod/: a dev-only tester mod that is never published. It adds /emotes play <name>, /emotes stop and /emotes info. Press F5 to see yourself. It also has a Fabric client gametest that creates a world and screenshots each animation from the front and side, into versions/<mc>/build/run/clientGameTest/screenshots/.

Supported versions: 1.21.4 to 1.21.11, the same range as Saturn Client. Each version's Fabric API version is set in stonecutter.properties.toml. The code uses Mojang mappings.

How it hooks in

  1. LivingEntityRendererMixin samples the entity's current pose into its render state (players only).
  2. HumanoidModelMixin applies that pose at the end of HumanoidModel.setupAnim. PlayerModel and the armor layers both go through this method, so armor follows the pose.

Both hook points exist unchanged in every supported version. Version-specific code goes behind Stonecutter comments, like the vertex accessors in BentCubeRenderer:

//? if >=1.21.9 {
return new float[] { vertex.x(), vertex.y(), vertex.z(), vertex.u(), vertex.v() };
//?} else
/*return new float[] { vertex.pos().x(), vertex.pos().y(), vertex.pos().z(), vertex.u(), vertex.v() };*/

Bending

Each limb bends along a single axis (BoneTransform.bend, bendAxis). It works like the cloak renderer: build a spine, put a ring of shared vertices along it, and draw the faces between the rings.

  • LimbBend (core) runs the spine along a cubic Bézier (Bezier). The control points are the rigid "upper half, joint, lower half" shape, and sharpness goes from a round curve (0) to a tight joint (1). The spine is cut into one ring per texture row (12 per limb), spaced evenly by arc length, so the limb keeps its length. Each ring moves its cross-section rigidly, so neighbouring pieces share vertices and the mesh stays closed.
  • QuadSplitter (core) cuts each vanilla face into strips at the rings and splits its UVs to match. The texture is unchanged apart from the cuts.
  • ModelPartMixin swaps in BentCubeRenderer for any part with a bend. That renderer deforms vanilla's own cubes, so slim arms, sleeves, pants and armor all work. Overlays and armor use the limb's spine, not their own inflated box, so they stay attached.

Axis 0 folds towards the front (-Z) and PI/2 towards +X, in the limb's own space. Knees fold back, so they use axis PI. The body bends too, but the head and arms don't follow its upper half yet. Held items still attach to the unbent arm.

Commands

./gradlew :core:test                      # core unit tests
./gradlew :1.21.11:runTestmodClient       # launch the game with the tester (run dir: ./run)
./gradlew :1.21.11:runClientGameTest      # screenshot each test animation, then quit
./gradlew :1.21.4:compileTestmodJava      # quick compile check for one version
./gradlew buildAll                        # every version's jar → build/libs/<version>/
GITEA_TOKEN=... ./gradlew publish         # publish core + every version to Gitea

The active version is set in stonecutter.gradle.kts (stonecutter active "..."), and it's the version whose code is uncommented in src/. Switch versions with the Stonecutter IntelliJ plugin or the Set active project to <v> Gradle task. Run Reset active project before committing, so the tree is committed with vcsVersion (1.21.11) active.

Loom 1.18 needs a Java 25 JVM to run Gradle, so the build is pinned to Loom 1.17 until the toolchain moves. Minecraft 26.x will need that upgrade.

Publishing

Artifacts go to the saturnclientmc organization's Maven registry: https://git.selimaj.dev/api/packages/saturnclientmc/maven. Use a token with package:write, set as GITEA_TOKEN or -PgiteaToken=....

S
Description
A powerful emote library for Saturn Client, with bending
Readme Apache-2.0
242 KiB
Languages
Java 57.4%
Python 40.9%
JavaScript 1.7%