Files
security-game/docs/npc-model-sourcing.md
T
zwitschi aec1fb7b69
Full build / Import + headless tests (push) Successful in 41s
Full build / Full desktop build + optional release (push) Skipped
Raise character limits and document optional Blender MCP
2026-11-04 13:47:16 +01:00

337 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NPC model sourcing and expansion plan
Status: **direction chosen 2026-11-03.** The procedural generator is the primary
path, the Quaternius CC0 pack is a reviewed reference/prototype, and MakeHuman is
deferred to a separate local development pipeline. No downloaded source model is
committed to the game or included in a release; the committed research preview
sheets carry the required attribution. Local evaluation files live under
`tmp/npc-research/` (gitignored). The decision is recorded in
[ADR-0015](adr/0015-npc-model-variety-sourcing.md).
This page records the free/open-source character sources that were reviewed, how
they behave when imported into the existing Blender tooling, and the options for
creating a larger NPC model library. The runtime contract and current library are
documented in [`blender-assets.md`](blender-assets.md) and
[`npc-visual-review.md`](npc-visual-review.md).
## Why this review exists
The library has eight skinned characters, and `NpcAvatar.character_model_for()`
reuses them across role families. That is enough for a release candidate, but
crowds repeat the same faces, builds and outfits. Three venues, a 24-NPC budget
and close-up portraits make that repetition visible.
Any expansion must preserve the existing integration contract:
- mesh node `npc_base` under a `Skeleton3D`;
- bones `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R` plus
`Hips`, `Spine`, `Neck` at the contract rest positions;
- five tint slots (`skin`, `cloth`, `pants`, `accessory`, `hair`);
- front on Blender −Y / Godot +Z, feet near the floor, 1.6–2.1 m height;
- at most 16,000 triangles and 16,000 glTF vertices per character;
- gear attach points at head, back, chest and hand.
The existing `audit` command is the gate. A source that looks good in a preview
but fails `audit` is not usable without an adaptation step.
## What was evaluated
Blender 5.2.0 LTS (Flatpak) imported each file in background mode. Measurements
below come from the imported scene, because external GLBs often carry node
transforms that the project's audit (which reads accessor-local bounds) does not
apply. "Vertices" are the imported, seam-split counts that matter for the glTF
budget, not the source file's vertex count.
| Source | Licence | Files checked | Geometry / rig | Height | Verdict |
| --- | --- | --- | --- | --- | --- |
| **Quaternius Ultimate Modular Men / Women** (mirrored GLB pack) | CC0 1.0 | `Casual_Hoodie.glb`, `Worker.glb`, `Casual.glb` | 5,240–6,424 tris; 10,534–12,968 verts; 62 bones; 24 animations; 4 skinned meshes on one armature | 1.80–1.87 m | **Best silhouette/outfit match.** Fails the contract: wrong bone names/rest positions, no tint slots, no `npc_base` node, vertex budget exceeded. |
| **Kenney Blocky Characters 2.0** | CC0 1.0 | full pack (18 characters), `character-a.glb`, `character-f.glb` | 72 tris; 143 verts; static, no armature; one texture atlas per character | 2.7 m imported (needs 0.67× scale) | **Not a close-up character.** Useful as a crowd/impostor LOD or placeholder, but cannot be driven by the skeleton and has no separate face/clothing slots. |
| **Khronos CesiumMan** | CC BY 4.0 (© Cesium) | `CesiumMan.glb` | 4,672 tris; 3,273 verts; 19-bone walk rig; 1 material/texture; 1 animation | 1.45 m | **Technical retarget test only.** One outfit and one look, wrong rig, short; works as a skinning/pose reference. Attribution is required. |
| **Khronos RiggedFigure** | CC BY 4.0 (© Cesium) | `RiggedFigure.glb` | 256 tris; 370 verts; 19-bone walk rig; no UVs; 1 animation | 1.48 m | **Rig smoke test only.** A mannequin with no usable surface detail. |
| **MakeHuman core base mesh / MPFB** | Core assets CC0 1.0; application AGPL/GPL | `makehuman/data/3dobjs/base.obj` | 19,158 verts; 18,486 polygons; 36,972 tris; no rig in the base mesh | ~16.9 model units before scaling | **Best body-shape diversity, highest pipeline cost.** Needs scaling, decimation/retopology and a generated rig. The MakeHuman/MPFB tool itself is not bundled. |
Local review sheet (front views, not committed):
`tmp/npc-research/previews/contact_sheet.jpg`.
Rejected without downloading: Mixamo and Ready Player Me. Both are useful
services, but their terms are not a simple free/open-source redistribution basis
for shipped character meshes. Sketchfab, Poly Pizza and OpenGameArt can contain
excellent CC0 work, but each asset needs a case-by-case licence and provenance
check; they are not a consistent library source.
### Measured contract failures
Every example fails the unmodified `audit` contract:
| Check | Quaternius | Kenney | CesiumMan | RiggedFigure | MakeHuman base |
| --- | --- | --- | --- | --- | --- |
| `npc_base` mesh node | no | no | no | no | no |
| Required 13 bones | no (62 different names) | no rig | no (19 different names) | no (19 different names) | no rig |
| Five tint slots | no | no | no | no | no |
| ≤ 16,000 tris | yes | yes | yes | yes | yes |
| ≤ 16,000 verts | yes (10.5–13k) | yes | yes | yes | no (19k) |
| Feet on floor | yes | yes (2.7 m scale) | yes (−11 mm) | yes | no (needs scaling) |
| Height 1.6–2.1 m | yes | no (2.7 m) | no (1.45 m) | no (1.48 m) | needs scale |
| Skinned / UV mapped | yes / yes | no / yes | yes / yes | yes / no | no / no |
The practical conclusion is that **there is no drop-in download**. External
sources need a deliberate intake step, and the expensive part is not decimation:
it is rebuilding or retargeting the skeleton and material slots without breaking
the animation proxies and gear placement.
The table uses Blender-imported world bounds. The stdlib `audit` reads accessor-local
bounds and ignores node transforms, so an unapplied external GLB can fail floor/height
checks even when the imported scene looks correct. The intake step must apply
transforms before it measures and exports.
## Options
### Option A — extend the procedural generator
Add more variation to `tools/blender_assets.py` rather than importing models:
body builds, height ranges, head/face shapes, hair and facial hair, outfit
layers, and more `CHARACTERS` specs. This keeps a single owned style, needs no
third-party licence review, and stays inside the existing builder/audit flow.
- **Strengths:** lowest integration risk, deterministic, no new dependencies.
- **Limits:** the current face and clothing vocabulary is intentionally simple;
more variants may still read as the same model with a different coat.
- **Best for:** a quick 4–8 variant expansion and filling obvious role gaps.
### Option B — Quaternius CC0 intake with a contract adapter
Use Quaternius meshes as silhouette/outfit donors and adapt them to the project
contract. The visible result is a different body/clothing language from the
current library, so the adapter must also normalise head/hand style or rebuild
those parts.
- **Strengths:** best free outfit/hair variety, animations included, CC0,
already close to game proportions, front axis and floor contact.
- **Work:** weld/decimate to the vertex budget, remap materials to the five
slots, rebuild the armature at the contract rest positions, transfer weights,
repose/normalise proportions, rename the mesh node, export, audit and compare
in the acceptance harness.
- **Main risk:** retargeting a 62-bone T-pose rig to the 13-bone arm-down
contract. Hip/knee rest heights differ substantially (Quaternius upper leg
starts near 0.96 m; the contract hip starts near 0.62 m), so a naive
nearest-bone weight transfer can produce a good still image and bad walk
poses. Prototype one character before committing to a roster.
- **Best for:** a larger, visibly different library if the prototype passes the
portrait and walk gates.
### Option C — MakeHuman/MPFB body-shape pipeline
Generate a range of ages, builds and body shapes from the CC0 MakeHuman base
mesh, then replace the head/hands and dress them with project-owned clothing.
This is the strongest answer to "everyone has the same body", but requires an
offline authoring step, decimation/retopology and a custom rig/weight step. It
should be treated as a later pipeline project, not a quick content drop.
## Decision and prototype outcome
The project owner reviewed this page on 2026-11-03. The decision is:
1. **CC0 is acceptable** for shipped character assets. CC-BY is acceptable only
with attribution in `CREDITS.md`; service assets whose terms restrict
redistribution remain excluded.
2. **Extend the procedural generator** as the primary path, starting with
crowd-variety looks.
3. **Prototype Quaternius** as a secondary source. It is not approved for
shipping yet; the contract-retarget result below is a no-go for the current
skeleton.
4. **Use crowd variety** as the first visual goal, not role-specific costumes.
5. **Evaluate MakeHuman for a later, separate local development pipeline.** It
is not part of the normal build, CI or release flow.
6. **Commit the research previews with attribution.** The CC-BY entries are
listed in `CREDITS.md` and remain development-only.
### Quaternius prototype
The prototype used `Casual_Hoodie.glb`:
- **Geometry budget is not the blocker.** Imported: 12,518 vertices / 6,206
triangles. After welding seam splits: 3,199 vertices / 6,206 triangles. After
a 0.75 decimate: 2,423 vertices / 4,654 triangles, comfortably inside the
library budget.
- **Animation quality is attractive.** The imported Idle and Walk clips read
well at game distance, and the model brings 24 animations.
- **The contract rest pose is the blocker.** The source is a T-pose 62-bone rig
with realistic proportions: upper leg starts near 0.96 m, arm span is about
1.68 m, and the shoulder joint sits near x = 0.153 m. The project contract is
an arm-down 13-bone rig with a hip near 0.62 m, a shoulder near x = 0.27 m and
a hand near z = 0.78 m. Rebuilding the contract armature around this mesh would
either distort the body substantially or require a full proportion warp.
- **Decision:** do not retarget Quaternius to the contract now. Keep it as a
silhouette/style reference and, if a second rig path is ever wanted, prototype
it as its own animation path (its own skeleton, AnimationPlayer clips and
per-model gear offsets) rather than forcing it into `npc_base`. That is a
separate architecture decision, not an asset conversion.
Preview: [`blender_previews/sheet_npc_sources_research.jpg`](blender_previews/sheet_npc_sources_research.jpg).
### Generator crowd prototype
Four proposed crowd specs were built with the existing generator in a temporary
output directory. All four audit clean and stay inside the character budget:
| Prototype | Look | Tris / verts | Gear |
| --- | --- | --- | --- |
| `npc_crowd_a` | slim, bun, sunglasses | 4,644 / 4,356 | sunglasses |
| `npc_crowd_b` | broad, bald, beard | 4,550 / 4,453 | shoulder bag |
| `npc_crowd_c` | average, short hair, long coat | 5,118 / 5,086 | umbrella |
| `npc_crowd_d` | slim, short hair, hood up | 5,258 / 4,907 | cup |
They vary build, head silhouette, hair/beard and outer layer while keeping the
existing face language. They are **not in the shipped catalogue**; production
integration belongs to the roster/runtime work under the unversioned `Character
development` section, after the current release candidate.
Preview: [`blender_previews/sheet_npc_crowd_prototypes.jpg`](blender_previews/sheet_npc_crowd_prototypes.jpg).
### MakeHuman local-pipeline evaluation
The CC0 base mesh is 19,158 vertices / 18,486 polygons (36,972 triangles after
triangulation), with 21,334 UV coordinates but no normals, material groups or
rig. Scaling it to 1.82 m and decimating to 0.35 produced 3,780 vertices / 6,468
triangles, which fits the geometry budget but is a collapse-decimation result,
not production topology.
MakeHuman is therefore **feasible only as a separate local body-shape pipeline**.
The boundary for that future work is:
Intake prototype (2026-11-04): `tmp/npc-research/makehuman_intake.py` scales the
CC0 base to 1.82 m, grounds it, triangulates and decimates to **3,790 verts /
6,444 tris**, then exports `tmp/npc-research/makehuman/makehuman_body.glb` (body
only, one neutral material, UVs present, no rig). The body is the input for the
contract-rig/retopology step; the script and mesh stay under `tmp/` and are never
committed.
Retarget prototype (2026-11-04): `tmp/npc-research/makehuman_retarget.py` rotates
the A-pose arms down (−38°/+38°) and scales them **1.63×** to the contract 0.70 m
arm, welds the split vertices, smooth-shades, adds the 13-bone contract armature
with the project's distance-to-bone `_auto_weights`, paints the five slot regions,
renames the mesh to `npc_base`, and exports `makehuman_retargeted.glb` — **6,444 /
16,000 tris, 7,726 / 16,000 verts**, 14 bones, skinned, arm-down, grounded, five
slots, no contract issues. The proportion warp (1.63× arms) is the same class of
issue that blocked Quaternius; here it is accepted, but the vision review shows
it distorts the deltoid/shoulder, the decimated hands are blocky, and the slot
boundaries are rough — refinement work (arm-scale falloff, project head/hands,
weight tuning) is the next step.
The arm-scale falloff (1.0 at the shoulder tapering to 1.63 at the fingertip) was
added, then the arm threshold was raised to x > 0.22 and a rotation falloff was
added — the deltoid/shoulder junction is now smooth (no spiky triangles or
arm/torso gaps). Remaining visual defects: the decimated MakeHuman head reads
dark/featureless, the hands are flat claw-like, and the feet are detached/spiked
— these are fixed by swapping in the project's `_face`/`_hands_and_shoes`/shoe
parts, the next step.
- MPFB/MakeHuman is installed and run by a contributor, never by CI or the game
build.
- Generated bodies and intermediate exports stay in `tmp/` (gitignored); the
CC0 base mesh is not committed.
- The contributor scales to 1.8–1.9 m, decimates or retopologises to roughly
6–7k triangles, and checks the silhouette against `npc-visual-review.md`.
- The body gets the contract armature, weights, project-owned head/hands,
clothing and gear; it must pass `blender_assets.py audit` and `fit`.
- Only the final CC0-derived GLB and its provenance note are committed. The
application code and any non-CC0 assets stay outside the repository.
This is deferred; it is not part of the current crowd-variety work.
## Provisional NPC roster
The design rule stays: clothes, props and behaviour carry the read; a model must
not label a person as "the threat". The roster therefore adds ordinary adults
across age, build and style, not costumes that telegraph the outcome.
| Look family | Current | Proposed additions | Variation axes |
| --- | --- | --- | --- |
| Everyday | `npc_casual`, `npc_jacket`, `npc_coat`, `npc_hoodie`, `npc_dress` | `npc_crowd_a` (bun/slim), `npc_crowd_b` (bald/broad), `npc_crowd_c` (coat/umbrella), `npc_crowd_d` (hood/cup) | build, height, hair, facial hair, outer layer, accessory |
| Workwear | `npc_vest`, `npc_guard` | later: second hi-vis build, venue-security build | build, height, headwear, badge/radio gear |
| Responder | `npc_firefighter` | later: paramedic/medical responder only if the design supports it | turnout vs medical kit, build, helmet |
| Crowd/LOD | none | optional Kenney blocky or a decimated library variant for distant crowds | silhouette and palette only |
A first target is **12 library characters** (the current eight plus the four
crowd prototypes), not twenty. That is enough to break the clone effect without
doubling the portrait QA matrix. `character_model_for()` becomes a pool per role
family with a deterministic seed choice; the current two-way alternation for
guests, trespassers and threats is the template.
## Implementation plan
The actionable tasks live in `TODO.md` under the `Character development` section. The workstreams
are:
1. **Decision and licence gate.** Complete: ADR-0015, the CC0/CC-BY rule and the
committed preview sheets.
2. **Generator crowd variants.** Complete as a prototype (four specs, clean
audit, review sheet). Production integration is pending the current release candidate.
3. **Quaternius intake.** The contract-retarget prototype is a no-go. Keep the
source as a reference; a second-rig/animation path would need its own ADR.
4. **Roster build and runtime.** Add the four approved specs to `CHARACTERS`,
build and commit their GLBs, update the manifest, and extend the role pools.
5. **QA and integration.** Run the 24-NPC budget, portrait baseline, gear fit,
headless suite and release build. Record provenance and any accepted limits.
Only then update `CHANGELOG.md` and `docs/development-status.md`.
### Adapter notes
- Apply node transforms before measuring or exporting. CesiumMan imports with a
Z-up root and an `Icosphere` helper object; exclude helpers, not just the mesh
names, when measuring.
- Weld duplicate seam vertices before decimating. Quaternius' 12k imported
vertices are mostly UV/normal splits; the underlying surface is much smaller,
but the glTF budget counts the splits after export.
- Decimate to a target below 16,000 tris/verts, then re-unwrap only if the
existing UVs cannot survive the material remap. The current library uses
smart projection at 80° to keep islands large.
- Map materials by semantic role, not by source name. A source "Purple" or
"Worker_Vest" needs an explicit mapping to `cloth`, `pants`, `skin`, `hair` or
`accessory`; do not rely on colour heuristics.
- Keep the face direction unambiguous. The front is Blender −Y; verify it with
a capture, not by guessing from the source file.
- For any future external-rig path, do not silently add a second skeleton format
to `NpcAvatar`. Record the per-model skeleton, animation source and gear
offsets as an explicit contract.
## Review outcome
The project owner answered the review questions on 2026-11-03:
| Question | Answer |
| --- | --- |
| Are third-party CC0 characters acceptable? | Yes. |
| Which direction? | Prototype Quaternius and extend the procedural generator. |
| Which looks first? | Crowd variety. |
| Should MakeHuman be pursued? | Evaluate it for a later, separate local development pipeline. |
| Commit the previews? | Yes, with CC-BY attribution. |
## Downloaded example provenance
Source models were downloaded on 2026-11-03 and kept out of version control. The two
research preview sheets are committed under `docs/blender_previews/`; the CC-BY
attribution is recorded in `CREDITS.md`.
| File | Source | SHA-256 |
| --- | --- | --- |
| `quaternius/Casual_Hoodie.glb` | `raw.githubusercontent.com/LuanDucate/Ducz.CharacterCreator/HEAD/public/models/Male/Casual_Hoodie.glb` (Quaternius CC0) | `f5b722199f7628b24550989af4230f65d5230727df9c326fd2b3e360effe5249` |
| `quaternius/Worker.glb` | same mirror, `Male/Worker.glb` | `8d0f454336c2effc5c08f63a177af3a82d1a7af025ca9588b40e4c90c4703534` |
| `quaternius/Casual.glb` | same mirror, `Female/Casual.glb` | `bc2d84fba249ebaa0629cb9d133ad37234196e4b8004aa205a6397abed04e05c` |
| `kenney_blocky_characters.zip` | `kenney.nl/assets/blocky-characters` | `5e123859aa0c1598342b600c6db197024a1d63eb9ec531398b310725f589887e` |
| `khronos/CesiumMan.glb` | `github.com/KhronosGroup/glTF-Sample-Assets` | `b7001eaeea8254bd44773bcd247e78696d94169388fbb2a1800fc69434e777d9` |
| `khronos/RiggedFigure.glb` | same repository | `d6be85417d3e256861ee733eea6916093a7af7c79c16366181fd8abcaeb38cf5` |
| `makehuman/base.obj` | `github.com/makehumancommunity/makehuman/blob/master/makehuman/data/3dobjs/base.obj` | `8e761e6624b8f54536409135d1636da63b32486a90d4897f84e121d144f6fb4c` |
Source terms:
- Quaternius packs are CC0 1.0 according to the pack page and the mirror's
credits. The mirror is a convenience download; the original pack page is
<https://quaternius.com/packs/ultimatemodularcharacters.html>.
- Kenney Blocky Characters 2.0 is CC0 1.0; the pack's `License.txt` is included
in the download.
- CesiumMan and RiggedFigure are CC BY 4.0, © 2017 Cesium. If any derivative is
committed or shipped, the attribution must be added to `CREDITS.md`.
- MakeHuman core assets are CC0 per `LICENSE.ASSETS.md`; the application source
is AGPL and is not bundled.