feat(paint-slots): paintable slots on the procedural shelf (phase 5)
Proves the unified (nodeId, slotId) slot contract on a procedural generator, beyond items and walls. A shelf now exposes three paintable slots — shelves / frame / back — painted through the same PaintCapability dispatch and the same node.slots: Record<slotId, MaterialRef> shape items use. Foundation (shared, reusable by future procedural kinds): - core: SlotDeclaration type + capabilities.slots(node) registry declaration; GeometryContext gains `materials` so a pure builder can resolve scene:<id> slot refs without importing useScene. - viewer GeometrySystem: threads the scene material library into every builder ctx, and re-dirties (bypassing the geometryKey skip) any geometry node that references a scene material when that material changes — so editing a custom colour propagates to every shelf using it, matching items. Shelf: - schema: slots: Record<string, MaterialRef> (mirrors ItemNode). - geometry: per-slot material resolution (slot override -> legacy whole-shelf -> declared default colour); every mesh stamped with userData.slotId; DEFAULT_SHELF_MATERIAL retired (declared default gives identical off-white). - paint.ts: PaintCapability (resolveRole from userData.slotId, scene-material commit for one-off colours, preview restricted to __fromGeometry meshes so hosted items aren't ghosted, getEffectiveMaterial incl. legacy fallback). - definition: paint + slots capabilities; slots folded into geometryKey. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
89a7232a67
commit
101341d98d
@@ -86,6 +86,13 @@ export const ShelfNode = BaseNode.extend({
|
||||
// unchanged once `'shelf'` is added to `MaterialTarget`.
|
||||
material: MaterialSchema.optional(),
|
||||
materialPreset: z.string().optional(),
|
||||
|
||||
// Per-slot material overrides, mirroring `ItemNode.slots`. Key = slot id
|
||||
// (`shelves` / `frame` / `back`, see `shelfSlots`), value = a `MaterialRef`
|
||||
// string (`library:<id>` or `scene:<id>`). Absent slot = fall back to the
|
||||
// legacy whole-shelf `material` / `materialPreset`, then the registry slot
|
||||
// default. A dangling ref renders the default (never blocks).
|
||||
slots: z.record(z.string(), z.string()).optional(),
|
||||
})
|
||||
|
||||
export type ShelfNode = z.infer<typeof ShelfNode>
|
||||
|
||||
Reference in New Issue
Block a user