10 KiB
Render Pipeline
cadjs exposes a staged render pipeline for shared viewer, docs, and generated
snapshot browser-runtime work:
const source = await loadSource(input, sourceOptions);
const model = buildModel(THREE, source, modelOptions);
const viewport = renderModel(THREE, model, viewportOptions);
const result = await captureModel(viewport, captureOptions);
The stages keep ownership narrow:
loadSourceowns source and sidecar loading plus file-kind validation.buildModelowns the CAD object graph, records, selection, clipping, materials, topology/display edges, and STEP parameter effects.renderModelowns renderer, scene, camera, lighting, background, floor, framing, resizing, and render loop concerns.captureModelowns deterministic snapshot outputs without filesystem writes.
The CAD skill's Python snapshot CLI remains responsible for job parsing, path resolution, Playwright routing, and writing returned outputs to disk.
Modules
common/source.js
import {
loadSource,
stepParameterFrameRuntime
} from "cadjs/common/source.js";
loadSource(input, options) returns a normalized render source:
{
kind,
meshData,
selectorRuntime,
displayEdgeRuntime,
stepParameterSource,
resolved,
url,
glbUrl,
cadPath
}
Accepted input fields:
kind:step,stp,glb,stl,3mf, or inferred from a URL.meshData: already-loaded mesh data. If present, no mesh URL fetch is needed.url: source URL for non-STEP GLB loading.glbUrlorresolved.glbUrl: STEP/STP hidden GLB sidecar URL.cadPathorresolved.inputPath: CAD path used by STEP selectors.selectorRuntimeanddisplayEdgeRuntime: preloaded runtimes when a caller already owns sidecar loading.stepParameters: raw STEP render values or animation envelope.stepParameterUrlorresolved.stepParameterUrl:.step.js/.stp.jsparameter sidecar URL.
STEP-only options are rejected for non-STEP sources. The old shared params
field is rejected; use stepParameters.
Use stepParameterFrameRuntime(stepParameterSource, frameIndex) to turn the
loaded STEP parameter source into the runtime object accepted by buildModel
for a still or animation frame.
common/cadScene.js
import {
buildModel,
fitCameraToModel
} from "cadjs/common/cadScene.js";
buildModel(THREE, source, settings) returns a model API:
{
source,
meshData,
root,
modelGroup,
edgesGroup,
displayRecords,
records,
bounds,
radius,
runtime,
update(nextSettings),
dispose()
}
source can be a loadSource() result or raw mesh data. The model owns the
Three.js object graph and its mutable state.
Common settings:
theme: normalized or raw theme settings.displayMode:solid,rendered,transparent,hidden_edges,hidden_lines_removed,unshaded, orwireframe.scale/sceneScale: CAD or robot scene scale.selection: internal selection/filtering state.focus,refs, andhidefilter rendered parts before records are built. Viewer-only fields such asselectedPartIds,hiddenPartIds, andshowEdgesaffect visual state.clip: normalized clip-plane settings.stepParameters: STEP parameter runtime object, usually fromstepParameterFrameRuntime().parameterSetup: setfalseto skip sidecar setup lifecycle calls.renderPartsIndividually: build per-part records instead of a whole mesh.edgeRendering: declarative edge rendering configuration.
Declarative screen-space edge rendering:
buildModel(THREE, source, {
edgeRendering: {
mode: "screen-space",
Line2,
LineGeometry,
LineSegments2,
LineSegmentsGeometry,
LineMaterial,
wireframeEdgeColor: "#111827"
}
});
The model keeps screen-space line material bookkeeping internal through
runtime.screenSpaceLineMaterials and runtime.syncScreenSpaceLineMaterials().
Callers should not provide callbacks that create edge objects.
model.update(nextSettings) merges mutable settings, rebuilds geometry only
when needed, reapplies material/selection/clip/STEP parameter state, and returns
the same model API. model.dispose() releases model-owned scene objects and
STEP parameter cleanup hooks.
fitCameraToModel(THREE, camera, bounds, options) is the shared orthographic
camera framing helper used by interactive rendering.
common/renderModel.js
Use this module for interactive browser canvases, including the docs hero.
import { renderModel } from "cadjs/common/renderModel.js";
renderModel(THREE, model, options) returns an interactive viewport API:
{
THREE,
model,
renderer,
scene,
camera,
ready,
resize(),
render(),
start(),
stop(),
capturePng(),
dispose()
}
Common options:
canvas: existing canvas for the renderer.hostElement/container: element used for responsive sizing.renderer: caller-owned renderer. If omitted, one is created.sceneandcamera: caller-owned scene/camera. If omitted, defaults are created.theme/themeSettings: background and lighting settings.alpha,antialias,powerPreference,preserveDrawingBuffer,logarithmicDepthBuffer,shadows: renderer controls.direction,up,padding,scale/sceneScale: framing controls.pixelRatio,maxPixelRatio: output density controls.autoResize: setfalseto disableResizeObserver.autoStart: settrueto start an animation loop.autoRender: setfalseto prevent the initial render.beforeRender({ deltaSeconds, viewport }): per-frame hook for animation.disposeModel: setfalsewhen the caller will dispose the model.
dispose() stops animation, disconnects resize observation, removes the model
root from the scene, and disposes the created renderer/model unless ownership
was explicitly retained by options.
common/renderMeshScene.js
Use this module for deterministic headless snapshot rendering.
import {
renderJobContext,
modelOptionsForRenderJob,
renderModel,
captureModel,
renderMeshJob
} from "cadjs/common/renderMeshScene.js";
renderJobContext(meshData, job) normalizes snapshot-owned render policy:
appearance, display, scene scale, outputs, STEP topology edge visibility, and
warnings.
modelOptionsForRenderJob(context, job) converts that policy into
buildModel() settings.
Snapshot renderModel(THREE, model, { job, context }) returns a headless
viewport:
{
THREE,
model,
scene,
renderer,
orthographicCamera,
perspectiveCamera,
context,
sceneBuildStarted,
ready,
dispose()
}
This renderModel is intentionally separate from common/renderModel.js.
It uses snapshot sizing, theme environment, floor, orthographic/perspective
camera presets, and deterministic renderer settings.
captureModel(viewport, { job }) returns data only:
mode: "view": PNG data URLs inoutputs.mode: "orbit": the caller composes frames into GIF output.mode: "section": PNG data URLs or SVG text inoutputs.mode: "list": part list and bounds.
It does not write files. The CAD skill snapshot CLI writes the returned data to
disk. Source checkouts use packages/cadjs; generated snapshot browser assets
bundle this entrypoint into skills/cad/scripts/snapshot/runtime.
renderMeshJob(meshData, job) is a compatibility wrapper that builds a context,
builds a model, renders/captures it, and disposes owned resources.
STEP Parameters
Shared render APIs use the name stepParameters.
Raw render values can be direct parameter values:
{
"drive": 180,
"ringVisible": false
}
Or an animation envelope:
{
"values": {
"ringVisible": true
},
"animate": {
"drive": { "from": 0, "to": 1260 }
},
"durationSeconds": 6,
"fps": 18,
"loop": true
}
common/stepParameters.js validates these values against the loaded STEP
parameter sidecar schema, normalizes defaults, and computes per-frame values.
loadSource() uses it to populate source.stepParameterSource; callers can
then use stepParameterFrameRuntime() when passing parameters into
buildModel().
Examples
Interactive viewer/docs usage:
import * as THREE from "three";
import { loadSource, stepParameterFrameRuntime } from "cadjs/common/source.js";
import { buildModel } from "cadjs/common/cadScene.js";
import { renderModel } from "cadjs/common/renderModel.js";
const source = await loadSource({
kind: "step",
glbUrl: "/models/.part.step.glb",
stepParameterUrl: "/models/.part.step.js",
cadPath: "models/part.step",
stepParameters: { drive: 180 }
});
const model = buildModel(THREE, source, {
theme,
displayMode: "solid",
stepParameters: stepParameterFrameRuntime(source.stepParameterSource, 0)
});
const viewport = renderModel(THREE, model, {
canvas,
hostElement: canvas.parentElement,
theme,
autoStart: true
});
Headless snapshot usage:
import * as THREE from "three";
import { loadSource } from "cadjs/common/source.js";
import { buildModel } from "cadjs/common/cadScene.js";
import {
captureModel,
modelOptionsForRenderJob,
renderJobContext,
renderModel
} from "cadjs/common/renderMeshScene.js";
const source = await loadSource(job);
const context = renderJobContext(source.meshData, job);
const model = buildModel(THREE, source, modelOptionsForRenderJob(context, job));
const viewport = renderModel(THREE, model, { job, context });
try {
const result = await captureModel(viewport, { job });
// Write result.outputs in the CAD skill snapshot CLI or another caller-owned layer.
} finally {
viewport.dispose();
}
Orbit GIF jobs default to 6 fps over 12 seconds for a calmer review spin
without increasing default render frame count.
Override with orbit.fps and orbit.durationSeconds when a shorter or denser
render is needed.
Ownership Rules
- Do not write files from shared render APIs. Return data to the owning CLI or application layer.
- Do not expose object-construction callbacks for edges. Use declarative
edgeRendering. - Keep STEP-only options explicitly STEP-named and reject them for non-STEP sources.
- Dispose viewports and models that you create.
- Prefer
loadSource -> buildModel -> renderModel -> captureModelfor new shared render code instead of loading assets or constructing render scenes inline.