# Renderer, Material, and Component Foundation Evaluation Date: 2026-07-11 Branch: `codex/renderer-material-component-foundation` This record captures implementation evidence for the dedicated skinned renderer, shared Material and Material Instance workflow, registry-driven authoring components, and custom Surface contract. The permanent design contracts live in [ADR 0033](../../../adr/0033-dedicated-skinned-mesh-renderer.md), [ADR 0034](../../../adr/0034-registry-driven-authoring-components.md), [ADR 0035](../../../adr/0035-shared-material-assets-and-renderer-slots.md), and [ADR 0036](../../../adr/0036-surface-abi-and-solari-parity.md). The user workflow lives in the [material-system guide](../../material-system.md). ## Live editor evidence All images below are native Wayland captures from the running editor. The temporary actors and assignments used for visual verification were not saved into the authored scene. ### Dedicated skinned renderer and stable material slots ![RobotExpressive in its explicit Standing edit pose with a dedicated Skinned Mesh Renderer and stable named material slots](skinned-mesh-renderer-material-slots.png) The actor is hydrated through `SkinnedMeshRenderer`, not `StaticMeshRenderer`. Its imported hierarchy remains intact, the configured default Standing clip is sampled in edit mode, and every draw binding exposes a stable slot ID plus source-material fallback. ### Shared custom Surface assigned through a Material Instance ![Custom Surface Material Instance assigned across the skinned renderer slots](custom-surface-on-skinned-renderer.png) Applying `surface_tint_instance` updates the existing hydrated draw bindings in place. The visible blue Surface evaluation does not respawn the hierarchy or reset the sampled pose. ### Sparse Material Instance authoring ![Material Instance editor showing sparse shader and texture overrides](material-instance-sparse-overrides.png) Instances reference one direct base Material. Unchecked values inherit; selecting or dropping a texture creates only that override, and **Clear** removes the binding so inheritance resumes. ### Registry-driven component composition ![One actor composed from Static Mesh Renderer and Light components](mesh-light-component-composition.png) The actor combines renderer and light components without an `ActorKind` exclusivity rule. The registry owns add/remove/reset/copy/paste/history/persistence behavior; `ActorKind` remains a derived compatibility and presentation hint. ### Larger material thumbnail previews ![Material thumbnails with tightly framed sphere previews](larger-material-thumbnail-previews.png) Material spheres now fill roughly 78-81% of the preview while retaining edge padding. Model and mesh-subasset framing is unchanged. Additional captures: - [Material editor and Create Instance action](material-editor-create-instance.png) - [Registry-driven Add Component categories](component-registry-add-menu.png) ## Acceptance results | Area | Result | Evidence | |------|--------|----------| | Dedicated static/skinned ownership | Pass | Separate authored/hydrated renderer types; rigged/animated manifests are rejected by static hydration | | Pose lifecycle | Pass | Explicit stable default clip; transform and morph-weight baseline restore on stop, controller removal, preview reset, and PIE exit | | Renderer material slots | Pass | Stable named IDs, imported fallback, explicit assignment, orphan retention, and in-place material patching | | Material assets/instances | Pass | Versioned shared assets, direct-base sparse instances, transient property blocks, Apply/Revert/Create Instance | | Texture parameters | Pass | Project-wide picker includes imported texture subassets; base/schema/stored/instance slots share browse, drop, reject, and clear behavior | | Component lifecycle | Pass | Stable registry IDs, reflected transactions/history, persistence allowlist, independent active state, and compatible composition | | Surface raster/Solari semantics | Pass for eligible geometry | Shared ABI packing, evaluator, textures, normal/emissive/unlit values, and cutout predicate | | Project migration | Pass | Transactional schema-v4 upgrader, backup/rollback path, canonical slots and active-state split; second dry run is empty | | Project validation | Pass | 59 dependencies, 5 non-blocking findings, 0 blocking errors | ## Automated verification | Command/suite | Result | |---------------|--------| | `cargo test -p shared --offline` | 84 passed | | `cargo test -p scene --offline` | 64 passed, 1 ignored migration fixture | | `cargo test -p game --lib --offline` | 37 passed | | `cargo test -p game_hot --lib --offline` | 16 passed | | `cargo test -p editor --lib --offline` | 171 passed | | `cargo test -p blacksite_surface --offline` | 3 passed plus doc tests | | `cargo test -p bevy_solari --lib --offline` | 1 passed | | `cargo --locked --offline validate-levels` | 0 blocking errors | | `cargo --offline upgrade-project --project . --json` | No pending changes or warnings | | `cargo fmt --all -- --check` / `git diff --check` | Pass | ## Deliberate boundary Bevy 0.19's maintained Solari path does not expose dynamic post-morph/post-skin vertex buffers and BLAS updates. Skinned and morph-deformed actors therefore remain visible in raster but are omitted from ray tracing with an actionable diagnostic. This is safer than tracing a stale bind-pose ghost; dynamic deformed BLAS support remains future work and the corresponding Gitea acceptance item stays open.