Skip to content

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

MapGameplayCharacter presentation
L_MF_ThirdPersonHordeDefenseShared 420-enemy, three-phase horde-defense rulesRigged player plus a bounded twelve-enemy skeletal near tier; coherent single-piece static far tier
L_MF_ThirdPersonHordeDefenseVATThe same population, controls, abilities, balance, obstacles, damage, and objectivesOne 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

Live skeletal Third-Person Horde Defense at Wave 1 and one second from the fixed default camera, with the editor monitor showing population layers

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.

Live VAT Third-Person Horde Defense at Wave 1 and one second from the identical fixed default camera, with the editor monitor showing population layers

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.

Mass Forge Live Showcase monitor for the skeletal map with 420 allocated, 140 active, 420 simulated, 420 represented, 13 animated, and zero resolved

Mass Forge Live Showcase monitor for the VAT map with 420 allocated, 140 active, 420 simulated, 420 represented, 420 animated, and zero resolved

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.

Third-Person Horde Defense VAT map during assault wave three with one rigged player, a dense batched enemy population, team palettes, and bright gameplay-driven material states

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.py and import_character_animation_pack.py beside the source.
  • Receipts: CharacterAnimationAssetReceipt.json, CharacterAnimationBakeMetadata.json, and CharacterAnimationUnrealImportReceipt.json under Generated.
  • Unreal destination: /MassForge/MassForgeShowcases/CharacterAnimation.
  • Provenance gate: Verify-ArtProvenance.ps1 matched 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:

StateVAT framesLoopAuthored event
Idle0–29yes
Move30–53yes
Attack54–71noimpact frame 10
Cast72–95norelease frame 13
Hit96–105no
Jump106–129noapex 12, land 23
Death130–159nocompletion 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 (acceptance 0.1 cm);
  • mean position error: 0.0031324 cm;
  • maximum normal error: 0.0370094° (acceptance 0.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:

  1. phase offset;
  2. play rate;
  3. current start frame;
  4. current end frame;
  5. previous start frame;
  6. previous end frame;
  7. transition alpha;
  8. animation state;
  9. health ratio;
  10. palette index;
  11. material-state flags; and
  12. 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.

ObservationAllocated enemiesSimulated enemiesRepresented enemiesSkeletal enemiesVAT enemiesStatic far enemiesRepresentation-culled
Skeletal map, initial4204204201204080
VAT map, initial420420420042000
Either map, gameplay audit after seven defeats420420413backend-specific survivorsbackend-specific survivorsbackend-specific survivors0

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:

MetricAverageMedian95th percentileMaximum
Frame time6.621 ms5.145 ms11.983 ms19.788 ms
Game thread4.534 ms3.561 ms8.979 ms17.195 ms
Render thread4.979 ms3.365 ms10.398 ms12.378 ms
GPU0.688 ms0.666 ms0.873 ms1.177 ms
RHI draw calls183183183183
RHI primitives37,76537,76537,76537,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_VertexAnimation map 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.

Mass Forge documentation — generated from the shipping Markdown source.