Character models and animation¶
Updated 2026-09-08.
The 14 goblin, orc and village NPC variants use Blender-authored meshes with rigid joints. Source: art/blender/vyrgarde_characters_v2.blend. The previous vyrgarde_characters.blend is preserved. Runtime models live in client/public/models/characters.
Rebuild and inspect¶
/Applications/Blender.app/Contents/MacOS/Blender --background --factory-startup --python-exit-code 1 --python tools/blender/create_character_models.py
/Applications/Blender.app/Contents/MacOS/Blender --background art/blender/vyrgarde_characters_v2.blend --python tools/blender/render_character_preview.py
npm run validate:characters
npm run test:characters
npm run typecheck
npm run build
With the Vite client running, open http://127.0.0.1:5173/characters.html. The local workshop supports all 16 variants, individual animation states, an animation reel, lineup/crowd scenes and close/distant geometry inspection. It uses the game's actual loader, animator and entity view. It is a development page, not a production build entry. Normal gameplay remains at /.
Visuals and movement¶
The upgrade adds shaped axe blades, layered belts and bracers, buckles, outfit accents, war paint, improved faces and curved capes. The head and torso own their visible geometry, so hair, helmets and clothing move with their joints. Elbows and knees articulate independently; held items follow the right wrist.
client/src/render/characterAnimation.ts blends locomotion, breathing, body sway, NPC gestures, glances, windups, attacks, casting, blocking and hit/stagger reactions. Poses are computed on the client from server animation states; they are not baked glTF animation clips. Animation affects the visual hierarchy only. Server position, heading, damage, attack timing, hit capsules and interaction ranges retain their existing authority.
Keep all named pivots when editing: root, torso, head, armL, armR, forearmL, forearmR, legL, legR, shinL, shinR, heldItem, cloth. Blender faces -Y; the glTF export faces +Z, with the runtime visual wrapper converting it to the game's -Z forward. Preserve that conversion when adding poses.
Rendering cost¶
Each joint has one material batch using a shared colour atlas, packed metallic/roughness atlas and emission atlas. Textures are embedded. Clones share geometry and textures while keeping one private material per character for damage flashes and corpse fading. Static mesh-local matrices are cached; joint matrices remain animated.
Every rendered part has paired character_lod: near/far meshes. The runtime switches to distant geometry beyond 34 metres and back below 29 metres, avoiding flicker at the boundary. Close animation updates every frame, then drops to 20 Hz beyond 35 metres and 10 Hz beyond 85 metres. Offscreen joint updates run at 5 Hz; animation state changes still update immediately. Existing 48-metre character shadow culling remains. Nameplates and health bars have distance limits.
Measured from the complete 14-character library:
| Metric | Previous | Updated |
|---|---|---|
| Close triangles | 156,286 | 63,270 (-59.5%) |
| Close mesh batches | 267 | 153 (-42.7%) |
| Distant triangles | 156,286 | 20,002 (-87.2%) |
| Library bytes | 6,533,520 | 3,887,704 (-40.5%) |
| Materials | 27 | 1 |
| Embedded images | 24 | 3 |
These are character asset costs, not a claimed percentage increase in whole-game FPS. Close and distant geometry have the same number of batches; distant geometry reduces triangle processing. Recorded manifests are in art/blender/previews/performance-before.json and performance-after.json.
Verification¶
- All 14 individual GLBs and the combined library pass attachment, texture, version, LOD pairing and geometry-budget checks.
- Four animation regression tests cover movement authority, pose continuity, interrupted attacks, active NPC idles and optional-joint fallbacks.
- Browser checks confirm moving heads, knees and elbows, windup-to-attack transitions, visible damage flashes and restoration of the authored emission map.
- A 56-character browser scene loaded all 14 variants with 56 independent materials. Rendered triangles fell from 253,092 to 80,020 when selecting distant detail. Switching back to a single character continued rendering correctly.
- The actual browser game loaded the 3,887,704-byte combined library without errors. Walking to Captain Rusk and opening his dialogue worked.
- TypeScript checks and the production client build pass. The build retains its existing large shared Three.js/world chunk advisory.
- The multiplayer smoke test saw both clients, movement and snapshots. The existing 45-second combat bot left the safe zone, killed four mobs, dealt 192 damage, received 101 damage, gained skills and looted a bag on an isolated local server.
These checks do not establish performance on other machines or a public deployment. The procedural fallback remains available if a model cannot load. Blender source and preview images remain editable/reproducible from the authoring scripts.
Runtime batching¶
The environment polish pass adds rigBatching.ts: each Blender character now renders as one GPU skin per LOD while retaining its named rigid joints and shared atlas. Near/far triangle counts are unchanged; runtime character mesh draw calls are reduced from 11–13 articulated batches to one. Skeletons and flash materials remain private per instance. See Environment models for verification and regeneration commands.
M1 wolves¶
art/blender/vyrgarde_wolves_v1.blend adds a hunting wolf and scarred pack leader. Rebuild using Blender's background mode and tools/blender/create_wolf_models.py. The wolf atlas has its own texture directory, so rebuilding it does not overwrite the humanoid atlas. npm run validate:characters now checks all 16 variants and both combined libraries.
| Wolf library metric | Value |
|---|---|
| Combined GLB | 827,904 bytes |
| Near triangles, wolf / leader | 5,856 / 6,300 |
| Far triangles, wolf / leader | 1,564 / 1,682 |
| Atlas material / embedded images | 1 / 3 |
| Runtime draws per visible wolf | 1 for the selected LOD |
The quadruped rig uses armL/R for front legs, legL/R for hind legs, cloth for the tail and heldItem for the jaw. All four legs attach to the root; the head attaches to the torso. Its rig metadata is version 3 with quadruped: true. Do not apply the humanoid consolidation step to these pivots.
wolfAnimation.ts blends distance-driven diagonal strides with a lowered-head windup, committed lunge, recovery and howl. The client renders these poses from authoritative combat state. Two added tests sample pose continuity at 30/60/200 Hz and verify diagonal leg, tail and jaw motion. npm run test:starter includes them alongside eight content/combat tests.