Render: integrated workspace, preview image jobs and opaque PBR (#1046)
Lands the Render workspace (R3), bounded preview-image jobs with cancellation and atomic PNG export (R4a), and opaque PBR looks as one change. - Render workspace: an overlay-free product preview on the installed GPU device. Returning to Design restores its camera. render.scene and render.settings.apply are added. - Image jobs: render.preflight/start/status/cancel/export run bounded CPU preview jobs. Export captures the job, overwrite choice and document, and checks the caller's file scope before writing. - Opaque PBR: looks can carry metal fraction, roughness and dielectric IOR. The product viewport and preview PNG use them; geometry and mass are unchanged. The v1 catalog export refuses PBR rather than dropping it. - Render setups and PBR looks are saved under design format 21. Labels are in en/de/ja/ko/zh/ru. Supersedes #706 and #800. Refs #620, #673, #756, #1004. Contributed by @0xlillith. Co-authored-by: lillith <[email protected]>
This commit is contained in:
1 parent
e4ef6cfe29
commit
a261aaa841
70 files changed
+5860
-101
No files matched your search
@@ -24,6 +24,14 @@ unless **Replace existing library file** is explicitly selected. Export is a sna
|
||||
the design definitions, not an automatic merge with entries in an existing catalog.
|
||||
Editing a copied look/material uses the normal commands and does not edit its source.
|
||||
|
||||
The combined render integration uses main's merged #1127 asynchronous picker.
|
||||
Model edits and tab changes wait for its answer while drawing/resizing continues.
|
||||
Overwrite consent is captured before opening; export snapshots the design when
|
||||
`library.export` runs after choosing the destination. Document identity is checked,
|
||||
and an old library answer cannot replace another Appearance editor. Cancellation
|
||||
does not read or write a library. The earlier #1114 candidate is no longer a runtime
|
||||
dependency of the refreshed consumer.
|
||||
|
||||
The desktop panel applies appearances. Physical-material import/assignment is available
|
||||
through `library.apply`; imported definitions also appear in the existing material list.
|
||||
The browser can export a library through the existing download service. Interactive local
|
||||
@@ -123,3 +131,8 @@ The copied design remains independent after successful assignment, undo/redo and
|
||||
reopening. This library has no dependency on the pending render stack and does not change
|
||||
the document format. User-visible evidence and actual native/wasm results are recorded in the PR;
|
||||
geometry/robustness checks remain separate and no reference baseline is weakened.
|
||||
|
||||
The opaque PBR consumer makes v1 export fail explicitly when any body, component,
|
||||
face or material-default appearance contains PBR parameters. Neither overwrite consent
|
||||
nor an existing output file permits a lossy downgrade; use the richer transport
|
||||
when available. Scalar catalog imports still select the scalar response explicitly.
|
||||
@@ -73,7 +73,7 @@ is available. Missing or incompatible pictures use the usual thumbnail generatio
|
||||
The default `cache: false` omits the portable thumbnail too.
|
||||
The repository's `docs/design-container.md` describes limits and encoding details.
|
||||
|
||||
Current designs use format 21, including design properties, named render setups, thin extrudes, sheet metal perforation fields, associative editable sketch patterns, editable associative offset-distance dimensions, the style of sketch text (bold, italic, alignment, spacing and the curve it follows), face anchors on a piece of a face by its own name, sheet metal rims and bend ends, bend edges for Convert to Sheet Metal, the tear corner relief of sheet metal rules, the names of projected edges, physical materials defined in the design, the font of sketch text, sheet metal corner relief shapes, sketch face anchors on cylinder caps, sketch axis
|
||||
Current designs use format 21, including design properties, named render setups, opaque PBR looks, thin extrudes, sheet metal perforation fields, associative editable sketch patterns, editable associative offset-distance dimensions, the style of sketch text (bold, italic, alignment, spacing and the curve it follows), face anchors on a piece of a face by its own name, sheet metal rims and bend ends, bend edges for Convert to Sheet Metal, the tear corner relief of sheet metal rules, the names of projected edges, physical materials defined in the design, the font of sketch text, sheet metal corner relief shapes, sketch face anchors on cylinder caps, sketch axis
|
||||
links, persistent
|
||||
names from exact face history, exact planar shell-face anchors, persistent analytic offset boundary roles and
|
||||
association profile references, durable sketch face anchors, periodic fit splines and versioned
|
||||
|
||||
@@ -71,6 +71,10 @@ MCP server sends its folders this way on every request ([mcp.md](mcp.md)). Witho
|
||||
|
||||
Commands never open dialogs when run this way. `solidcraft-cli commands` prints the same registry.
|
||||
|
||||
Product images use `render.preflight/start/status/cancel/export` through `engine.execute`.
|
||||
Keep the returned session-scoped job ID for subsequent calls; status does not block or
|
||||
render. See [render jobs](render-jobs.md) for capture, cancellation and export semantics.
|
||||
|
||||
While an interactive native file picker is open, `ui.inspect` reports `file_picker_busy: true`.
|
||||
Engine commands, `ui.file` and `ui.home` requests wait for its answer; window resizing,
|
||||
inspection and screenshots remain available.
|
||||
@@ -97,7 +101,7 @@ unit regardless of the design, pass an expression with one: `"value": "50 mm"` o
|
||||
|
||||
| Method | Params | Notes |
|
||||
|---|---|---|
|
||||
| `ui.inspect` | — | UI state, what is hidden (`visibility`: bodies, origin and construction items, sketches, joints (`hidden_joints`, joint ids); set it with the `browser.visibility` command), viewport rect, camera, active tool/dialog, the status line (`status`: text and whether it is an error), hover, renderer, timings, live preview (`active`, `busy`, `error`, `ms`, bodies it stands in for) |
|
||||
| `ui.inspect` | — | UI state, pending file picker (`file_picker_busy`; compatibility alias `file_dialog_open`, no path), what is hidden (`visibility`: bodies, origin and construction items, sketches, joints (`hidden_joints`, joint ids); set it with the `browser.visibility` command), viewport rect, camera, active tool/dialog, the status line (`status`: text and whether it is an error), hover, renderer, timings, live preview (`active`, `busy`, `error`, `ms`, bodies it stands in for) |
|
||||
| `ui.set` | any `UiState` field (`tab`, `perspective`, `showGrid`, `dark`, …), and `hiddenBodies`, `hiddenOrigin`, `hiddenSketches`, `shownSketches`, `hiddenJoints` (whole lists; `browser.visibility` changes single items) | `dark: false` switches to the light theme |
|
||||
| `ui.view` | `view`: `front`, `back`, `top`, `bottom`, `left`, `right`, `iso`, `home`, `fit`; `animate` (bool, default false: snap) | |
|
||||
| `ui.start` | `command` | like a toolbar click: starts the sketch tool or dialog for the command |
|
||||
@@ -134,6 +138,10 @@ unit regardless of the design, pass an expression with one: `"value": "50 mm"` o
|
||||
| `ui.resize` | `width`, `height` | |
|
||||
| `app.quit` | `force?` (quit without asking about unsaved designs) | `quitting` (the window closes), `asking` (a "save changes?" prompt is open) |
|
||||
|
||||
`ui.workspace {workspace?: "design" | "render"}` reads or changes the active workspace.
|
||||
Entering Render requires finishing the current modelling operation; returning restores
|
||||
the Design camera. See [the integrated Render workspace](render-workspace.md).
|
||||
|
||||
## MCP
|
||||
|
||||
`solidcraft-cli mcp --connect 127.0.0.1:PORT` bridges an MCP client (an AI agent) to this
|
||||
|
||||
+9
-2
@@ -1,6 +1,6 @@
|
||||
# Features
|
||||
|
||||
SolidCraft's commands by area, generated by `cargo xtask features` from the command registry (`solidcraft-cli commands`). **354 commands.** Planned (registered, not built yet): 6. Every command runs the same way from the toolbar, a script, `solidcraft-cli exec <id> '<json>'`, the control channel and MCP; parameters are in the [command reference](book/src/reference/commands.md) (`cargo xtask book`). Status and plans: [ROADMAP](../ROADMAP.md).
|
||||
SolidCraft's commands by area, generated by `cargo xtask features` from the command registry (`solidcraft-cli commands`). **363 commands.** Planned (registered, not built yet): 6. Every command runs the same way from the toolbar, a script, `solidcraft-cli exec <id> '<json>'`, the control channel and MCP; parameters are in the [command reference](book/src/reference/commands.md) (`cargo xtask book`). Status and plans: [ROADMAP](../ROADMAP.md).
|
||||
|
||||
## Sketch (99)
|
||||
|
||||
@@ -517,13 +517,20 @@ SolidCraft's commands by area, generated by `cargo xtask features` from the comm
|
||||
| Insert Part | `parts.insert` | – |
|
||||
| Change Part Size | `parts.resize` | – |
|
||||
|
||||
## `render.*` (3)
|
||||
## `render.*` (10)
|
||||
|
||||
| Command | Id | Toolbar |
|
||||
|---|---|---|
|
||||
| Check Render Request | `render.preflight` | – |
|
||||
| Render Image | `render.start` | RENDER › IMAGE |
|
||||
| Render Progress | `render.status` | – |
|
||||
| Cancel Render | `render.cancel` | – |
|
||||
| Export Render Image | `render.export` | – |
|
||||
| Render Backends | `render.backends` | – |
|
||||
| Render Settings | `render.settings.get` | – |
|
||||
| Save Render Settings | `render.settings.set` | – |
|
||||
| Inspect Render Scene | `render.scene` | RENDER › SCENE |
|
||||
| Apply Render Camera | `render.settings.apply` | – |
|
||||
|
||||
## `select.*` (2)
|
||||
|
||||
|
||||
@@ -5,6 +5,11 @@ SolidCraft speaks the [Model Context Protocol](https://modelcontextprotocol.io)
|
||||
An agent models exactly like a person does: every tool runs engine commands, the same ones the
|
||||
toolbar, palette, scripts and control channel use, and never opens a dialog.
|
||||
|
||||
Persistent headless and bridged sessions expose asynchronous product images through the
|
||||
ordinary `execute` tool: `render.preflight`, `render.start`, `render.status`,
|
||||
`render.cancel` and `render.export`. Keep the complete returned job ID; image bytes remain
|
||||
in the session until explicit PNG export. See [render jobs](render-jobs.md).
|
||||
|
||||
## Start it
|
||||
|
||||
```sh
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Render backend capabilities (R1)
|
||||
|
||||
This page describes the R1 metadata contract. The installed GPU viewport and CPU
|
||||
product-job hosts extend it through complete profiles; HDR input preparation adds
|
||||
no lighting capability. See [the integrated phase boundaries](render-integration.md).
|
||||
|
||||
`render.backends` discovers render metadata without opening a window, probing a GPU,
|
||||
evaluating the model or changing the document, camera, dirty state or undo/redo history.
|
||||
It also leaves a model made stale by a live sketch gesture stale.
|
||||
@@ -55,11 +59,13 @@ descriptors, not design-file settings. Executor IDs are scoped to their backend.
|
||||
Backend/executor IDs are 1–64 ASCII bytes: a lowercase letter followed by lowercase
|
||||
letters, digits, `.`, `_` or `-`. Both construction and deserialization validate IDs.
|
||||
|
||||
The integrated backend currently describes only the existing CPU raster snapshot
|
||||
Without runtime hosts the integrated backend describes the existing CPU raster snapshot
|
||||
path. Its `preview`/`image` profile exports PNG; it does not claim HDRI, PBR,
|
||||
refraction, progressive rendering or a final-quality integrator. The existing GPU
|
||||
viewport remains separate and is not advertised here until a GPU host can register
|
||||
and probe its actual capabilities. CPU and GPU are execution paths, not separate
|
||||
viewport registers separately when the UI installs its actual device/pipelines.
|
||||
The installed GPU product viewport and CPU product-image host support the subsequent
|
||||
[opaque PBR consumer](render-opaque-pbr.md); this does not alter the static raster
|
||||
descriptor or enable HDRI/refraction/final rendering. CPU and GPU are execution paths, not separate
|
||||
user-selectable engine families.
|
||||
|
||||
## Pure request resolution
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# Phased render and material integration
|
||||
|
||||
Tracked in #842, with parent render design #620 and physical/visual material roadmap
|
||||
#922. The combined integration keeps contribution ancestry; it is an alternative
|
||||
review path, not evidence that the individual PRs have merged. Each result in GitHub
|
||||
is pinned to its tested source revision. Do not reuse an older green result for a new
|
||||
merge or describe compiled wasm as verified browser interaction.
|
||||
|
||||
## Delivery phases
|
||||
|
||||
| Phase | Contribution / deliverable | Acceptance gate |
|
||||
|---|---|---|
|
||||
| 1 | R2 #667, R3 #706, R4a #800: document-owned setups, product viewport, bounded CPU PNG jobs | Setup migration/save/reopen/undo, document and job isolation, strict capabilities, cancellation, native evidence and repository gates |
|
||||
| 2 | Merged HDR input #837 and surface reference #1019 | Bounded codec/BRDF tests; unchanged existing CPU capabilities until actual consumers exist |
|
||||
| 3 | Opaque product PBR #1046; engineering-card persistence #1060 | Explicit supported optical fields, source/condition preservation, one-step undo, cold reopening, unchanged geometry and independent assigned density |
|
||||
| 4 | Merged scalar catalogs #863; richer transport #1086; inspector/import #1094 | Exact selected revisions, copied definitions, hostile-input bounds, no lossy downgrade, scoped file access, reviewed import and actual native evidence |
|
||||
| 5 | Environment consumer #813, then texture/wood mapping #1004/#786 using resource admission #909 | Actual linear/sRGB interpretation and lighting, persistent object-local frames, cancellation/memory bounds, transformed instances, separate native/browser evidence |
|
||||
| 6 | Family-specific optical/data quality under #922 | Wood L/R/T and moisture/grade conditions; polymer grade/process/temperature/rate; glass thickness/IOR/absorption; metal alloy/temper/conductor response and brushed anisotropy; source/license review before shipping data |
|
||||
|
||||
The released document format is 20 (v0.11.0); render setups and opaque PBR looks are saved
|
||||
under format 21. Saved render setups migrate from older formats with older-reader refusal. The Design
|
||||
viewport retains upstream display-plan tessellation; Render uses its separate
|
||||
prepared product meshes. Neither runtime jobs nor device handles are persisted.
|
||||
See the versioned contracts rather than reserving another format independently.
|
||||
|
||||
## Current foundation boundary
|
||||
|
||||
The GPU host provides a product **viewport**. The CPU product executor provides
|
||||
bounded preview **PNG images**, with opt-in opaque PBR and approximate sorted transparency.
|
||||
The [fixed studio consumer](render-opaque-pbr.md) provides direct lighting only;
|
||||
it has no HDRI, cast shadows, transmission or indirect bounces. The Radiance
|
||||
utility prepares linear RGB in the source colour space; it is not lighting. The
|
||||
numerical opaque surface contract is not proof that a host renders those properties.
|
||||
Imported resources or saved intent never silently add HDRI/refraction/final/EXR
|
||||
support. Unsupported requests fail and preserve the last valid image and CAD state.
|
||||
See [backends](render-backends.md), [settings](render-settings.md),
|
||||
[workspace](render-workspace.md), [jobs](render-jobs.md),
|
||||
[HDR input](render-hdr-input.md) and [surface reference](render-surface.md).
|
||||
|
||||
Both atomic PNG publication modes check the existing caller file scope. The
|
||||
control-channel regression rejects outside folders and traversal and permits a
|
||||
valid destination, including explicit overwrite. This guard and document-adoption
|
||||
regression now belong to standalone R4a, so its safety does not depend on this
|
||||
combined integration. Render engine and scene production code match that foundation. The combined UI
|
||||
additionally consumes the async picker candidate described below; it does not add
|
||||
a second renderer or file-access service.
|
||||
|
||||
## Picker integration before release
|
||||
|
||||
The merged upstream #1127 service (`file_picker`) replaces the earlier #1114
|
||||
candidate in this branch. There is one picker service, provided by main. Its future
|
||||
returns a selected path or cancellation; the event loop continues drawing/resizing
|
||||
while model input, tab switching/closing and control engine commands wait.
|
||||
The compatibility inspection flag `file_dialog_open` mirrors `file_picker_busy`
|
||||
and exposes no paths. Render and library callbacks capture overwrite consent and
|
||||
retain explicit job/editor/document identity. An evicted image fails instead of
|
||||
substituting a newer result. Cancellation writes nothing. Source/host changes that
|
||||
replace the requesting document refuse the export; a replaced editor cannot receive
|
||||
an old library response. Controlled future regressions cover these boundaries.
|
||||
|
||||
Earlier native responsiveness evidence and tab-routing tests used #1114's candidate
|
||||
and keep their original pins. They are not proof of the new hold-input policy.
|
||||
The upstream service's own tests cover held input and resize frames; add native
|
||||
review evidence before describing the refreshed product workflow as GUI-verified.
|
||||
|
||||
## Material, appearance and resource ownership
|
||||
|
||||
Physical properties, assigned density, visual appearances and environment lighting
|
||||
are distinct. A card-only edit does not replace assigned density or visual style;
|
||||
applying an appearance does not change mass. Missing engineering values stay unknown,
|
||||
with per-property sources and measurement conditions. Numerical consistency is not
|
||||
certification or a structural solver. Mechanical axes are not inferred from a texture.
|
||||
|
||||
The first scalar catalog remains usable without optical/engineering extensions.
|
||||
Richer transport must preserve copied PBR/card records and reject lossy downgrade;
|
||||
reopening a design must not require the source catalog. Keep source revisions,
|
||||
conflicts and applicability explicit. All inspection/application uses existing engine
|
||||
commands; programmatic calls never open pickers. Library export edits the reusable
|
||||
file rather than the CAD design. Applying a definition is one undoable design edit.
|
||||
|
||||
Use original synthetic fixtures first. A licence string in imported data is a source
|
||||
claim, not redistribution permission. Any shipped dataset or texture needs actual
|
||||
source/license review and attribution. Commercial reference artwork is not copied.
|
||||
No second renderer, certified material database or simulation solver is implied.
|
||||
|
||||
## Verification and collaboration
|
||||
|
||||
Run `cargo xtask ci` before each committed arc. Record local/remote results, pinned
|
||||
upstream, actual platform and failures separately. Do not weaken geometry baselines,
|
||||
mask warnings or infer whole-app performance from cached capture/cancellation times.
|
||||
Attach actual app/PNG evidence for visible behavior; label older evidence with its
|
||||
source revision instead of relabeling it as current. Use scoped original examples.
|
||||
|
||||
The private `plan/` documents are unavailable in this checkout; no content is
|
||||
invented. Public contracts and current instructions govern this work. Propose bounded
|
||||
interfaces in GitHub where a handoff is missing, invite feedback, and check existing
|
||||
claims/PRs before implementing. Keep independent contributor branches untouched.
|
||||
@@ -0,0 +1,119 @@
|
||||
# Product-image jobs (R4a)
|
||||
|
||||
R4a adds bounded CPU preview images to the Render workspace. The installed `builtin` /
|
||||
`cpu-product` executor supports Preview, Image and PNG. It does not promise refraction,
|
||||
HDRI, exposure, final mode or EXR; those requests fail explicitly. The subsequent
|
||||
[opaque PBR consumer](render-opaque-pbr.md) adds opt-in direct-lit surfaces through
|
||||
the same host. Legacy scalar looks and the Design snapshot renderer retain their
|
||||
existing shading.
|
||||
|
||||
## One command path
|
||||
|
||||
`render.preflight` and `render.start` take `setup` (a saved name) OR `settings` (a
|
||||
RenderSetup object; omitted fields use its documented defaults), plus optional
|
||||
`transparent_background` and `replace` booleans, both false by default. Without either
|
||||
setup/settings, the current requested camera is used, or the camera is fitted to the
|
||||
product. Saving a setup is independent of starting a job. Unknown fields are errors.
|
||||
|
||||
Both commands reject stale geometry without settling a live modelling gesture. Finish
|
||||
the gesture or use the explicit `render.scene` boundary first. Capture uses R3's
|
||||
authoritative visible meshes, placements and appearances, without selection, edges,
|
||||
axes, grids, sections or analysis overlays. A cold capture can prepare world geometry
|
||||
and meshes synchronously; it is not a promise of bounded CAD evaluation latency.
|
||||
Preflight validates capabilities and estimates admission, but does not reserve a queue
|
||||
slot or the host memory pool. Start checks those resources again.
|
||||
|
||||
Start returns a typed `job: {session, serial, generation}`. Keep all three fields when
|
||||
calling `render.status`, `render.cancel` or `render.export`. IDs are never accepted by
|
||||
another document session, scratch copy, or a newly opened/recovered document. Status
|
||||
does no rendering and never waits for completion. It reports queued, preparing,
|
||||
running, encoding, cancelling, cancelled, completed or failed, with phase-local
|
||||
`done/total`, structured job errors and completed frame metadata. Progress restarts
|
||||
when a phase changes; it is not a time estimate.
|
||||
|
||||
The captured `scene_generation` is the session revision at start. Later edits, undo,
|
||||
camera changes and tab switches do not mutate the snapshot. An image continues to
|
||||
describe that captured scene; the UI labels a revision mismatch conservatively (view
|
||||
cache invalidation can also advance a revision). Runtime state is outside the document,
|
||||
undo, checkpoints and the design format. Closing/replacing a session cancels its work;
|
||||
the worker cannot publish a cancelled frame. Direct host-side document replacement
|
||||
preserves the installed image service while allocating fresh job ownership; old IDs
|
||||
and completed images never cross into the new document. `replace: true` cancels retained active
|
||||
and pending jobs before submitting the new request. A cancelled generation cannot
|
||||
publish late. A successful older generation never replaces a newer successful result.
|
||||
|
||||
Each document retains at most two live jobs, four recent terminal status records and
|
||||
one last successful image. Cancellation/failure preserves that image. A new successful
|
||||
image releases the previous image; old terminal metadata can still report completion,
|
||||
but it does not promise that its bytes remain exportable. Export requires an explicit
|
||||
retained completed job ID; it never silently exports a different/newer job.
|
||||
|
||||
## Native and browser execution
|
||||
|
||||
Each application installs one CPU host, shared by its documents. Native execution uses
|
||||
one worker and at most two queued inputs across documents; it runs without UI polling.
|
||||
The browser uses the same incremental task, pumped by UI frames, without threads.
|
||||
Preparation, stable transparency sorting, clearing, pixel tests and downsampling check
|
||||
cancellation between small units. A step admits at most 16384 units; PNG encoding
|
||||
additionally yields after four rows. The UI pumps 1024 units per frame. These are work
|
||||
bounds, not hard real-time guarantees: allocation, codec compression and OS scheduling
|
||||
still have platform-dependent latency.
|
||||
|
||||
Admission caps output at 4096 per dimension and 4 million pixels; pixel scratch is
|
||||
estimated at 48 bytes per output pixel (2x supersampling). Other limits: 262144
|
||||
triangles/face overrides/edge polylines, 128 MiB retained source geometry, 384 MiB total
|
||||
estimated memory per job, 512 MiB shared host admission including retained PNGs, and
|
||||
128 million projected pixel tests. The projected-work limit is checked during bounded
|
||||
preparation and may fail an admitted job. Encoded PNG bytes have an additional
|
||||
`8 * output_pixels + 65536` cap. Allocator and codec internals are estimates, not an
|
||||
operating-system memory quota. Limits fail with errors rather than allocating unbounded
|
||||
buffers. Completed frames keep their memory reservation while bytes are retained.
|
||||
|
||||
The CPU path shades in linear light, blends sorted triangle alpha approximately, then
|
||||
encodes 8-bit sRGB with straight alpha and an sRGB PNG chunk. Transparent shared edges
|
||||
use a consistent fill rule. Intersecting transparent surfaces and triangle sorting
|
||||
remain raster approximations. This is not a glass/refraction renderer. Native GPU
|
||||
preview and CPU export can differ in shading and transparency.
|
||||
|
||||
PNG uses the already-transitive Rust `png` crate (MIT OR Apache-2.0), now an explicit
|
||||
render dependency. No external render engine or third-party art is introduced.
|
||||
|
||||
## Export and automation
|
||||
|
||||
`render.export` takes `job`, `path` ending in `.png`, and `overwrite` (default false).
|
||||
The destination is checked against the caller's existing file-access scope before
|
||||
reading the retained frame or calling either native writer. Disallowed folders and
|
||||
traversal are refused in both overwrite modes. Native no-clobber export writes/syncs a temporary beside the target and publishes it
|
||||
with an atomic hard link; an existing destination wins even in a race. Filesystems
|
||||
without that operation return an error. Explicit overwrite uses the existing atomic
|
||||
writer without reading or backing up the previous target. Failed writes preserve the completed
|
||||
image and CAD state. Browser export uses the existing virtual file/download service;
|
||||
no filesystem paths or base64 payloads are persisted in the design.
|
||||
|
||||
The Render panel provides Check Render Request, Start Render, phase progress,
|
||||
Cancel Render and Export PNG. Replace existing file is an explicit opt-in. A failed or
|
||||
cancelled new render leaves the previous successful export available.
|
||||
|
||||
Native PNG export in the combined integration uses the document-bound asynchronous
|
||||
picker from #1114. The requested job ID and overwrite choice are captured before the
|
||||
dialog opens: cancellation writes nothing, closing the originating document drops
|
||||
the answer, and an evicted image fails instead of substituting a newer result. A tab
|
||||
switch does not redirect the export to another document.
|
||||
|
||||
Controls and phase messages use stable offline catalog IDs in English, Japanese, Chinese,
|
||||
German and Korean; command IDs and scenario handles remain unchanged.
|
||||
|
||||
```sh
|
||||
solidcraft-cli render examples/render-studio.json --out product.png --width 800 --height 600
|
||||
solidcraft-cli render design.solidcraft --setup Studio --out product.png --transparent
|
||||
```
|
||||
|
||||
The CLI follows preflight/start/native wait/export, with a default 120-second wait
|
||||
limit (`--timeout 1..3600`). Timeout requests cancellation. `--overwrite` is required
|
||||
to replace an existing target. A persistent headless MCP session installs the same
|
||||
host: use the ordinary `execute` tool with the five commands and returned job ID.
|
||||
Closing the CLI/MCP process ends its session; a one-shot `exec render.start` is not a
|
||||
durable background service. In a browser, leave the app running so frames can pump work.
|
||||
|
||||
Validation evidence and responsiveness measurements are recorded in the R4a issue/PR;
|
||||
passing native tests or a wasm compile does not establish browser visual performance.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Opaque product surfaces
|
||||
|
||||
This is the first integrated consumer slice of #1004, under #813/#620. It uses the
|
||||
existing Render workspace, product geometry, appearance assignment and PNG job host.
|
||||
There is no second renderer or external material dataset.
|
||||
|
||||
## Apply and preserve a response
|
||||
|
||||
```json
|
||||
{"command":"appearance.assign","params":{"bodies":["Specimen"],"color":"#b47846","pbr":{"metallic":1,"roughness":0.3,"dielectric_ior":1.5}}}
|
||||
```
|
||||
|
||||
The Appearance editor has an **Opaque PBR** checkbox and separate Metallic,
|
||||
Roughness and Dielectric IOR fields. Enabling it explicitly sets opacity to one.
|
||||
View the response in Render or export a product PNG; Design retains its simple
|
||||
color preview. Apply/OK, swatch reuse and component/face targeting use the existing
|
||||
assignment command. A later scalar face override replaces a body's PBR response
|
||||
on that face; it does not inherit the optical response implicitly.
|
||||
|
||||
Parameters are finite and bounded: metallic [0,1], perceptual roughness [0.045,1],
|
||||
relative dielectric IOR [1,3]. Unknown optical fields and nonnumeric/out-of-range
|
||||
opacity refuse before changing the design. PBR requires opacity exactly one.
|
||||
IOR controls the nonmetal fraction's reflection; it does not enable refraction.
|
||||
Omitting `pbr` or supplying null in an appearance assignment selects the previous
|
||||
scalar response. Assignment replaces a look; it is not a partial parameter patch.
|
||||
|
||||
`material.create/edit` accept the same optional `pbr` block alongside a default
|
||||
appearance/color. A density-only edit retains the entire existing look. Visual
|
||||
response changes do not modify density, geometry, placement or engineering axes.
|
||||
No grade-specific optical or mechanical values are inferred from a name or color.
|
||||
|
||||
Format **21** saves `pbr` inside existing body/face/component and material-default
|
||||
looks. Missing fields retain scalar shading. Format-20 render setups and old looks
|
||||
upgrade without reinterpretation; older readers refuse newly saved files instead
|
||||
of dropping their optical response. Undo/redo, copying and cold reopening use the
|
||||
existing design-owned values, with no external file dependency.
|
||||
|
||||
The current scalar catalog v1 cannot represent PBR. Conversion/export refuses a
|
||||
PBR look or physical-material default instead of silently dropping its parameters.
|
||||
In-design swatches retain and reuse the response. A richer catalog version remains
|
||||
a separate integration step.
|
||||
|
||||
## Actual consumers and color
|
||||
|
||||
The installed integrated `gpu-viewport` and `cpu-product` image hosts advertise the
|
||||
`pbr` feature. Requests require it when the captured product scene contains PBR.
|
||||
The static legacy `cpu-raster` descriptor retains its previous capabilities.
|
||||
No host gains HDRI, refraction, progressive/final rendering or EXR support.
|
||||
|
||||
Look colors are decoded from sRGB8 to linear sRGB once in the shared document/scene
|
||||
adapter. PBR GPU vertices carry linear reflectance and optical parameters. The CPU
|
||||
rasterizer interpolates world position and normals with perspective correction;
|
||||
the GPU uses the corresponding fragment interpolation. Both use the original
|
||||
[opaque BRDF](render-surface.md) and three fixed neutral directional lights in Z-up
|
||||
world coordinates. They sum BRDF × relative incident radiance × light cosine,
|
||||
apply per-channel Reinhard preview tone mapping, then encode exactly once.
|
||||
Linear and sRGB GPU attachments have distinct output transfer paths.
|
||||
|
||||
This fixed studio has no cast shadows, environment illumination, indirect bounces,
|
||||
emission, anisotropy, coating, absorption or transmission. Lighting magnitudes are
|
||||
relative preview values, not calibrated lux or spectral data. The single-scattering
|
||||
model can darken rough metals. Transparent legacy looks retain approximate sorted
|
||||
alpha blending, including when seen in front of an opaque PBR surface; that is not
|
||||
glass optics. Nondefault exposure/environment controls remain explicitly unsupported.
|
||||
|
||||
Scene uploads keep the 128 MiB bound, accounting for the larger PBR vertices.
|
||||
CPU face-map admission accounts for retained optical descriptors. Existing immutable
|
||||
capture, ownership, cancellation, stale-result rejection and PNG export policies
|
||||
remain in effect. Invalid/nonfinite geometry or zero input normals refuse.
|
||||
|
||||
## Validation and original example
|
||||
|
||||
`examples/render-opaque-studio.json` builds four original spheres and a base,
|
||||
varying metallic fraction and roughness independently while keeping base color.
|
||||
Its values are an illustrative optical study, not engineering data for real grades.
|
||||
|
||||
The product offscreen test uses the actual installed callback/pipelines on Vulkan,
|
||||
including both linear and sRGB attachments. For four metal/roughness combinations,
|
||||
each comparison checks 12,454 interior pixels against CPU PNG output; observed mean
|
||||
channel error is below 0.2/255, with no pixels beyond the 12/255 outlier threshold.
|
||||
Silhouettes are excluded because CPU images use 2x supersampling and that test's GPU
|
||||
target uses 1x. This is a scoped parity result, not browser or universal GPU certification.
|
||||
|
||||
Command tests check unchanged geometry/mass, atomic refusal, undo/redo, cold reopen,
|
||||
physical default/face precedence, retained scalar format-20 looks upgraded to 21 and explicit v1
|
||||
catalog refusal. A clicked editor regression checks one transaction and reopening
|
||||
the same selected body. Native media and exact current-head gate results belong
|
||||
in the PR. wasm compilation and actual browser interaction are separate evidence.
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
Named setups save common render intent in the document. They do not start a render,
|
||||
apply the navigation camera or make a currently unavailable backend available.
|
||||
The Render workspace, scene extraction, assets, PBR materials and jobs are later slices
|
||||
of [#620](https://github.com/bherbruck/solidcraft/issues/620).
|
||||
The integrated workspace and jobs consume these settings through their own capability
|
||||
checks. [Opaque PBR](render-opaque-pbr.md) is an opt-in appearance response; texture
|
||||
and environment assets remain follow-ups of [#620](https://github.com/bherbruck/solidcraft/issues/620);
|
||||
see [the combined integration boundary](render-integration.md).
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# Integrated Render workspace (R3)
|
||||
|
||||
R4a builds on this viewport contract with a separately advertised CPU product-image
|
||||
executor and Check/Start/Cancel/Export controls. See [render jobs](render-jobs.md) for
|
||||
the asynchronous protocol, image limits, captured revisions and PNG export. The legacy
|
||||
`ui.render` snapshot remains a separate route.
|
||||
|
||||
Choose **Render** in the workspace switcher after finishing a modelling operation.
|
||||
The current integrated GPU renderer shows a separate product scene. Return to Design
|
||||
restores its camera, projection preference and existing viewport drawing policy.
|
||||
No second engine is installed or selectable.
|
||||
|
||||
## Product scene
|
||||
|
||||
`engine::product_scene::extract` consumes authoritative evaluated geometry, reusing
|
||||
the document's world/occurrence placement and mesh Arcs. Body, component, material,
|
||||
face and imported appearances retain their existing precedence and opacity. Source
|
||||
geometry is never transformed a second time or altered to serve a renderer.
|
||||
|
||||
The shared L1 `ProductScene`/`ProductMesh` contract contains world-space meshes,
|
||||
resolved sRGB colours, opacity and face appearance overrides. Face indices are the
|
||||
existing mesh-to-B-rep mapping, resolved through document appearance references;
|
||||
they are not new persistent names or triangle-derived face identities.
|
||||
|
||||
There are no grid/axis/edge lines, sketches, construction objects, selection or hover
|
||||
colours, section cuts, analysis shading, active-component fading, canvases or decals
|
||||
in this initial product policy. Browser visibility still controls which bodies appear.
|
||||
The existing Design viewport and CPU snapshot routes keep their current policies.
|
||||
|
||||
Extraction refuses a stale live-drag model. The explicit model-reading `render.scene {}`
|
||||
command follows the normal engine evaluation boundary, then returns bounded metadata:
|
||||
body names, triangle counts, resolved colours/opacity, face-override counts and bounds.
|
||||
It does not return mesh payloads, change the document or add an undo step. Product
|
||||
extraction admits at most 16,384 visible bodies.
|
||||
|
||||
## Settings and camera
|
||||
|
||||
The right panel stages a named setup's background and camera changes. **Save Setup**
|
||||
records the current existing `Camera` snapshot through `render.settings.set`, with
|
||||
one undo step; the other R2 fields retain their values. **Reload Saved Settings**
|
||||
discards staged settings. Draft settings are separate for each open document.
|
||||
|
||||
**Restore Saved Camera** runs `render.settings.apply {name}`, preserving target,
|
||||
distance, yaw, pitch, roll, FOV and up axis as view state. It neither edits the document
|
||||
nor settles a stale model. The UI honours its up axis in Render; the Design camera
|
||||
is restored when returning. Drag orbits, middle-drag pans, scrolling zooms, and
|
||||
**Fit Product** frames visible product bodies without including sketches or selection.
|
||||
|
||||
Preview drafts do not make the document dirty until saved. Unknown backend preferences
|
||||
and unsupported modes remain saved intent: the strict resolver reports the unsupported
|
||||
viewport request. **Use Integrated Preview** is an explicit draft change, never an
|
||||
automatic fallback. There is no final-mode or second-engine selector in this slice.
|
||||
Saved exposure/environment/output fields are not newly applied to shading or export.
|
||||
|
||||
## Host capabilities and resources
|
||||
|
||||
`render.backends` always retains the built-in `cpu-raster` preview/PNG image executor.
|
||||
Only an installed UI GPU host adds `gpu-viewport`, advertising **Preview / Viewport**
|
||||
with no image formats or HDRI/refraction/progressive features. The installed
|
||||
[opaque PBR consumer](render-opaque-pbr.md) adds the `pbr` feature. The host reports
|
||||
effective limits from its created device and depth attachment: at most 4096 pixels
|
||||
per dimension and 4,194,304 pixels, within the 256 MiB pixel scratch admission policy.
|
||||
The estimate includes the selected MSAA colour/depth cost; it excludes scene geometry,
|
||||
driver overhead and cached source meshes. It is not a measurement of total GPU memory.
|
||||
|
||||
Pure capability metadata lives in the session/host, outside persistence and history,
|
||||
surviving document replacement, scratch sessions and document-tab switches. No wgpu
|
||||
types enter the engine or document crates. A host without an installed GPU/depth buffer
|
||||
reports that product viewport preview is unavailable, rather than silently using the
|
||||
opaque CPU snapshot path to approximate transparent appearances.
|
||||
|
||||
GPU product batches are separate from Design batches while sharing the installed
|
||||
device and pipelines. Model/visibility/appearance changes update the product scene;
|
||||
camera movement updates uniforms without rebuilding vertices. Selection and section
|
||||
state do not change the product buffers. Vertex uploads are bounded to 128 MiB and
|
||||
checked before allocation; malformed/non-finite triangles and invalid opacity fail.
|
||||
Reinstalling the host invalidates product uploads. This is not a general device-loss,
|
||||
render-job or cancellation protocol; those ownership mechanisms belong to R4.
|
||||
|
||||
Transparency uses the existing raster far/near alpha-blending approximation, without
|
||||
refraction or HDR illumination. Opaque PBR uses a separate surface batch in the
|
||||
same installed renderer. R3 itself added no export job; R4a supplies CPU product
|
||||
PNG jobs. There is no result gallery or Bevy dependency. Saved setups were added
|
||||
in format 21, alongside opt-in optical parameters on existing looks.
|
||||
`ui.render` remains the existing Design CPU snapshot API; `ui.screenshot` captures
|
||||
the actual integrated Render workspace. Product-image export uses `render.start`
|
||||
and `render.export`, rather than the Design snapshot API.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Run the original procedural `examples/render-studio.json` through the engine/CLI and
|
||||
open its saved design in the app. Select Render, adjust its background or camera and
|
||||
save a setup. The fixture uses original boxes/cylinders and simple colour/opacity
|
||||
assignments; no commercial CAD files, artwork or materials are used. Actual Windows
|
||||
Vulkan/MSAA evidence is attached to [#673](https://github.com/bherbruck/solidcraft/issues/673).
|
||||
Reference in new issue
Block a user