# ADR 0035: Shared Material Assets and Renderer Slots ## Status Accepted ## Context Renderer material authoring previously mixed copied `MaterialDesc` values, imported source-material defaults, and static-mesh draw data. That made a renderer's ownership unclear, encouraged material copies per actor, and left rigged geometry without the same assignment model as static geometry. Matching imported materials by a display name was also unsafe: names are neither unique nor stable when a model is reimported. Static and skinned renderers need the same Unity-like authoring contract without sharing their mesh or hydration components. A project material must be reusable, an instance must express explicit overrides over one base material, and each renderer must expose stable material slots independently from its draw or skeleton representation. ## Decision - Project materials use versioned `MaterialAsset` documents. A `MaterialInstanceAsset` references one direct base `MaterialAsset` and stores parameter and texture overrides. Nested material instances are invalid. - `MaterialRef` is the assignment type. Its registry UUID and subasset ID are authoritative; `source_path` is a cached loading hint that validation or migration may repair after a move. - `StaticMeshRenderer` and `SkinnedMeshRenderer` each own a `RendererMaterialSet`. They do not share a mesh component or hydration path. Each `RendererMaterialSlot` has a stable ID, a presentation name, a read-only imported `source_material`, and an optional explicit `material` assignment. Explicit assignment wins; clearing it returns to the imported source material. - Static draw parts and imported skinned-hierarchy draws bind to material slots by stable IDs. Reimport reconciles by ID only. An explicit assignment whose source slot disappears is retained as an `OrphanedMaterialAssignment`; the editor never silently reconnects it by display name. - Asset Browser **Apply Material** assigns a shared material or material instance to every material slot on each selected static or skinned renderer. Renderer inspectors provide per-slot Browse/Select/Locate/Clear controls and expose orphan warnings. - `HydratedRendererMaterialBinding` identifies each runtime draw's owner, slot, and effective material. A skinned renderer material-only edit patches the instantiated hierarchy without reloading its source hierarchy or joints. - `MaterialPropertyBlocks` are runtime-only, per-slot overrides. They do not mutate a shared asset and are excluded from scene and prefab persistence. Persisting a reusable variation requires an explicit material instance. - Scene schema v4 migrates legacy renderer assignments into `RendererMaterialSet`. Project material, material-instance, shader-schema, and scene rewrites are performed by the explicit transactional `cargo upgrade-project` command; ordinary loading remains read-only. ## Consequences - Static and skinned renderers have a consistent material-slot workflow while retaining distinct geometry ownership and hydration behavior. - An assignment remains a reference rather than a copied material value, so editing or replacing a shared asset does not create actor-local material copies. Material/base/schema/evaluator dependency revisions update the shared runtime handle in place across all assigned slots. - Source reimport cannot silently move an override to the wrong draw. Orphans require an explicit user decision to reassign or discard. - Legacy primitive/actor `MaterialDesc` paths remain compatibility paths until their consumers move to renderer slots; new mesh-renderer work uses shared references. - Runtime property blocks have no editor promotion workflow yet. - Material graph authoring, nested instances, blended/transmissive materials, and a generalized per-renderer property-block inspector are outside this decision.