# ADR 0033: Dedicated Skinned Mesh Renderer ## Status Accepted ## Context The normalized model artifact originally exposed every imported primitive to `StaticMeshRenderer`, including primitives with joint indices and weights. Whole-model static placement could therefore detach rigged geometry from its joints and render it in bind pose. The animation workflow avoided individual skinned-part placement, but it still represented animated geometry as generic `ImportedModel + ModelRef`, leaving no authoring boundary between an arbitrary source scene and geometry that requires a skeleton hierarchy. Static, generic scene-instance, and skinned rendering have different ownership rules. Static parts can be flattened into independent slots and triangle-mesh colliders. A skinned mesh must retain the source joints, inverse bind poses, animation player, and hierarchy. A generic scene instance may contain cameras, lights, or other source data and does not imply an animation contract. ## Decision Rigged geometry uses `ActorKind::SkinnedMesh + SkinnedMeshRenderer`. The renderer stores stable asset identity, cached source path, and source scene index. Its dedicated hydration system creates a `HydratedSkinnedMeshRoot` and instantiates the complete source hierarchy so Bevy owns the runtime `SkinnedMesh`, joints, inverse bind poses, and animation-player placement. The animation runtime binds only to this dedicated root. `AnimationControllerDesc` is valid only beside a `SkinnedMeshRenderer`. Renderer hydration itself remains animation-agnostic. The animation adapter captures the imported hierarchy baseline when that root becomes ready and optionally drives the explicit default clip recorded by animation-manifest schema v3. Without a controller, the dedicated renderer remains at the captured imported pose. Static-mesh artifact schema v2 marks every part as skinned or unskinned. Static renderer creation filters out skinned parts and all geometry from an animated source; static hydration enforces the same rule even if a scene contains a hand-authored slot. Automatic renderable-model placement chooses `SkinnedMeshRenderer` when any source part is skin-bound or the source contains animation, and otherwise chooses `StaticMeshRenderer`. Placing a skinned or animated subasset places its owning model through the dedicated renderer; it never creates a detached static slot. `ImportedModel + ModelRef` remains the explicit generic scene-instance path. It is not the animation renderer. Scene schema v3 migrates v2 entities containing both `ModelRef` and `AnimationControllerDesc` to `SkinnedMeshRenderer + ActorKind::SkinnedMesh`; unrelated imported scene instances remain unchanged. Automatic triangle-mesh collider generation applies only to static renderers. Animated collision must be authored explicitly with stable gameplay shapes or hitboxes rather than a deforming render surface. ## Consequences - Static renderer slots can no longer silently accept newly imported skinned or animated geometry. - Rigged models retain the exact hierarchy Bevy needs for skinning and animation playback. - Generic scene instances, static meshes, and skinned meshes have distinct persisted components, actor kinds, hydration markers, validation, and editor presentation. - A source containing both rigid and skinned nodes is placed as one skinned actor so the imported hierarchy remains coherent; splitting it requires separate source assets. - FBX skinning and animation remain blocked by ADR 0031 until the FBX loader can build the same runtime hierarchy and clip contract as glTF.