Appearance
Character animation, VAT, and material-state evidence
This document records the concrete implementation and verification evidence for the two published Third-Person Horde Defense presentation paths. It is not a claim that every Mass entity owns an Actor or skeletal mesh.
Release shape
| Map | Gameplay | Character presentation |
|---|---|---|
L_MF_ThirdPersonHordeDefense | Shared 420-enemy, three-phase horde-defense rules | Rigged player plus a bounded twelve-enemy skeletal near tier; coherent single-piece static far tier |
L_MF_ThirdPersonHordeDefenseVAT | The same population, controls, abilities, balance, obstacles, damage, and objectives | One batched VAT/material presentation path for represented enemies; no per-enemy Actor requirement |
Both maps use FMFCharacterPresentationFragment as presentation-neutral Mass state. Gameplay authority remains in Mass Forge attributes, abilities, effects, tags, damage, and the sample's deterministic movement/objective code.
Matched runtime comparison

Skeletal presentation sample: Wave 1, 1 / 60 s, default camera and untouched controls. The monitor reports 420 allocated, 140 currently active, 420 simulated, 420 represented, and 13 animated—the rigged player plus the bounded twelve-enemy skeletal near tier.

VAT presentation sample: the same Wave 1, 1 / 60 s, camera, game rules, arena, and population. The monitor reports the same 420 allocated, 140 active, 420 simulated, and 420 represented entities, while all 420 enemies are animated through the batched VAT path.


The focused monitor crops make the accounting legible. “Allocated,” “active,” “simulated,” “represented,” and “animated” are separate workload statements; neither backend relabels allocation as visible or active enemies.

This packaged evidence frame is the accepted post-fix VAT presentation: whole connected bodies, correct frame-row orientation, purple/cyan palette variation, and no diagonal shards or unintended partial meshes. The player remains rigged; the enemy crowd uses batched VAT instances.
Original source and provenance
- Asset: Mass Forge Arcane Sentinel.
- Author/tool: original deterministic Blender 5.2.1 LTS source and scripts owned by the Mass Forge project. Morrow/Trellis was not used for this character pack.
- Rights: original project-owned work, cleared for redistribution with Mass Forge; no third-party or paid assets.
- Authoritative source:
Examples/MassForgeShowcases/ArtSource/CharacterAnimation/Generated/MF_ArcaneSentinel_Source.blend. - Rebuild/import scripts:
build_character_animation_pack.pyandimport_character_animation_pack.pybeside the source. - Receipts:
CharacterAnimationAssetReceipt.json,CharacterAnimationBakeMetadata.json, andCharacterAnimationUnrealImportReceipt.jsonunderGenerated. - Unreal destination:
/MassForge/MassForgeShowcases/CharacterAnimation. - Provenance gate:
Verify-ArtProvenance.ps1matched the original source plus 14/14 UE 5.8 skeletal/VAT packages.
The model contains 3,512 source vertices and 7,020 rendered triangles, uses one section/material and one fixed topology for skeletal and VAT output, has no unweighted vertices, uses at most four influences per vertex, and recorded a maximum normalized-weight error of 4.8e-08. The skeletal mesh has three LODs. Release 1 deliberately constrains VAT to one measured fixed-topology LOD and keeps explicit skeletal/static fallbacks.
Animation and VAT bake
The runtime contract contains seven states at 30 samples per second:
| State | VAT frames | Loop | Authored event |
|---|---|---|---|
| Idle | 0–29 | yes | — |
| Move | 30–53 | yes | — |
| Attack | 54–71 | no | impact frame 10 |
| Cast | 72–95 | no | release frame 13 |
| Hit | 96–105 | no | — |
| Jump | 106–129 | no | apex 12, land 23 |
| Death | 130–159 | no | completion frame 29 |
Victory and Stun source clips are also retained. All clips are in-place; gameplay transforms remain authoritative.
The position and normal data textures are each 4096×160, linear HDR, nearest-filtered, mip-free, non-streaming, and imported as RGBA16F. A direct (vertex_id + 0.5) / 4096 UV lookup failed packaged visual QA even with full-precision UV storage: cooked output could render stretched diagonal shards. An intermediate split using low bits in U and high bits in V also failed because the FBX/Unreal V-axis convention changed the high field. The accepted Split6x6TwoUChannels mesh stores low6 / 63 in UV1.U and high6 / 63 in UV2.U; both V components are deliberately ignored. The VAT material rounds both U fields, reconstructs high6 * 64 + low6, and only then calculates the texel center. Blender EXR scanline zero is at the lower edge, so the generated material explicitly applies OneMinus to its frame V coordinate before sampling in Unreal; without that conversion Idle reads Death and Move reads Jump. Editor automation decodes every render vertex, requires the unique reconstructed set to contain all 3,512 source IDs despite render-vertex duplication, and rejects out-of-range or non-integer U fields. Full-precision UV storage remains enabled as defense in depth. Raw runtime texel payload is 5,242,880 bytes per texture (10,485,760 bytes total) before platform/container overhead. The imported .uasset files are 4,513,552 and 5,033,637 bytes respectively; the fixed-topology VAT mesh package is 400,333 bytes. VAT bounds are extended by ±(200, 200, 220) cm.
Half-precision comparison sampled 561,920 vertex poses:
- maximum position error:
0.087183 cm(acceptance0.1 cm); - mean position error:
0.0031324 cm; - maximum normal error:
0.0370094°(acceptance0.1°); and - mean normal error:
0.0058121°.
Shared runtime contract
UMFCharacterPresentationProfile validates required assets, compatible skeletons, all seven unique non-overlapping clips, texture properties/dimensions, mesh render data, a positive finite sample rate, and the exact versioned twelve-float payload:
- phase offset;
- play rate;
- current start frame;
- current end frame;
- previous start frame;
- previous end frame;
- transition alpha;
- animation state;
- health ratio;
- palette index;
- material-state flags; and
- effect intensity.
The material flags are Damage Flash, Frozen, Burning, Shielded, Selected, and Dissolving. Death deterministically resolves to Dissolving. Team palette and temporary states use instance custom data; the VAT crowd does not allocate a dynamic material instance per enemy.
Move playback remains in-place and presentation-only, but its cadence is scaled from authoritative gameplay velocity for the rigged player, bounded skeletal enemies, and VAT instances. This reduces visible foot sliding without moving collision or gameplay authority into animation, and it preserves the frozen twelve-float layout by multiplying the existing play-rate slot. Arena decisions remain on a bounded 10 Hz fixed step; presentation retains the previous/current authoritative positions and interpolates between them every rendered frame. Movement direction drives facing while travelling, stopped units select Idle and face their target, and attack wind-up owns facing until its authored release. This removes visible 10-FPS stepping and sideways travel without multiplying the Mass simulation workload.
Lifecycle presentation is explicit. Newly engaged units use a 0.28-second smooth scale-in. Defeated units retain the authored Death clip for a 1.35-second retirement window, with EffectIntensity driving dissolve only after 0.88 seconds. The animation transition value no longer controls opacity, so a death reads as a fall followed by a late fade rather than an unexplained despawn.
Automated evidence — UE 5.8
The clean customer-style verifier passed on the i9-14900K Windows 11 25H2 reference machine:
- UE 5.8 Editor build;
- 9/9 Mass Forge showcase automation tests, warning-free;
- sample-boundary and character-art provenance gates;
- root menu plus all five published maps;
- Tower Defense 1,000-entity accepted VAT scale vista;
- Survivor Roguelite 1,000,000 allocated enemies with the explicitly bounded 1,200 simulated/represented pool; and
- the internal procedural-WPO fixture at 1K, 5K, 10K, and 25K, still labeled as procedural WPO rather than VAT.
At 28 simulated seconds both horde maps reported 420 allocated/simulated enemies, 413 represented survivors, seven defeated enemies, positive hero health, no game-over, zero obstacle penetrations, all seven animation-state evidence bits (StateMask=127), all six material-state evidence bits (MaterialMask=63), 61 Mass projectile launches, 61 matching impacts, and no spawn failure. The skeletal map reported NearSkeletal=12 CharacterBackend=SkeletalMesh; the twin reported NearSkeletal=0 CharacterBackend=VertexAnimationTexture. The full skeletal objective gate completed all three phases with a living hero and 385 launches/385 impacts.
Packaged Win64 cooking included 564 packages with zero reported cook warnings/errors. Separate packaged skeletal and VAT runs loaded all cooked assets, retained the complete 420-entity population, selected their intended backends, remained at positive health with GameOver=0, and reported zero penetrations or spawn failures. Verify-CharacterPresentationPackage.ps1 makes this two-map package gate repeatable after Verify-Showcases.ps1 -KeepWorkspace.
Representation accounting
The release evidence distinguishes Mass allocation/simulation from rendering. It does not add the rigged player to the enemy counts.
| Observation | Allocated enemies | Simulated enemies | Represented enemies | Skeletal enemies | VAT enemies | Static far enemies | Representation-culled |
|---|---|---|---|---|---|---|---|
| Skeletal map, initial | 420 | 420 | 420 | 12 | 0 | 408 | 0 |
| VAT map, initial | 420 | 420 | 420 | 0 | 420 | 0 | 0 |
| Either map, gameplay audit after seven defeats | 420 | 420 | 413 | backend-specific survivors | backend-specific survivors | backend-specific survivors | 0 |
The skeletal map additionally renders one rigged player. The VAT map renders its enemy population through one Hierarchical Instanced Static Mesh component/material batch rather than one Actor or dynamic material instance per enemy. The audit records all seven gameplay-driven animation states and all six combinable material states; defeated instances are retired rather than counted as live represented enemies.
Visual and performance observation
The current packaged visual evidence is .ci-artifacts/CharacterAnimationVAT/Evidence/ThirdPersonHordeDefenseVAT_VAxisFixed.jpg. A prior capture exposed that valid vertices were reading vertically mirrored animation rows: living idle enemies used Death poses and moving enemies used Jump poses. After adding the explicit EXR-to-Unreal V conversion and rebuilding the material, an independent controls-only release QA pass observed initial spawn through assault wave three. Living purple/cyan enemies remained upright connected whole bodies, advanced toward and generally faced the player, and showed no diagonal shards, false death poses, partial bodies, or unexplained hovering. The UI read ASSAULT WAVE and DEFEND THE STRONGPOINT, Escape returned to the hub, and the packaged application closed normally. Projectile release timing was not visually asserted from the wide shot; it remains covered by the deterministic launch/release/impact audit instead.
The deterministic Blender 5.2.1 source audit is recorded at .ci-artifacts/CharacterAnimationVAT/Evidence/CharacterAnimationQualityAudit.json. Across all nine clips it reports zero root drift, zero loop-seam delta, zero one-shot final-pose delta, finite deformation, one connected surface, normalized weights, no unweighted vertices, no more than four influences, maximum adjacent-frame motion of 78.9011 cm below the explicit 85 cm pop guard, and maximum planted-foot drift of 8.8095 cm below the 12 cm authored-loop guard. Visual QA remains the acceptance source for unintended self-intersection and bounds artifacts that this numeric audit cannot fully classify.
Verify-CharacterPresentationPackage.ps1 -SkipBuildCookRun -CaptureVATProfile produced a reproducible packaged Development observation on Windows 11 Pro 25H2, Intel Core i9-14900K, and NVIDIA GeForce RTX 5090. The VAT horde ran at 1600×900 with 420 allocated, simulated, represented, and VAT-animated enemies plus one rigged player. After discarding the first 100 of 600 captured frames:
| Metric | Average | Median | 95th percentile | Maximum |
|---|---|---|---|---|
| Frame time | 6.621 ms | 5.145 ms | 11.983 ms | 19.788 ms |
| Game thread | 4.534 ms | 3.561 ms | 8.979 ms | 17.195 ms |
| Render thread | 4.979 ms | 3.365 ms | 10.398 ms | 12.378 ms |
| GPU | 0.688 ms | 0.666 ms | 0.873 ms | 1.177 ms |
| RHI draw calls | 183 | 183 | 183 | 183 |
| RHI primitives | 37,765 | 37,765 | 37,765 | 37,765 |
The CSV and machine-readable summary remain under .ci-artifacts/CharacterAnimationVAT/PackagedWin64. This is a reference-machine observation, not a minimum-hardware guarantee and not a claim that one million enemies are visible or animated. The enemy VAT mesh is 7,020 triangles; the two uncompressed runtime VAT payloads total 10,485,760 bytes.
Measured boundaries
- “One million” in Survivor Roguelite means one million allocated enemy Mass entities, not one million visible, simulated, or VAT-animated enemies.
- The skeletal map does not create 420 skeletal Actors. It uses one rigged player and a bounded twelve-enemy skeletal tier; distant enemies remain an instanced presentation tier.
- The VAT map is genuine baked position/normal animation. The older
L_MF_VertexAnimationmap is a separate internal procedural-WPO regression fixture and is not linked from the customer launcher. - A common data contract and palette rules are shared, while the skeletal stylized material and VAT deformation material remain separate masters because their vertex paths are materially different.
- Performance numbers describe the exact packaged reference-machine run above; customers should profile their own content, platform, resolution, population, and material complexity.