fix(character): the arms hang at the sides — and why that took so long

The character now stands naturally: arms at its sides with a soft elbow bend, hands
at the hips, feet flat. Both character shots confirm it, front and back.

The root cause was not the pose. The FBX importer had been silently refusing to
replace the existing assets: after changing the arm base from 1.55 rad to 0.55 rad
and rebuilding, the imported clip returned the IDENTICAL bone quaternion
(+0.006, +0.002, -0.179, -0.984). replace_existing=True was set and delete_asset
returned False even after chmod u+w, because the pawn's constructor hard-references
the clips. Importing under a new name (CharacterV2) created fresh assets and the
same probe then returned (+0.439, +0.898) — the rotation had landed. Every earlier
attempt had been invisible for this reason.

The rotation itself is measured, not inherited: sweeping it in Blender gives 0.55 rad
for arms down and clear of the body, 0.65 for straight down against the hip, 0.75 for
past vertical and into the torso. It lives in the clamp keys, not in the bind: every
bind experiment was impossible on this shape-keyed MPFB mesh or left Unreal showing
the old bind.

Remaining, for 0.2.2: the fingers splay from the bind, and the walk clip has still
not been captured in motion.
This commit is contained in:
zwitschi committed 2026-11-09 11:42:36 +01:00
1 parent 8993fd83c4
commit 46b84884b8
45 files changed
+131 -67

No files matched your search

+8
View File
@@ -478,3 +478,11 @@ Scope: facts and traps that cost time to establish. Planned work lives in [TODO.
- **The base pose needs inverted signs when baked to rest.** Applied as written, the FBX axis mapping produced arms raised in a V; negating the table gives arms down. Both states were verified on the imported asset, not assumed.
- **Unreal bone names use underscores**: `upperarm01_L`, not Blender's `upperarm01.L`. `GetBoneIndex(TEXT("upperarm01.L"))` returns INDEX_NONE, `GetBoneTransform` then returns identity, and a probe reading it reports a constant 0.0000 — which looks like "animation not applied" and is actually "bone not found". Check `AnimPose.get_bone_names()` before trusting any bone probe.
- **`Log` verbosity does not reach the capture log, `Warning` does.** Two diagnostic runs produced zero lines because the messages were `UE_LOG(LogTemp, Log, ...)`; the same lines at `Warning` appeared. Instrument with Warning when the evidence has to survive into `/tmp/gigline/render-*.log`.
## 2026-11-09 — Unreal silently refuses to replace an imported asset
- **The real reason the character pose never changed across a dozen rebuilds:** the FBX importer reported success every time, but `Character_Anim` and its two AnimSequences kept their original data. Proof: after changing the arm base from 1.55 rad to 0.55 rad and rebuilding, `AnimPose.get_bone_pose("upperarm01_L")` returned the **identical** quaternion `(+0.006, +0.002, -0.179, -0.984)` as before. An asset that does not change when its source does is not being written.
- `AssetImportTask.replace_existing = True` was set, and `EditorAssetLibrary.delete_asset` returned **False** for both clips, even after `chmod u+w` (the `.uasset` files are LFS-lockable and check out read-only). Deleting is blocked because the pawn's C++ constructor hard-references the clips.
- **Workaround that works: import under a new name.** `Tools/ue_character_anim_import.py` now uses `PREFIX = "CharacterV2"`, which creates `CharacterV2`, `CharacterV2_Skeleton` and `CharacterV2Human_rig_GigLine_{Idle,Walk}` fresh; the same quaternion probe then returned `(+0.439, +0.898)`, i.e. the 0.55 rad arm rotation had finally landed.
- **Check the asset, not the importer.** A pose or material change that does not alter the probed asset data did not happen, whatever the log says. Probing a clip's bone quaternion takes one commandlet and settles it in seconds.
- **The arms-down rotation belongs in the clips, not in the bind.** Every bind experiment (bake pose to rest, clear shape keys, apply the armature modifier, re-bind) was either impossible on this shape-keyed MPFB mesh or left Unreal showing the old bind. `Tools/character_anim_build.py` now carries a measured base offset in the clip keys (`BASE`: 0.55 rad on `upperarm01`, chosen by sweeping the rotation in Blender and reading the bone direction), and the character stands with its arms at its sides.