Summary
Fix the runtime skinned-mesh reconstruction so the Mudwretch Foreman renders as a single connected body in-game instead of an exploded triangle cloud. Apply the minimal fix first (FR-2): after assigning the runtime-built sharedMesh, reassign the live SkinnedMeshRenderer.bones array so Unity rebinds the skin to the new mesh's bindpose set. Only if that does not reach connected:PASS, escalate (FR-3) to a FTK.Stitcher-exact fresh SkinnedMeshRenderer. The two share one acceptance (an in-game connected body on the rigid asset), so they are one deliverable with an internal escalation decided by the gate.
Parent Spec
Implements #72
Responsible Agent
csharp-harmony-engineer
Files to Touch
- FTKModFramework/Core/EnemyVisualPatch.cs (the glb path in
ApplyMeshSwap: FR-2 smr.bones rebind; FR-3 fresh-SMR fallback)
Dependencies
Acceptance Criteria
In-Game Verification
Run tools/ai-model-pipeline/run_visual_gate.py against a bridge-enabled launch (diagnostic flag OFF). The in-game verdict for the Mudwretch Foreman must report connected:PASS: a single connected component, not a shard cloud, on the rigid asset.
A green build alone does NOT satisfy this work item. The in-game connected:PASS is required.
Technical Notes
- In-engine precedent (verified via
game-decompile-analyst): FTK.Stitcher is the only runtime skinned-mesh build path in the game (player wearables). It keeps each mesh's own sharedMesh, copies materials, and NAME-remaps bones[] onto the live skeleton via TranslateTransforms; it never sets bindposes/boneWeights/rootBone. Mirror this exactly for FR-3.
- The enemy SMR is otherwise fully prefab-baked (zero runtime enemy
bindposes/boneWeights/rootBone assignments assembly-wide), which is why swapping only sharedMesh on the live renderer leaves the skin bound to the previous mesh and scatters the new verts.
BuildFreshSkin does NOT exist on this branch; FR-3 builds it fresh.
- Do NOT use
Mesh.indexFormat / IndexFormat.UInt32 (absent in Unity 2017.2.2p2); 16-bit indices only (21662 verts).
EnemyVisualPatch re-applies per spawn by design (no _done on the apply); idempotency is PER CLONE.
- If, after BOTH FR-2 and FR-3, the gate still reports
connected:FAIL on the rigid asset, do NOT keep grinding: surface it; the spec's objective pivot rule (FR-6) then applies and is recorded in the acceptance work item.
Definition of Done
Summary
Fix the runtime skinned-mesh reconstruction so the Mudwretch Foreman renders as a single connected body in-game instead of an exploded triangle cloud. Apply the minimal fix first (FR-2): after assigning the runtime-built
sharedMesh, reassign the liveSkinnedMeshRenderer.bonesarray so Unity rebinds the skin to the new mesh's bindpose set. Only if that does not reachconnected:PASS, escalate (FR-3) to aFTK.Stitcher-exact fresh SkinnedMeshRenderer. The two share one acceptance (an in-game connected body on the rigid asset), so they are one deliverable with an internal escalation decided by the gate.Parent Spec
Implements #72
Responsible Agent
csharp-harmony-engineer
Files to Touch
ApplyMeshSwap: FR-2smr.bonesrebind; FR-3 fresh-SMR fallback)Dependencies
Acceptance Criteria
sharedMeshswap, the patch reassignssmr.bones(and refreshesrootBoneonly if proven necessary) to force the rebind.connected:PASS): a fresh SkinnedMeshRenderer is created that shares the livebones/rootBone, the custom mesh and material are assigned to it, and the original renderer is disabled (not destroyed). It is idempotent per combat clone via a name/flag guard, exactly like the existing lantern and golem paths.Core/EnemyVisualPatch.cs: no publicContent.*surface is added and the glTF decoder is not modified.SELF-TEST PASSlines still present.In-Game Verification
Run
tools/ai-model-pipeline/run_visual_gate.pyagainst a bridge-enabled launch (diagnostic flag OFF). The in-game verdict for the Mudwretch Foreman must reportconnected:PASS: a single connected component, not a shard cloud, on the rigid asset.A green build alone does NOT satisfy this work item. The in-game
connected:PASSis required.Technical Notes
game-decompile-analyst):FTK.Stitcheris the only runtime skinned-mesh build path in the game (player wearables). It keeps each mesh's ownsharedMesh, copies materials, and NAME-remapsbones[]onto the live skeleton viaTranslateTransforms; it never setsbindposes/boneWeights/rootBone. Mirror this exactly for FR-3.bindposes/boneWeights/rootBoneassignments assembly-wide), which is why swapping onlysharedMeshon the live renderer leaves the skin bound to the previous mesh and scatters the new verts.BuildFreshSkindoes NOT exist on this branch; FR-3 builds it fresh.Mesh.indexFormat/IndexFormat.UInt32(absent in Unity 2017.2.2p2); 16-bit indices only (21662 verts).EnemyVisualPatchre-applies per spawn by design (no_doneon the apply); idempotency is PER CLONE.connected:FAILon the rigid asset, do NOT keep grinding: surface it; the spec's objective pivot rule (FR-6) then applies and is recorded in the acceptance work item.Definition of Done
SELF-TEST PASSlines still appear on startup (no regression)run_visual_gate.pyreportsconnected:PASSfor the Mudwretch Foreman on the rigid asset