- Base code:
src/camera-frames.ts/src/ui/camera-frames-panel.ts/src/camera.ts/src/scene.ts/src/render.ts(package version 2.24.2 / as of HEAD). - CAMERA FRAMES individual version:
cameraFramesVersion= v2.21.3 (frompackage.json, appended as| CAMERA FRAMES v2.21.3to#app-label). - This document replaces v9 with the implementation-aligned v10. Updates:
- Reworked the header to CAMERA FRAMES ON/OFF + Capture Camera Controls (separate panel) and removed uiTarget switching UI.
- Unified lens UI by view mode; edit-view capture edits are now in the Capture Camera Controls panel.
- Added
mainEditModeso capture frustum highlight and near-clip updates persist while the panel is open even when CF is OFF.
-
Render box scaling with anchors
- Choose a reference point from 9 anchors (left/center/right × top/center/bottom) and implement an off-axis frustum that keeps the composition on the anchor side intact while expanding or shrinking only the opposite side.
- Even when scaling width and height independently, the composition at the anchor remains unchanged.
-
Downscaled display for viewZoom (25-100%)
- When zooming out, the original composition shrinks on the canvas and surrounding 3D scene shows up as margin. No black cutoff outside the canvas.
- Zoom out WebGL rendering, render-box outline, frame, and mask together, and extend the frustum to fill the entire screen.
-
Separate base FOV and effective frustum
- Keep FOV(mm) as the composition baseline horizontal FOV, and only reapply when the UI value changes.
- Recompute the actual projection each time based on render-box scale, viewZoom, and anchor.
-
Match preview and export
- What you see inside the render box in preview matches PNG/PSD output pixel-for-pixel (export uses viewZoom=100% with no frustum extension).
-
Preserve existing UX + strengthen implementation
- Keep existing operations such as moving/rotating/scaling/anchoring frames, Shift axis lock, Alt anchor swap, double-click reset, etc.
- Also requirements for details in the current implementation such as adding pHYs to 150dpi PNG, PSD layer split, grid/eye-level optional output, near clip safety.
-
Camera editing aid when CAMERA FRAMES is OFF
- Keep Capture Camera Pose and allow pose/FOV(mm)/navMode/near edits via the Capture Camera Controls panel while CAMERA FRAMES is disabled.
- In edit view show the normal camera lens (mm) slider; in capture view show the composition baseline FOV(mm).
- The rectangle (px) occupied by Supersplat’s WebGL
<canvas>. UI/overlay calculations use CSS pixels, separate fromscene.targetSize(device pixels).
- Baseline resolution:
1754 × 1240 px(A4 150dpi). - Scale:
Only allow 100% or higher, clamp each axis to 16000px or below.
logicalW = baseW * scale.kx; // kx = scalePct.x / 100 logicalH = baseH * scale.ky; // ky = scalePct.y / 100
- Anchor
ax/ay= 0 | 0.5 | 1. Anchors are the “composition reference point” used for off-axis deformation during scaling. In the UI grid, ay=0 corresponds to “top.” centeris screen coordinates (px). When resizing, adjust to keep the anchor’s screen position.fitScale= factor to fit the logical size into the viewport when viewZoom=100%.viewScale = fitScale * (viewZoomPct / 100)becomes the actual overlay scale.
- Baseline size:
1536 × 864 px.scalePct(10-400%) scales uniformly. posis render-box local 0-1 (values outside allowed).rotationDegis normalized to 0-360.anchoris the frame’s own reference point (defaults to pos). Used as the fixed point for Alt-resize and rotation.- Draw frames in ascending
order; hit testing favors the frontmost.
- Enabled when
mask.enabledis true. Opacity 0.0-1.0 (UI shows %). scope: 'all' | 'selected'. When set to selected, only the chosen frames are targeted (fall back to all frames if none selected).- Fill outside the rotated screen bounding box of all frames with a black mask. Preview only; not included in PNG/PSD.
- Composition baseline FOV (
baseFov): Horizontal FOV baseline. UI FOV(mm) displays this converted to mm. Do not auto-switch axis based on aspect (always equivalent to horizontalFov=true). - Effective frustum (
effectiveFrustum): Frustum used for rendering. Apply render-box scale and anchor for off-axis transformation, then only in preview extrapolate based on viewZoom to fill the screen.
- Logical Space: px on the render box (baseline sheet coordinates). Relative calculations use
renderBox.centeras origin. - RenderBox Local 0-1: Used for UI inputs such as
frame.pos/anchor. Values below 0 or above 1 allow frames to be placed outside the sheet. - Screen Space: CSS px on the canvas. Overlay drawing and pointer operations occur here.
- UI Space: PCUI DOM coordinates. Used for UI-specific processing such as panel movement.
- Repeated resizing/scaling/zooming keeps the anchor-based screen coordinates and composition unchanged.
- Recompute from
baseFrustum + current parameterseach time without delta updates or accumulated error.
- framesEnabled true = capture view (CAMERA FRAMES ON), false = edit view.
mainCameraPoseis the baseline pose for CAMERA FRAMES. Keep it even when disabled, and allow editing via the Capture Camera Controls panel.- While the panel is open, treat it as mainEditMode and highlight the capture frustum (draw-only, not hittable).
- Lens (mm) slider is available only in edit view. It shows the current camera FOV converted to 35mm and applies via
camera.setFov. - The conversion range uses crop factor based on the renderBox baseline width (equivalent to HFOV 10-120°).
- Reference images are organized by presets. The default empty preset is
(blank). - When the active preset is
(blank)and reference images are imported for the first time, create a new preset named after the first imported filename and switch to it (do not auto-update the name on later imports).
type CameraFramesState = {
enabled: boolean;
renderBox: RenderBoxState;
frames: FrameState[];
mask: FrameMaskState;
mainCameraPose?: CameraPoseSnapshot | null;
nearClip?: number | null;
exportName?: string;
exportFormat?: 'png' | 'psd';
exportGridOverlay?: boolean;
exportModelLayers?: boolean;
exportTarget?: 'current' | 'all' | 'selected';
exportPresetIds?: string[];
};baseSize: { w: 1754, h: 1240 }scalePct: { x: 100, y: 100 }→scale: { kx: 1, ky: 1 }(100% or higher, each axis capped at 16000px)anchor: { ax: 0.5, ay: 0.5 }center: { cx: 0, cy: 0 }(initialized to viewport center when enabled)fitScale: 1(recalculate AutoFit on first load/resize)viewZoomPct: 100(clamped to 25-100)lastViewport: { vw: 1, vh: 1 }projection: { type: 'perspective', baseFov: 60 }(supportstype: 'ortho'/orthoHalfHeight, persist using horizontal baseline)
id: A, B, C… up to 20.pos: { 0.5, 0.5 }scalePct: 100→scaleK: 1baseSize: { w: 1536, h: 864 }order: increases from 0 in added order.rotationDeg: 0anchor: same aspos.
enabled: falseopacity: 0.8scope: 'all'
mainCameraPose: snapshot and keep the current camera pose (if missing, capture on enable).nearClip: null (calculate a safe value from the camera on enable and fix it)exportName:'cf-%cam'(replaces%camwith the selected camera preset name)exportFormat: initial state ispsd. When initializing from unsaved document (docState=null), set topngand overwrite through UI sync.exportGridOverlay: falseexportModelLayers: falseexportTarget:current(export target defaults to current camera)exportPresetIds:[](selection list for export targets)- Constants:
HFOV_MIN=10,HFOV_MAX=120,W_35MM=36(35mm equivalent width) cameraFramesVersion: save and displaycameraFramesVersionfrompackage.json(include in docSerialize).
- Mode detection: If
scene.camera.targetSizeexists, it is export mode; otherwise preview. - Size choice:
vw/vhare targetSize in export, canvas CSS px in preview. - Logical size:
logicalW/H = baseSize * scale(values <= 1e-6 are corrected to 1e-6). - Auto Fit:
autoFit = min(vw/logicalW, vh/logicalH)(use 1 if vw/vh are 0). - viewZoom: 100 fixed during export; clamp to 25-100 in preview.
- fitScale update: In preview, when
updateFitScaleis requested adoptautoFit. Always 1.0 in export. - viewScale: 1.0 during export; otherwise
fitScale * viewZoom/100, fixed to at least 1e-6. - center: Screen center for export. In preview use
renderBox.center. On resize withupdateFitScale && viewportChanged, adjustcenterto keep the anchor’s screen position. - Rectangles:
rectPxRawis unclipped;rectPx/rectNormare clipped for display. Use Raw for frustum extension.
- Aspect: use render-box baseSize (A4 ratio for A4). Do not use frame ratio (16:9).
- FOV:
baseFovDeg = projection.baseFov ?? scene.camera.fov ?? HFOV_MIN; horizontalRad = clampFov(baseFovDeg -> horizontal, aspect); // 10-120° halfW = near * tan(horizontalRad / 2); halfH = halfW / aspect; l0=-halfW, r0=halfW, b0=-halfH, t0=halfH;
lockFovAxisis fixed to'horizontal'(when CAMERA FRAMES is enabled). Setcamera.horizontalFovto horizontal lock as well.- For ortho, use
orthoHalfHeightto determine left/right/bottom/top.
width1 = (r0 - l0) * kx;
height1 = (t0 - b0) * ky;
left1 = l0 + ax * ((r0 - l0) - width1);
right1 = left1 + width1;
// ay=0 points to "top", so use (1 - ay) for Y-up frustum
bottom1 = b0 + (1 - ay) * ((t0 - b0) - height1);
top1 = bottom1 + height1;- This is also the export frustum (no extension).
- From
rectPxRaw(render-box rectangle on screen, unclipped) andrbW/rbH(width on near plane), derive world width per pixel:pxToWorldX = rbW / rectPxRaw.w; pxToWorldY = rbH / rectPxRaw.h;
- Left/right/top/bottom covering the entire screen:
left = rbLeft - rectPxRaw.x * pxToWorldX; right = left + vw * pxToWorldX; top = rbTop + rectPxRaw.y * pxToWorldY; // DOM Y=top, frustum Y-up bottom = top - vh * pxToWorldY;
- Apply this to
camera.setCustomFrustum, and always reset PlayCanvas Rect/Scissor to 0,0,1,1.
- While
scene.camera.targetSizeis set, fix viewZoom=100%, center=screen center, fitScale=1 and useleft1/right1/bottom1/top1as is. - Before export, explicitly apply the export frustum with
syncExportFrustum()(temporarily set targetSize). After finishing, restore targetSize and resync to the preview frustum.
- Crop factor:
crop = renderBox.baseSize.w / 1536. - Conversion:
eqMm = (W_35MM / (2 * tan(hfovRad/2))) * crop; hfovFrameDeg = 2 * atan(tan(hfovRad/2) / crop);
- UI slider range is dynamically determined by converting
HFOV_MIN/HFOV_MAXto mm. UseDEFAULT_FRAME_BASE(16:9) aspect for calculation. UpdatebaseFovand recompute only when changed. - In edit view, the viewport lens (mm) uses the same conversion to display/edit the current camera FOV (capture view edits baseFov instead).
computeSafeNearClip: requiresnear > 0andnear < far, capped byfar*0.1/boundRadius*0.5. If invalid, useDEFAULT_NEAR_CLIP=0.01.- Schedule a guard on enable/scene change, recheck a few frames later, and force correction if needed.
- PCUI-based. Header includes CAMERA FRAMES ON/OFF toggle (Capture/Edit icons), Capture Camera Controls (separate panel) button, and compact mode toggle (fold all contents). Header draggable; clamped to the window. Adjust position on resize.
- Prevent panel pointer events from propagating to canvas (stopPropagation).
pointerenterclears overlay hit testing. - Compact mode shows header only. ON/OFF toggled by the header Capture/Edit buttons (Capture=ON, Edit=OFF).
- The Capture Camera Controls panel can be opened only while CAMERA FRAMES is OFF and shows a locked state when ON.
- The Reference Image panel includes a
Preset Nameinput to rename the active preset ((blank)is read-only).
- Collapsible header (default collapsed). Anchor 3×3 buttons, width% / height% (min 100 / max 1000 in UI, actually clamped to 16000px), viewZoom 25-100, output resolution display.
- Output resolution display includes logical size (outW/outH), scale (kx/ky), and viewport overflow warning.
- FOV(mm) slider shows eqMm. Enabled only in capture view. Use the Capture Camera Controls panel to edit capture camera FOV while in edit view.
- Viewport lens(mm) slider is enabled only in edit view.
- Add (+), delete (trash) buttons, list shows A/B/C… Text shows effective pixel size (including render-box scale) and %.
- Click list to select / click again to deselect. Only selected frames show handles / are deletion targets.
- Scale% numeric input for selected frame (UI 1-500, actual clamp 10-400).
- Toggle, opacity (0-100%), scope selection (all / selected). With scope=selected and no selection, fall back to all frames.
- Preview only. Included in history and persistence.
- FOV(mm) slider uses eqMm. Enabled only in capture view.
- Canvas Zoom input is 25-100%. Update viewZoom immediately on input.
- Viewport lens(mm) slider is for the normal camera in edit view. Range covers HFOV 10-120° considering render-box crop.
- Filename text (extension auto-added, use
camera-frameswhen blank). Format selection (PSD/PNG). Two toggles:- Grid/Eye-level overlay output toggle (one button controls both. PNG composites, PSD adds layers).
- Model layers toggle (PSD only). UI always visible; ignored when PNG is selected.
- Render button and progress spinner (disable button while rendering). Default selection is PSD.
- Collapsible section (default collapsed). Orbit/FPV toggle icons, position XYZ, rotation yaw/pitch/roll (with roll lock), local move sliders (right/up/forward, revert to 0 after use), near clip input.
- Alt for slow edit (nearClip step=0.1, precision=3; pose/position changes at 0.1x).
- While a numeric input is focused, pause transform auto-sync; resume on blur.
- In capture view, edits apply to mainCameraPose; in edit view, edits apply to the normal camera. Use the Capture Camera Controls panel to edit mainCameraPose while in edit view.
- nearClip input is shown/enabled only in capture view (in edit view it appears in the Capture Camera Controls panel).
- Insert
#camera-frames-overlayright after the canvas. Scale by devicePixelRatio. Default pointerEvents=none. - Render box: white dashed 1px. Frame: red 2px, with white dotted 1px overlay when selected. Handles: white fill + red edge 10px; rotation handle is 30px above the frame.
- Mask: Fill outside bounding boxes of target frames with black at specified opacity.
- For 90° step rotations, pixel-snap export outlines to draw sharply.
- When CAMERA FRAMES is ON or mainEditMode (panel open), draw the capture frustum in the debug layer with selection colors (selected=magenta, not selected=cyan). Non-interactive.
- Enable:
- Keep current viewport pose/FOV, set
camera.setLockFraming(true)andcamera.setLockFovAxis('horizontal'). Switch to capture view and lock composition. - If
mainCameraPoseis empty, capture current camera as baseline and apply it. Inherit baseFov from the camera and sync to UI. - Correct current near to a safe value and override. Through
initDefaultsIfNeeded, fill center to viewport center, compute fitScale, and add one frame if none exist. - Rebuild baseFrustum → sync frustum → redraw overlay → schedule near guard and update viewport lens state.
- Keep current viewport pose/FOV, set
- Disable:
- Save current pose to mainCameraPose, release near override and customFrustum, release lockFovAxis.
- If viewportPoseRuntime/viewportFovRuntime exist, restore them to the normal camera.
- Keep the state but stop follow logic. The Capture Camera Controls panel becomes available in edit view.
- Detect via
ResizeObserverandcamera.resize/cameraFrames.forceRefreshViewport. Update overlay size and scale in CSS px, runcomputeViewportMapping(true)to do AutoFit and center correction. Keep anchor screen position.
- On change: update viewScale → recalc frustum (extrapolate) → redraw overlay. Do not change fitScale.
- Visual behavior: lower viewZoom shrinks the render box and frames evenly, showing more scene around without black bars.
- Clamp to 100% or higher, each axis capped so baseSize does not exceed 16000px. Changes recorded as
cameraFrames.renderBoxScalein history (debounced). - Recalculate center based on anchor and update fitScale. Remap
frame.posso the logical center stays the same on screen. - When CAMERA FRAMES is enabled, update the camera aspect lock.
- Changing render-box anchor only switches the reference point for the next scale change. Does not alter current frustum or display (composition stays). UI/overlay updates immediately.
- Hit priority: Higher
orderis front. Handles active only when selected. - Move: Drag outline to update
pos. Hold Shift for axis lock (confirm after a certain distance). Convert between local and screen based on scale. - Scale: Drag handles for uniform scale. Hold Alt for symmetrical scaling around the frame’s anchor. Clamp to 10-400%.
- Rotate: Rotation handle 30px above. Shift for 15° snap. Double-click rotation handle to reset to 0°.
- Frame anchor edit: Drag anchor handle to update
frame.anchor. Double-click anchor handle to reset to frame center. - Selection: Click outline or list to select; click again to deselect. Selection is saved in snapshots.
- Shift + drag empty area to move the entire render box within the screen (
renderBox.centerupdate). Clamp overflow beyond viewport withPAN_MARGIN_PX=0.
- Draw only when enabled. With
scope='selected', use only the bounding boxes of selected frames; if none, fall back to all frames. Preview only.
- Pass through overlay when gizmo is hit. Disable overlay when CAMERA FRAMES is off.
- Overlay pointerEvents=auto only while hit. During drag show
grabbingcursor. Hold Shift to showgrabeven when no handle is selected. - On
lostpointercapture, commit drag history. Dragging/selecting the frustum itself is disabled.
- When set from UI, record to history with debounce (apply near override immediately in capture view). Input shown only in capture view or in the Capture Camera Controls panel.
- Use
computeSafeNearClipto ensure 0.01 or above, withinfar*0.1andboundRadius*0.5, and less thanfar. Schedule a guard to recheck a few frames later.
- When the FOV slider changes in capture view or the Capture Camera Controls panel, update
baseFovand rebuild baseFrustum. Display value is unaffected by renderBox scale or viewZoom. - In edit view, the viewport lens slider edits the normal camera FOV in mm (does not change baseFov). Range covers HFOV 10-120° considering render-box crop.
- eqMm calculation uses
DEFAULT_FRAME_BASEaspect. Use viewportFovRuntime to restore FOV when toggling CF ON/OFF.
- Camera operations while CAMERA FRAMES is enabled reflect into mainCameraPose (suppress updates during timeline playback). When disabled, camera operations are stored in viewportPoseRuntime and viewport lens display is updated.
- In capture view, edits to Transform/FOV/near/navMode apply only to mainCameraPose and are used when CAMERA FRAMES is enabled. In edit view, the Capture Camera Controls panel can still edit mainCameraPose and highlights the capture frustum.
- The Capture Camera Controls panel is available only when
!enabledandmainCameraPoseexists and not exporting.
outW = baseSize.w * scale.kx;
outH = baseSize.h * scale.ky;Independent of viewZoom and viewport size.
- Explicitly apply export frustum with
syncExportFrustum(width,height)(temporarily set targetSize). After finishing, restore targetSize/preview frustum. - Use
render.offscreen(width,height, options)for base render. Withoptions.overlaysOnly=true, disable World / shadow / gizmo / overlay to output only grid etc. UseunpremultiplyAlphato remove premultiplication. - Draw frame overlays (red outlines) on a separate canvas for PNG/PSD. Pixel-snap for 90° steps.
- Options: Grid/eye-level overlay (
exportGridOverlay). PNG composites; PSD adds layers (export.grid-layer.grid/export.grid-layer.eye-levelnames). - Options: Model layers (PSD when
exportModelLayers). Render each visible model individually, add with localized layer namepanel.camera-frames.export.model-layer. - For PSD, group frame layers per management name (capitalize first letter of frame ID, or
Framesif none). - After export, return to preview mode frustum (
syncCameraFrustum).
- Composite Base + (optional) grid/eye-level + frames. Composite grid with
destination-overso it sits behind. Add DPI=150 via pHYs chunk. Compress withPngCompressorand download. - To avoid double flip in
render.offscreen, flip bottom-up once before compression.
- Layer order top to bottom:
- Grid (when enabled)
- Eye level (when enabled)
- Model layers (when enabled, per visible model)
- Frame layer groups (grouped by management name, each layer is red outline only)
- Render (base render)
- PSD includes 150 PPI info and thumbnail. Mask is not included.
- Save/restore via
docSerialize.cameraFrames/docDeserialize.cameraFrames. - Fields to save:
enabled,renderBox(baseSize/scalePct/scale/anchor/center/fitScale/viewZoom/lastViewport/projection),frames,mask(including scope),nearClip,exportName,exportFormat,exportGridOverlay,exportModelLayers,exportTarget,exportPresetIds,selectedId,mainCameraPose,version(cameraFramesVersion). - On load:
- Support legacy
uiScale/viewScale/fovY. - Clamp scale to 100% or higher and 16000px or below. Normalize viewZoom to 25-100.
- Correct nearClip to a safe value. Reset selected according to
selectedId(first item if none). - If viewport info is missing, estimate AutoFit from current viewport.
- If projection.baseFov is missing, supplement from camera FOV. If mainCameraPose is missing, supplement from current camera.
- Support legacy
- Snapshot-based history. begin/commit on drag start/end; continuous edits are
debounced(250ms). - Targets:
- enabled toggle
- renderBox: scalePct/scale, anchor, center, fitScale, viewZoomPct, projection.baseFov
- frames: add/delete/select/pos/scalePct/rotationDeg/anchor/order, anchor & rotation reset
- mask: enabled/opacity/scope
- nearClip
- mainCameraPose (including capture view/Capture Camera Controls panel transform/nav/fov/near edits)
- render-box pan (Shift+drag)
- Export settings (exportName/exportFormat/exportGridOverlay/exportModelLayers/exportTarget/exportPresetIds) are not included in history.
- After applying history, resend
cameraFrames.stateChangedto sync UI and recalc overlay cursor.
- Handles state management, viewport mapping, frustum calculation, overlay drawing, pointer interactions, and export pipeline.
- Passes extrapolated frustum to
camera.setCustomFrustum; when CAMERA FRAMES is enabled, do not usecamera.rect/scissorRect. - Applies near override, attaches cameraFramesVersion display, hooks History, switches mainCameraPose/viewportPose, restores viewport lens.
- When CAMERA FRAMES is ON or mainEditMode (panel open), draws the capture frustum in debugLayer with selection colors. Suppress mainPose auto-update during timeline playback.
- Builds PCUI panel, validates inputs and fires events, handles panel move/collapse, render button and spinner control.
- Updates ranges for FOV(mm) / Viewport lens(mm), shows output resolution and viewport overflow warnings, manages grid/model layer toggles, handles view mode switching and Capture Camera Controls panel visibility, and routes transform inputs to the active camera.
- Apply values passed via
camera.setCustomFrustumto the projection matrix. When CAMERA FRAMES is enabled, lock aspect/horizontal FOV, and reset Rect/Scissor to 0,0,1,1 every frame. camera.setLockFraminglocks aspectRatio and reallocates render targets when targetSize changes.- In
scene.onPreRender, refer to aspectLock info; in postrender, resendcameraFrames.forceRefreshViewportto fix initial layout drift.
- Handles offscreen rendering and extraction of grid/eye-level overlays, premultiplied alpha removal (unpremultiplyAlpha option), and flip processing.
- With overlaysOnly, disable background/shadow/overlay/gizmo/World to return pure overlays.
- Resize invariance: Changing window width/height keeps anchor-based screen coordinates matching and composition fixed.
renderBox.centeris corrected. - Zoom back to 50%: Going from viewZoom 100 → 50 shrinks render box and frames evenly, shows surrounding scene without black bars.
- Anchor-based enlargement: With top-left anchor + width 150%, composition at top-left stays, FOV expands only to bottom-right. Y-inversion logic keeps vertical orientation correct.
- Repeated zoom × scale: Alternating render-box scale and viewZoom does not cause composition drift (recalculate every time).
- Preview = export: Export PNG/PSD with arbitrary settings; viewport render-box area matches pixels. viewZoom fixed at 100% for export. After export, frustum returns to preview.
- FOV display: Changing scale/zoom does not change FOV(mm) display; composition changes only when FOV is adjusted. HFOV clamped to 10-120°.
- Portrait composition: Changing baseSize to portrait still keeps FOV axis locked to horizontal. PlayCanvas does not auto switch to vertical.
- Frame operations: Move with Shift axis lock, Alt symmetric scale, 15° snap rotation, double-click to reset rotation/frame anchor. Hit priority is frontmost.
- Mask: With scope=selected only outside selected frames is darkened (fall back to all when none). Not included in export.
- Grid/eye-level output: When enabled at export, PNG composites overlays, PSD adds dedicated layers with no premultiply mismatch.
- Model layer output (PSD): With multiple visible models, PSD export adds per-model layers without mixing World/grid/gizmo.
- Undo/Redo: One step per drag start/end, continuous input combined every 250ms. Render box/viewZoom/FOV/nearClip/frame edits/mask/mainCameraPose are tracked; export settings are not.
- CAMERA FRAMES OFF lens/pose: In edit view, moving viewport lens(mm) changes normal camera FOV. Editing pose/FOV/near in the Capture Camera Controls panel draws the capture frustum for debug and applies when enabled.
Switched to frustum extension, so always reset camera.rect / camera.scissorRect to 0,0,1,1.
The normalized coordinate calculations using Rect/Scissor used until v3 are kept only as reference for px→world conversion validation.
rectNorm.x = rectPx.x / vw;
rectNorm.y = rectPx.y / vh;
rectNorm.w = rectPx.w / vw;
rectNorm.h = rectPx.h / vh;- viewZoom: 25-100 (%)
- renderBox scalePct: minimum 100%, maximum baseSize up to 16000px per axis
- frame scalePct: 10-400% (UI input 1-500% but actually clamped to 10-400)
- FOV: HFOV 10-120° clamp, eqMm varies with crop
- nearClip: 0 < near < far, corrected within far×0.1 / scene radius×0.5
- Frames: up to 20
- Handle size: 10px, rotation handle 30px above the frame