feature/gltf-pipeline-v2 #18

Merged
NotEvil merged 19 commits from feature/gltf-pipeline-v2 into develop 2026-04-17 02:07:45 +00:00

19 Commits

Author SHA1 Message Date
NotEvil
f37600783a docs(artist-guide): update for Full/FullHead conventions, custom bones, frame 0 behavior, validation tools 2026-04-17 04:06:33 +02:00
NotEvil
168c0675bb fix(animation): fix variant caching bug in multi-item path — same fix as single-item 2026-04-17 04:06:33 +02:00
NotEvil
a3287b7db8 fix(animation): variant randomness no longer permanently cached + fix FullHead false-positive
Two fixes from the animation audit:

1. Variant cache key now includes the resolved animation name (e.g., Struggle.2).
   Previously, the cache key only used context+parts, so the first random variant
   pick was reused forever. Now each variant gets its own cache entry, and a fresh
   random pick happens each time the context changes.

2. FullHead check changed from contains("Head") to startsWith("gltf_FullHead")
   to prevent false positives on names like FullOverhead or FullAhead.
2026-04-17 04:06:33 +02:00
NotEvil
e56e6dd551 fix(animation): extend resolver fallback chain to include FullHead variants
The resolver now tries FullHead* before Full* at each fallback step.
Example: FullHeadStruggle → FullStruggle → Struggle → FullHeadIdle → FullIdle → Idle

Previously FullHead* names were dead — the resolver never constructed them,
so animations named FullHeadStruggle were unreachable.
2026-04-17 04:06:33 +02:00
NotEvil
806a1e732d feat(animation): add FullHead convention — opt-in head animation in Full animations
FullStruggle, FullWalk etc. animate body+legs but preserve head tracking.
FullHeadStruggle, FullHeadWalk etc. also animate the head.
The 'Head' keyword in the animation name is the opt-in signal.
2026-04-17 04:06:33 +02:00
NotEvil
3d57d83a5b fix(animation): preserve head tracking in Full animations — head never enabled as free bone 2026-04-17 04:06:33 +02:00
NotEvil
229fc66340 fix(animation): free bones only enabled for Full-prefixed animations
Previously, any GLB with keyframes on free bones would animate them,
even for standard animations like Idle. This caused accidental bone
hijacking — e.g., handcuffs freezing the player's head because the
artist keyframed all bones in Blender.

Now the Full prefix (FullIdle, FullStruggle, FullWalk) is enforced:
only Full-prefixed animations can animate free bones. Standard
animations (Idle, Struggle, Walk) only animate owned bones.

This aligns the code with the documented convention in ARTIST_GUIDE.md.
2026-04-17 04:06:33 +02:00
NotEvil
b0766fecc6 feat(validation): show toast notification when GLB errors are detected on reload 2026-04-17 04:06:33 +02:00
NotEvil
9dfd2d1724 fix(validation): reduce log spam + add OOM guard + check correct mesh for WEIGHTS_0
- DataDrivenItemParser: downgrade animation_bones absence log from INFO to
  DEBUG (was spamming for every item without the optional field)
- GlbValidator: read 12-byte GLB header first and reject files declaring
  totalLength > 50 MB before allocating (prevents OOM on malformed GLBs)
- GlbValidator: WEIGHTS_0 check now uses the same mesh selection logic as
  GlbParser (prefer "Item", fallback to last non-Player) instead of
  blindly checking the first mesh
2026-04-17 04:06:33 +02:00
NotEvil
3f6e04edb0 feat(validation): add /tiedup validate client command for GLB diagnostics 2026-04-17 04:06:33 +02:00
NotEvil
ca4cbcad12 feat(validation): add GlbValidationReloadListener — validates GLBs on resource reload 2026-04-17 04:06:33 +02:00
NotEvil
17269f51f8 perf(gltf): add skinning cache — skip re-skinning when pose is unchanged 2026-04-17 04:06:33 +02:00
NotEvil
c0c53f9504 feat(validation): add GlbValidator — structural validation from JSON chunk 2026-04-17 04:06:33 +02:00
NotEvil
eb759fefff feat(validation): add diagnostic data model — GlbDiagnostic, GlbValidationResult, GlbDiagnosticRegistry 2026-04-17 04:06:33 +02:00
NotEvil
8af58b5dd5 feat(gltf): accept custom bones — remove 11-bone filter, simplify joint remap 2026-04-17 04:06:33 +02:00
NotEvil
d7f8bf6c72 fix(gltf): strip armature prefix from bone names in FurnitureGlbParser
Strip pipe-delimited armature prefixes (e.g., "MyRig|body" -> "body")
from bone names in parseSeatSkeleton and remapSeatAnimations, matching
the existing stripping already present in GlbParser. Without this,
isKnownBone checks fail when Blender exports prefixed bone names.
2026-04-17 04:06:33 +02:00
NotEvil
6dad447c05 fix(gltf): select mesh by 'Item' name convention with fallback 2026-04-17 04:06:33 +02:00
NotEvil
7ef85b4e92 feat(datadriven): make animation_bones optional — absent means permissive 2026-04-17 04:06:33 +02:00
NotEvil
ad74d320be feat(gltf): add suggestBoneName + knownBoneNames helpers to GltfBoneMapper 2026-04-17 04:06:33 +02:00