Skip to content

viewer: give bake replace renderers the published graph - #1011

Draft
AxiomeCG wants to merge 1 commit into
pascalorg:mainfrom
AxiomeCG:fix/glb-replace-renderer-resolve
Draft

AxiomeCG wants to merge 1 commit into
pascalorg:mainfrom
AxiomeCG:fix/glb-replace-renderer-resolve

Conversation

@AxiomeCG

@AxiomeCG AxiomeCG commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

For a user: in a published project's baked viewer, ground that a plugin draws from its site (painted surface, grass) now shows up. Before, the lot was an empty grey plane in the baked viewer, while the editor showed the grass.

Why it was missing: a kind with bake: 'replace' has its baked mesh hidden by GlbScene, and is re-rendered live through its bakeReplaceRenderer. That renderer only received { nodes }. The baked viewer never loads the scene graph into useScene, so a renderer that builds from its parent (a ground surface needs its site's polygon and terrain) found no parent and drew an empty group.

What changes:

  • BakeReplaceRenderer's component takes an optional resolve, with the same signature as GeometryContext.resolve.
  • GlbScene takes sceneNodes (the graph the artifact was baked from), builds one stable resolver, and GlbReplaceInstances passes it to every replace renderer. It's stable because GlbReplaceInstances is memoized.
  • wiki/architecture/node-definitions.md → Bake policy documents the prop.

Compatibility: no schema change, so saved scenes and existing bakes load as before (E-003). A renderer that ignores resolve behaves exactly as today, and a host without the graph just omits it. Plugins keep loading (E-010).

Test (T-001): one repro in the existing plugin-dispatch.test.tsx. It mounts a replace renderer with an empty useScene and checks that it resolves its parent through resolve.

How to test

How to see it, once a host passes sceneNodes and the plugin reads resolve (merge order below):

  1. Publish a project whose site has a painted ground surface and ground cover (Environment plugin), and let it bake.
  2. Open it in the baked viewer (the lod0.glb path): the grass is drawn. Before this change, only the lot outline showed, on grey.
  3. Open the same project in the editor: unchanged. Grass shows there before and after.

Checks run on this branch:

  • bun test packages/viewer/src/components/viewer/plugin-dispatch.test.tsx: 6 pass. Full viewer suite (bun test src in packages/viewer): 510 pass.
  • bun run check (Biome): exit 0, warnings only. turbo run check-types --filter=[132c029b] (core, viewer): 2/2 successful.
  • End to end in the hosted app, which publishes, bakes locally and opens the baked viewer: E2E_BASE_URL=http://localhost:3106 dotenv -e .env.local -- playwright test e2e/community/baked-viewer-environment-terrain.spec.ts.
    • Before the fix: 1 failed, with environment:surface-material and environment:ground-cover both missing from the baked viewer.
    • With this PR, the plugin PR and the host change: 1 passed (53.5s; plugin fetched at its published commit ed62989, submodule at dd4f780).

Merge order

  1. This PR (pascalorg/editor).
  2. fix: baked-viewer replace renderers build from the host's resolver AxiomeCG/environment#1: the Environment plugin's four replace renderers read resolve, falling back to useScene.
  3. The hosted viewer change (pascalorg/private-editor): passes the published graph as sceneNodes, and bumps this submodule and the plugin ref.

If this PR is squash-merged, the host's submodule pointer (dd4f780) must be re-bumped to the squash commit before step 3.

Screenshots / screen recording

Local before/after captures, not committed (to be dragged in):

  • before-baked-viewer.png: baked viewer, the lot outline on a grey, empty ground.
  • after-baked-viewer.png: the same view, the painted grass surface fills the lot.
  • before-editor-live.png / after-editor-live.png: the editor's live view, identical, grass in both.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

🤖 Generated with Claude Code

…ed graph

The baked viewer never loads the scene graph into useScene, so a
bakeReplaceRenderer that builds from its parent (a ground surface needs its
site's polygon and terrain) found nothing and drew an empty group, while
GlbScene had already hidden the baked mesh: the kind vanished.

BakeReplaceRenderer's component now takes an optional `resolve`, the same
signature as GeometryContext.resolve. GlbScene takes the published graph's
nodes (`sceneNodes`), builds one stable resolver and GlbReplaceInstances
hands it to every renderer. Renderers that ignore it are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pascal

pascal Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

What this changes

Nodes with bake: 'replace' have their baked mesh hidden by GlbScene and get re-rendered live through their bakeReplaceRenderer, which until now only received { nodes }. Since the baked viewer never loads the scene graph into useScene, a renderer that builds from other nodes (a ground surface reading its parent site's polygon) found nothing and drew an empty group. This PR threads an optional resolve prop, with the same signature as GeometryContext.resolve, from GlbScene down through GlbReplaceInstances to every replace renderer. GlbScene builds it from a new optional sceneNodes prop, memoized so the memoized GlbReplaceInstances subtree keeps short-circuiting on camera moves. Nothing in this repo passes sceneNodes yet; per the description, the plugin and the hosted viewer land after this one.

File Change What changed
packages/core/src/registry/types.ts modified BakeReplaceRenderer's component props gain optional resolve, plus a doc comment on why
packages/viewer/src/components/viewer/glb-replace-instances.tsx modified New ReplaceRendererProps type for the lazy cache; resolve accepted as a prop and passed to each portaled renderer
packages/viewer/src/components/viewer/glb-scene.tsx modified New optional sceneNodes prop, a memoized resolveNode built from it, handed to GlbReplaceInstances
packages/viewer/src/components/viewer/plugin-dispatch.test.tsx modified Test fixture renderer now reads resolve; new test mounts replace renderers with an empty useScene and asserts the parent resolves
wiki/architecture/node-definitions.md modified Bake policy section documents resolve and the empty store in the baked viewer

Start with glb-scene.tsx for how the resolver is built and kept stable, then the new test in plugin-dispatch.test.tsx for the behaviour it is meant to guarantee.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant