- 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]>
80 lines
4.2 KiB
Markdown
80 lines
4.2 KiB
Markdown
# 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](https://stonecutter.kikugie.dev/) 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`:
|
|
|
|
```java
|
|
//? 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
|
|
|
|
```sh
|
|
./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=...`.
|