93 lines
6.5 KiB
Markdown
93 lines
6.5 KiB
Markdown
# 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.
|
|
|
|
Primitives, static renderers, and skinned renderers need the same Unity-like authoring contract
|
|
without sharing their geometry or hydration components. A project material must be reusable, an
|
|
instance must express explicit overrides over one base material, and every renderable surface 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. Schema-driven input storage and processed Texture bindings are defined by
|
|
[ADR 0046](0046-schema-driven-material-inputs-and-processed-textures.md).
|
|
- `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.
|
|
- `Primitive`, `StaticMeshRenderer`, and `SkinnedMeshRenderer` own `MaterialSlot` values. Mesh
|
|
renderers use a `MaterialSlotSet`; a primitive owns one stable `surface` slot with ID
|
|
`slot:primitive:surface`. They do not share a geometry component or hydration path. Each saved
|
|
slot has a stable ID, a presentation name, and only an optional actor/prefab `material`
|
|
assignment. Model defaults and imported source materials remain asset-owned in model import
|
|
metadata and runtime manifests. Clearing an actor assignment returns resolution to those weaker
|
|
model-owned layers.
|
|
- 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 a selected
|
|
primitive surface or every material slot on each selected static/skinned renderer. One inspector
|
|
widget provides drag/drop, Browse/Locate/Clear, inherited status, expandable shared parameters,
|
|
exact-slot extraction, and orphan diagnostics.
|
|
- `HydratedMaterialSlotBinding` identifies each runtime draw's owner and exact slot.
|
|
`HydratedMaterialSelection` carries Project Actor, Project Model, Imported Source, or Inherit
|
|
ownership without copying model defaults into the scene. 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.
|
|
- Model assets may provide a project Material/Instance default between their imported source and a
|
|
scene/prefab assignment. The terminal fallback chain and immutable DefaultGrid contract are owned
|
|
by [ADR 0045](0045-content-workspace-and-material-fallback-contract.md).
|
|
- Effective precedence is runtime MaterialPropertyBlock, scene/prefab assignment, model-asset
|
|
default, imported source, project default, then built-in DefaultGrid. A configured but broken
|
|
authored reference uses the fallback with diagnostics instead of silently exposing another
|
|
authored layer.
|
|
- Repository scenes use schema v6, where renderable slots persist actor/prefab assignments only.
|
|
This pre-production project updates its fixtures directly. The explicit project upgrader remains
|
|
available for legacy primitive `MaterialDesc`, mesh-wide descriptors, and per-slot
|
|
`MaterialOverride` values: it deduplicates canonical descriptors as deterministic project
|
|
Materials under `assets/materials/migrated/` and rewrites project references transactionally.
|
|
Ordinary loading remains non-writing.
|
|
|
|
## Consequences
|
|
|
|
- Primitives, static renderers, and skinned renderers have one 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.
|
|
- Component-form `MaterialDesc` is restricted to the brush fallback compatibility path. It is not
|
|
addable to primitives or mesh renderers, does not override their slots, and never causes a
|
|
primitive-local runtime material allocation. Unmigrated actor material components produce an
|
|
upgrade diagnostic and visible fallback.
|
|
- `MaterialPropertyBlocks` define the runtime-only schema and persistence exclusion. Runtime
|
|
application clones and caches the resolved Standard or Surface material per owner/slot, above
|
|
direct-base Material Instance values. An invalid block leaves the resolved base visible and emits
|
|
a deduplicated diagnostic instead of applying a partial override. Promotion to a reusable
|
|
instance uses an explicit target-path review and source/target fingerprint guards, assigns one
|
|
exact slot through history, and clears the block only after the file, registry, and scene
|
|
transaction succeeds. It may never use DefaultGrid as its base. The editor registers the
|
|
component with reflection for trusted local BRP runtime injection and native promotion testing;
|
|
that registration does not add it to authoring persistence or the Inspector component registry.
|
|
- Material graph authoring, nested instances, and blended/transmissive materials are outside this
|
|
decision.
|
|
|
|
Scene schema v6 stores only the actor/prefab assignment in a renderable slot. Model defaults and
|
|
imported source selections remain model-owned, and hydration carries their origin through
|
|
`HydratedMaterialSelection`. Live project resolution is owned solely by the overlay/cache boundary
|
|
in [ADR 0047](0047-editor-authored-asset-documents.md); shared slot presentation follows
|
|
[ADR 0048](0048-modular-editor-composition-and-debt-ratchet.md).
|