@dailies-studio/shot-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Mounted INSIDE `ShotRuntime`'s canvas, and only while a second pane is on
|
|
4
4
|
// screen. `renderControl: "external"` is what makes that legal — see the
|
|
5
|
-
// contract in packages/shot/src/ShotRuntime.tsx. `useFrame(…,
|
|
6
|
-
// the present over:
|
|
7
|
-
//
|
|
5
|
+
// contract in packages/shot/src/ShotRuntime.tsx. `useFrame(…, { phase:
|
|
6
|
+
// "render" })` is what hands the present over: fiber's own render job stands
|
|
7
|
+
// down whenever a user job occupies the render phase (`hasUserJobsInPhase`),
|
|
8
|
+
// the same mechanism ScenePresenter uses. The phase also guarantees the
|
|
9
|
+
// update-phase subscribers (camera write, user `useFrame` code) ran first —
|
|
10
|
+
// the v9 ordering that a legacy `useFrame(cb, 1)` no longer gives, because
|
|
11
|
+
// the v10 scheduler sorts priority DESCENDING within a phase.
|
|
8
12
|
//
|
|
9
13
|
// ── WHY THE CAMERA PANE'S POST GOES THROUGH A RENDER TARGET ──────────────
|
|
10
14
|
//
|
|
@@ -78,82 +82,171 @@
|
|
|
78
82
|
//
|
|
79
83
|
// ── BINS ─────────────────────────────────────────────────────────────────
|
|
80
84
|
//
|
|
81
|
-
// A scene with a `mesh` layer draws in several passes
|
|
82
|
-
// EVERY render here goes through
|
|
83
|
-
// `gl.render` / `pass()`
|
|
84
|
-
//
|
|
85
|
+
// A scene with a `mesh` layer draws in several passes, and a mask run draws
|
|
86
|
+
// as a nested matte (packages/shot/bins.ts). EVERY render here goes through
|
|
87
|
+
// `renderSteps` or a `SceneSource` rather than `gl.render` / `pass()`
|
|
88
|
+
// directly, so both panes agree with each other and with the export about what
|
|
89
|
+
// covers what. `renderSteps` degenerates to exactly one
|
|
85
90
|
// `gl.render` when there is nothing to split, so a scene with no model takes
|
|
86
91
|
// the identical call this file always made.
|
|
87
92
|
//
|
|
93
|
+
// ── THE PLAYBACK RENDER SCALE REACHES THE CAMERA PANE ALONE ──────────────
|
|
94
|
+
//
|
|
95
|
+
// While the transport plays and the stage's controller answers a step below 1
|
|
96
|
+
// (`presentationScale`, packages/shot/src/presentationScale.ts), the camera
|
|
97
|
+
// pane's offscreen draws (the transition capture, the post chain, and for a
|
|
98
|
+
// Shot with no post a plain pane target) run inside `withPresentationScale`,
|
|
99
|
+
// so every target they size from the renderer comes out scaled, and the blit
|
|
100
|
+
// into the pane upscales them. The working pane and its furniture keep the
|
|
101
|
+
// canvas's density. At 1 every draw is the one this file always made.
|
|
102
|
+
//
|
|
88
103
|
// ── NOTHING IS ALLOCATED PER FRAME ───────────────────────────────────────
|
|
89
104
|
//
|
|
90
|
-
// The rig,
|
|
91
|
-
// hot path is scissor rects and one uniform write.
|
|
92
|
-
import { useEffect, useMemo } from "react";
|
|
93
|
-
import { useFrame, useThree } from "@react-three/fiber";
|
|
105
|
+
// The rig, appearance-keyed clay, blit quad and scratch colour are all stable
|
|
106
|
+
// between changes. The hot path is scissor rects and one uniform write.
|
|
107
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
108
|
+
import { useFrame, useStore, useThree } from "@react-three/fiber";
|
|
94
109
|
import { Color, ColorManagement, HalfFloatType, Layers, RenderTarget } from "three";
|
|
110
|
+
import { isInsideOutShell } from "./sceneExtent.js";
|
|
95
111
|
import { MeshBasicNodeMaterial, QuadMesh, RenderPipeline } from "three/webgpu";
|
|
96
|
-
import type { ColorSpace } from "three";
|
|
112
|
+
import type { Camera, ColorSpace, Material, Object3D, Scene } from "three";
|
|
97
113
|
import type { WebGPURenderer } from "three/webgpu";
|
|
98
114
|
import { texture, uv } from "three/tsl";
|
|
99
115
|
import {
|
|
116
|
+
PRESENTER_RIGS_MAX,
|
|
117
|
+
PresentationSchedule,
|
|
100
118
|
binSceneSource,
|
|
101
119
|
buildPostChain,
|
|
120
|
+
compileObjectForTarget,
|
|
121
|
+
compileRenderPipelineForTarget,
|
|
122
|
+
devicePixels,
|
|
123
|
+
drawScaleOf,
|
|
102
124
|
hasPostChain,
|
|
103
125
|
passSceneSource,
|
|
126
|
+
pendingFrameError,
|
|
127
|
+
pieceOwner,
|
|
104
128
|
postSignature,
|
|
105
|
-
|
|
129
|
+
presenterShapesOf,
|
|
130
|
+
registerSupplementalFrameCompile,
|
|
131
|
+
renderSteps,
|
|
106
132
|
transitionTap,
|
|
133
|
+
usePieceFailures,
|
|
134
|
+
withPresentationScale,
|
|
107
135
|
} from "@dailies-studio/shot";
|
|
108
|
-
import type {
|
|
136
|
+
import type { PieceFailureInput, PresentationScaleSource, RenderStep, SceneEffects, SceneSource } from "@dailies-studio/shot";
|
|
137
|
+
import { componentDefsOfCached, isCompositeStep } from "@dailies-studio/shot/doc";
|
|
138
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
109
139
|
import type { StageFeed } from "./feed.js";
|
|
110
140
|
import type { PaneRect } from "./panes.js";
|
|
111
141
|
import { workingDrawPlan } from "./shading.js";
|
|
112
142
|
import type { ShadingMode } from "./shading.js";
|
|
113
143
|
import { EDITOR_LAYER } from "./StageTools.js";
|
|
144
|
+
import { tokenColor } from "./tokenColor.js";
|
|
145
|
+
|
|
146
|
+
/** A CSS colour as a three `Color`, or `fallback` when three cannot parse it. */
|
|
147
|
+
function tokenColorOf(css: string, fallback: Color): Color {
|
|
148
|
+
try {
|
|
149
|
+
return new Color(css);
|
|
150
|
+
} catch {
|
|
151
|
+
return fallback;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
import type { StageTheme } from "./stageTheme.js";
|
|
114
155
|
|
|
115
156
|
/**
|
|
116
157
|
* The working pane's backdrop and its clay.
|
|
117
158
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
159
|
+
* They are GL colours, so each is resolved from a CONCRETE CSS token when the
|
|
160
|
+
* appearance changes. The camera pane never reads them: it keeps the shot's
|
|
161
|
+
* authored clear colour and remains identical to export.
|
|
121
162
|
*/
|
|
122
|
-
export const CLAY_BACKDROP = new Color(0x141417);
|
|
123
|
-
const CLAY_FILL = new Color(0x8a8a93);
|
|
124
|
-
/** The wireframe's line colour. Brighter than the clay so edges read as lines
|
|
125
|
-
* on the same backdrop rather than as a dimmer clay. */
|
|
126
|
-
const WIRE_FILL = new Color(0xb8b8c2);
|
|
127
|
-
|
|
128
163
|
/** Scratch for saving the shot's own clear colour across the working pane's. */
|
|
129
164
|
const _prevClear = new Color();
|
|
165
|
+
const NORMAL_WORKING_COMPILE_MODE = {};
|
|
166
|
+
/** No steps: `renderSteps`' single plain render. */
|
|
167
|
+
const NO_STEPS: readonly RenderStep[] = Object.freeze([]);
|
|
130
168
|
|
|
131
169
|
export interface SplitRendererProps {
|
|
170
|
+
/** Resolved editor theme. It rebuilds only the working-view palette. */
|
|
171
|
+
theme: StageTheme;
|
|
132
172
|
feed: StageFeed;
|
|
173
|
+
/** The document on stage: how many post shapes its Shots use sizes the rig
|
|
174
|
+
* cache. */
|
|
175
|
+
doc: Doc3D;
|
|
133
176
|
panes: { working: PaneRect | null; camera: PaneRect | null };
|
|
134
177
|
/** The active scene's effects, or null when nothing is on screen. */
|
|
135
178
|
effects: SceneEffects | null;
|
|
136
179
|
/** The active scene's render bins at this frame. Empty or single = one draw,
|
|
137
180
|
* which is every scene without a live mesh layer. */
|
|
138
|
-
bins: readonly
|
|
139
|
-
/** Whether the scene contains a mesh layer AT ALL
|
|
140
|
-
* ignored), because it picks the post
|
|
141
|
-
|
|
181
|
+
bins: readonly RenderStep[];
|
|
182
|
+
/** Whether the scene contains a mesh or a mask layer AT ALL
|
|
183
|
+
* (`sceneNeedsBins`). Static (lifetimes ignored), because it picks the post
|
|
184
|
+
* chain's SHAPE — see ShotRuntime. */
|
|
185
|
+
needsBins: boolean;
|
|
142
186
|
/** MASTER frame — the grain's seed, and the only clock the chain sees. */
|
|
143
187
|
frame: number;
|
|
188
|
+
/** The frame-exact readers (StageApp's `playhead`): the post values, the
|
|
189
|
+
* bins and the master frame at DRAW time. `effects`, `bins` and `frame`
|
|
190
|
+
* above are the render's snapshot, which is right about the post chain's
|
|
191
|
+
* SHAPE and the bins' kind and may be a frame behind on their values;
|
|
192
|
+
* absent, the snapshot is the frame (the test harness). */
|
|
193
|
+
live?: {
|
|
194
|
+
at(): { masterFrame: number; effects: SceneEffects | null; bins: readonly RenderStep[]; sceneId?: string | null };
|
|
195
|
+
};
|
|
144
196
|
/** How the WORKING pane draws. The camera pane is not addressable: it is
|
|
145
197
|
* always the graded render, whatever this says. */
|
|
146
198
|
shading: ShadingMode;
|
|
199
|
+
/** The Shot the effects belong to, for a post chain that fails to build or
|
|
200
|
+
* to draw: it is reported as that Shot's post stack (pieceFailures.ts). */
|
|
201
|
+
sceneId?: string | null;
|
|
202
|
+
/** The component preview's backdrop (bridge v45), a CSS colour the working
|
|
203
|
+
* pane clears with in place of `--working-bg`. Absent or null = the
|
|
204
|
+
* token. */
|
|
205
|
+
backdrop?: string | null;
|
|
206
|
+
/** The playback render scale's source (the stage's controller), asked once
|
|
207
|
+
* per played frame for the camera pane: see the header. Absent, nothing
|
|
208
|
+
* scales. */
|
|
209
|
+
presentationScale?: PresentationScaleSource;
|
|
210
|
+
/** The transport plays and no preparing pass draws: the only frames that
|
|
211
|
+
* scale. */
|
|
212
|
+
playing?: boolean;
|
|
147
213
|
}
|
|
148
214
|
|
|
149
|
-
export function SplitRenderer({
|
|
215
|
+
export function SplitRenderer({
|
|
216
|
+
theme,
|
|
217
|
+
feed,
|
|
218
|
+
doc,
|
|
219
|
+
panes,
|
|
220
|
+
effects,
|
|
221
|
+
bins: snapshotBins,
|
|
222
|
+
needsBins,
|
|
223
|
+
frame,
|
|
224
|
+
live,
|
|
225
|
+
shading,
|
|
226
|
+
sceneId = null,
|
|
227
|
+
backdrop = null,
|
|
228
|
+
presentationScale,
|
|
229
|
+
playing = false,
|
|
230
|
+
}: SplitRendererProps): null {
|
|
150
231
|
// The runtime class IS `WebGPURenderer` (createStageGl builds one, WebGL2 is
|
|
151
232
|
// a BACKEND of it, not a different class); r3f types `gl` as the classic
|
|
152
233
|
// renderer, so the cast happens once here rather than at five call sites.
|
|
153
234
|
const gl = useThree((s) => s.gl) as unknown as WebGPURenderer;
|
|
235
|
+
const store = useStore();
|
|
154
236
|
const scene = useThree((s) => s.scene);
|
|
155
237
|
const shotCamera = useThree((s) => s.camera);
|
|
156
238
|
const size = useThree((s) => s.size);
|
|
239
|
+
const workingPalette = useMemo(
|
|
240
|
+
() => ({
|
|
241
|
+
backdrop: tokenColor("--working-bg", "#141414"),
|
|
242
|
+
clay: tokenColor("--working-clay", "#8a8a93"),
|
|
243
|
+
wire: tokenColor("--working-wire", "#b8b8c2"),
|
|
244
|
+
}),
|
|
245
|
+
[theme],
|
|
246
|
+
);
|
|
247
|
+
// The preview's backdrop over the token, or the token when the colour is
|
|
248
|
+
// not one three can parse (`tokenColor`'s rule).
|
|
249
|
+
const clearBackdrop = useMemo(() => (backdrop === null ? workingPalette.backdrop : tokenColorOf(backdrop, workingPalette.backdrop)), [backdrop, workingPalette]);
|
|
157
250
|
|
|
158
251
|
/**
|
|
159
252
|
* The clay override, ONE dedicated instance and never shared.
|
|
@@ -161,7 +254,8 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
161
254
|
* `depthTest`/`depthWrite` stay OFF, matching every layer material. It is
|
|
162
255
|
* tempting to turn them on "because the working pane is the 3D one", and it
|
|
163
256
|
* would be a second truth: this lane's paint order is `renderOrder =
|
|
164
|
-
* layer.order
|
|
257
|
+
* layer.order`, the layer lists' walk, and nothing else (see ShotRuntime's
|
|
258
|
+
* header), so a depth-tested
|
|
165
259
|
* working pane would disagree with the camera pane about what covers what —
|
|
166
260
|
* and, because layers default to z = 0, would z-fight the common case.
|
|
167
261
|
*
|
|
@@ -172,7 +266,7 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
172
266
|
*/
|
|
173
267
|
const overrides = useMemo(() => {
|
|
174
268
|
const clay = new MeshBasicNodeMaterial();
|
|
175
|
-
clay.color.copy(
|
|
269
|
+
clay.color.copy(workingPalette.clay);
|
|
176
270
|
clay.depthTest = false;
|
|
177
271
|
clay.depthWrite = false;
|
|
178
272
|
clay.toneMapped = false;
|
|
@@ -180,13 +274,13 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
180
274
|
// depth/tone rationale above holds unchanged, because a wireframe pane's
|
|
181
275
|
// paint order is still `renderOrder` and nothing else.
|
|
182
276
|
const wire = new MeshBasicNodeMaterial();
|
|
183
|
-
wire.color.copy(
|
|
277
|
+
wire.color.copy(workingPalette.wire);
|
|
184
278
|
wire.wireframe = true;
|
|
185
279
|
wire.depthTest = false;
|
|
186
280
|
wire.depthWrite = false;
|
|
187
281
|
wire.toneMapped = false;
|
|
188
282
|
return { clay, wire };
|
|
189
|
-
}, []);
|
|
283
|
+
}, [workingPalette]);
|
|
190
284
|
useEffect(
|
|
191
285
|
() => () => {
|
|
192
286
|
overrides.clay.dispose();
|
|
@@ -195,66 +289,88 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
195
289
|
[overrides],
|
|
196
290
|
);
|
|
197
291
|
|
|
198
|
-
const signature = effects === null ? "" : `${postSignature(effects)}::${
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
pipeline.outputColorTransform = false; // see the colour note in the header
|
|
214
|
-
// Half float, working space: the chain's output is scene-referred light and
|
|
215
|
-
// an 8-bit sRGB hop here would band every gradient the bloom touches.
|
|
216
|
-
const target = new RenderTarget(1, 1, {
|
|
217
|
-
type: HalfFloatType,
|
|
218
|
-
// Read off ColorManagement rather than written as `LinearSRGBColorSpace`:
|
|
219
|
-
// the pipeline forces the WORKING space for the chain, so this target has
|
|
220
|
-
// to be whatever that is, not whatever it is today.
|
|
221
|
-
colorSpace: ColorManagement.workingColorSpace as ColorSpace,
|
|
222
|
-
});
|
|
223
|
-
const material = new MeshBasicNodeMaterial();
|
|
224
|
-
material.colorNode = texture(target.texture, uv());
|
|
225
|
-
material.depthTest = false;
|
|
226
|
-
material.depthWrite = false;
|
|
227
|
-
material.toneMapped = false;
|
|
228
|
-
// THREE'S OWN full-screen primitive, not a hand-rolled plane. Its geometry
|
|
229
|
-
// carries the v-FLIPPED uv three uses everywhere it blits a render target
|
|
230
|
-
// (`QuadGeometry`: the bottom-left vertex is uv 0,1), and its camera covers
|
|
231
|
-
// NDC exactly — so uv 0..1 lands on the current VIEWPORT, whatever pane
|
|
232
|
-
// that is, and the image comes out the same way up on both backends. A
|
|
233
|
-
// `PlaneGeometry` looks equivalent and is upside down.
|
|
234
|
-
const blit = new QuadMesh(material);
|
|
235
|
-
return { source, chain, pipeline, target, material, blit };
|
|
236
|
-
// `effects` is deliberately not a dependency: its VALUES are uniforms
|
|
237
|
-
// written every frame, and only the SIGNATURE changes the graph's shape.
|
|
238
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
239
|
-
}, [gl, scene, shotCamera, signature]);
|
|
240
|
-
|
|
292
|
+
const signature = effects === null ? "" : `${postSignature(effects)}::${needsBins}`;
|
|
293
|
+
// ── THE RIGS ARE KEPT BY SHAPE ──────────────────────────────────────────
|
|
294
|
+
//
|
|
295
|
+
// `PresenterRigCache`'s reason (ShotRuntime.tsx), in the layouts it does
|
|
296
|
+
// not reach: a cut to another post shape built a new chain and its
|
|
297
|
+
// pipelines on the cut's first frame and disposed the old one, so every
|
|
298
|
+
// cut back paid again, on every loop. Kept here, a shape that comes back
|
|
299
|
+
// finds its rig, and with it the registration identity ClockDriver already
|
|
300
|
+
// compiled. Sized to the film's shapes and capped as the presenter's is:
|
|
301
|
+
// each rig holds pane- and canvas-sized targets. One cache per renderer,
|
|
302
|
+
// scene and camera, since a rig is built against all three.
|
|
303
|
+
const rigs = useMemo(
|
|
304
|
+
() => ({ camera: new SplitRigCache(releaseCameraPost), working: new SplitRigCache(releaseWorkingPost) }),
|
|
305
|
+
[gl, scene, shotCamera],
|
|
306
|
+
);
|
|
241
307
|
useEffect(
|
|
242
308
|
() => () => {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
post.pipeline.dispose();
|
|
246
|
-
post.source.dispose();
|
|
247
|
-
post.target.dispose();
|
|
248
|
-
post.material.dispose();
|
|
309
|
+
rigs.camera.dispose();
|
|
310
|
+
rigs.working.dispose();
|
|
249
311
|
},
|
|
250
|
-
[
|
|
312
|
+
[rigs],
|
|
313
|
+
);
|
|
314
|
+
const rigCapacity = useMemo(
|
|
315
|
+
() => Math.min(PRESENTER_RIGS_MAX, Math.max(1, presenterShapesOf(doc, componentDefsOfCached(doc)))),
|
|
316
|
+
[doc],
|
|
251
317
|
);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
rigs.camera.resize(rigCapacity);
|
|
320
|
+
rigs.working.resize(rigCapacity);
|
|
321
|
+
}, [rigs, rigCapacity]);
|
|
322
|
+
// ── A POST STACK THAT FAILS IS ONE PIECE ────────────────────────────────
|
|
323
|
+
//
|
|
324
|
+
// With the runtime's isolation log (pieceFailures.ts), a chain that cannot
|
|
325
|
+
// be built leaves its pane ungraded and a pass that throws while drawing
|
|
326
|
+
// leaves that frame ungraded; both are reported as the Shot's post stack.
|
|
327
|
+
// Without one, the throw is the stage's, as before. A rig that threw is
|
|
328
|
+
// never kept.
|
|
329
|
+
const pieces = usePieceFailures();
|
|
330
|
+
const postBuild = useMemo((): { rig: CameraPost | null; error: unknown } => {
|
|
331
|
+
if (effects === null || !hasPostChain(effects)) return { rig: null, error: null };
|
|
332
|
+
const build = (): CameraPost => rigs.camera.acquire(signature, () => buildCameraPost(gl, scene, shotCamera, needsBins, effects));
|
|
333
|
+
if (pieces === null) return { rig: build(), error: null };
|
|
334
|
+
try {
|
|
335
|
+
return { rig: build(), error: null };
|
|
336
|
+
} catch (error) {
|
|
337
|
+
return { rig: null, error };
|
|
338
|
+
}
|
|
339
|
+
// `effects` is deliberately not a dependency: its VALUES are uniforms
|
|
340
|
+
// written every frame, and only the SIGNATURE changes the graph's shape.
|
|
341
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
342
|
+
}, [gl, scene, shotCamera, signature, pieces, rigs]);
|
|
343
|
+
const post = postBuild.rig;
|
|
344
|
+
|
|
345
|
+
useLayoutEffect(() => {
|
|
346
|
+
if (post === null) return;
|
|
347
|
+
const source = post.source;
|
|
348
|
+
return registerSupplementalFrameCompile(
|
|
349
|
+
gl,
|
|
350
|
+
post,
|
|
351
|
+
async () => {
|
|
352
|
+
await source.compile(gl);
|
|
353
|
+
await compileRenderPipelineForTarget(gl, post.pipeline, post.target);
|
|
354
|
+
await compileObjectForTarget(gl, post.blit, post.blit.camera, null);
|
|
355
|
+
},
|
|
356
|
+
[post.blit],
|
|
357
|
+
// THE UPCOMING SCENE, AND A CHANGED OBJECT, UNDER THIS SOURCE'S OWN
|
|
358
|
+
// TARGET, as ScenePresenter registers its source. Without it a cut's
|
|
359
|
+
// warm-up compiled nothing for this target, and ClockDriver's targeted
|
|
360
|
+
// path compiled a registration with no warm WHOLE, the live scene,
|
|
361
|
+
// while the played frame waited.
|
|
362
|
+
source.compileWarm === undefined
|
|
363
|
+
? undefined
|
|
364
|
+
: (warmScene, warmCamera, subject, side) =>
|
|
365
|
+
side === "outgoing" ? Promise.resolve() : source.compileWarm!(gl, warmScene, warmCamera, subject),
|
|
366
|
+
);
|
|
367
|
+
}, [gl, post]);
|
|
252
368
|
|
|
253
369
|
/**
|
|
254
370
|
* The WORKING pane's own post rig, for "rendered" shading. Mirrors `post`
|
|
255
371
|
* with two deliberate differences:
|
|
256
372
|
*
|
|
257
|
-
* 1. Its OWN source target, drawn by `
|
|
373
|
+
* 1. Its OWN source target, drawn by `renderSteps` at FRAME time with
|
|
258
374
|
* `feed.workingCamera` — never `pass()`/`binSceneSource`, which bake a
|
|
259
375
|
* camera at build time. The working slot's camera OBJECT is swapped by
|
|
260
376
|
* isolation (StageTools puts an orthographic head-on rig in it), so the
|
|
@@ -270,62 +386,199 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
270
386
|
* the source to the pane does not reach that, and no call here can — it
|
|
271
387
|
* needs a pass whose target aspect may differ from the canvas's.
|
|
272
388
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
389
|
+
* Kept only while rendered shading is on: solid users pay nothing, and
|
|
390
|
+
* leaving the mode disposes its rigs. Kept by the EFFECTS signature alone
|
|
391
|
+
* and not by `signature`, which carries `needsBins`: that bit picks the
|
|
392
|
+
* camera pane's SOURCE KIND (bin vs pass) and this rig has neither (it
|
|
393
|
+
* always draws its own target with `renderSteps`), so folding it in would
|
|
394
|
+
* build and compile another working pipeline every time a mesh or mask
|
|
395
|
+
* layer entered or left the scene.
|
|
280
396
|
*/
|
|
281
|
-
|
|
397
|
+
// THROUGH THE PLAN, never a second `=== "rendered"`: what a mode means is
|
|
398
|
+
// shading.ts's to say, and the draw below already asks it there. `hasChain`
|
|
399
|
+
// is true here because whether a chain EXISTS is the next line's question,
|
|
400
|
+
// not this one's.
|
|
401
|
+
const wantWorkingGrade = workingDrawPlan(shading, true).graded;
|
|
282
402
|
const workingSignature = effects === null ? "" : postSignature(effects);
|
|
283
|
-
const
|
|
284
|
-
if (!wantWorkingGrade || effects === null || !hasPostChain(effects)) return null;
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const target = new RenderTarget(1, 1, {
|
|
293
|
-
type: HalfFloatType,
|
|
294
|
-
colorSpace: ColorManagement.workingColorSpace as ColorSpace,
|
|
295
|
-
});
|
|
296
|
-
const material = new MeshBasicNodeMaterial();
|
|
297
|
-
material.colorNode = texture(target.texture, uv());
|
|
298
|
-
material.depthTest = false;
|
|
299
|
-
material.depthWrite = false;
|
|
300
|
-
material.toneMapped = false;
|
|
301
|
-
const blit = new QuadMesh(material);
|
|
302
|
-
return { src, chain, pipeline, target, material, blit };
|
|
403
|
+
const wpostBuild = useMemo((): { rig: WorkingPost | null; error: unknown } => {
|
|
404
|
+
if (!wantWorkingGrade || effects === null || !hasPostChain(effects)) return { rig: null, error: null };
|
|
405
|
+
const build = (): WorkingPost => rigs.working.acquire(workingSignature, () => buildWorkingPost(gl, effects));
|
|
406
|
+
if (pieces === null) return { rig: build(), error: null };
|
|
407
|
+
try {
|
|
408
|
+
return { rig: build(), error: null };
|
|
409
|
+
} catch (error) {
|
|
410
|
+
return { rig: null, error };
|
|
411
|
+
}
|
|
303
412
|
// Same rule as `post`: `effects` values are uniforms written per frame,
|
|
304
413
|
// only the SIGNATURE changes the graph's shape.
|
|
305
414
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
306
|
-
}, [gl, workingSignature, wantWorkingGrade]);
|
|
307
|
-
|
|
415
|
+
}, [gl, workingSignature, wantWorkingGrade, pieces, rigs]);
|
|
416
|
+
const wpost = wpostBuild.rig;
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
if (!wantWorkingGrade) rigs.working.dispose();
|
|
419
|
+
}, [rigs, wantWorkingGrade]);
|
|
420
|
+
// A chain that could not be built, reported once for the two panes (they
|
|
421
|
+
// build the same effects): the pane that failed draws ungraded.
|
|
422
|
+
const buildOwner = useMemo(() => pieceOwner("split-post"), []);
|
|
423
|
+
const drawOwner = useMemo(() => pieceOwner("split-post-draw"), []);
|
|
424
|
+
const buildError = postBuild.error ?? wpostBuild.error;
|
|
425
|
+
useLayoutEffect(() => {
|
|
426
|
+
if (pieces === null) return;
|
|
427
|
+
pieces.set(buildOwner, buildError === null ? [] : [postPiece(sceneId, buildError)]);
|
|
428
|
+
}, [pieces, buildOwner, buildError, sceneId]);
|
|
429
|
+
useLayoutEffect(
|
|
430
|
+
() => () => {
|
|
431
|
+
pieces?.set(buildOwner);
|
|
432
|
+
pieces?.set(drawOwner);
|
|
433
|
+
},
|
|
434
|
+
[pieces, buildOwner, drawOwner],
|
|
435
|
+
);
|
|
436
|
+
/** A pass that threw while drawing, until a frame draws clean. */
|
|
437
|
+
const drawFailed = useRef(false);
|
|
438
|
+
/** When the camera pane's scale may move (cuts, transition windows). */
|
|
439
|
+
const [schedule] = useState(() => new PresentationSchedule());
|
|
440
|
+
/** The camera pane's plain draw below 1 (step 1a), made with a scale source
|
|
441
|
+
* so its blit compiles ahead, as the grade's blit does (below): built at
|
|
442
|
+
* the first scaled frame instead, nothing else had built that blit's
|
|
443
|
+
* pipeline on a film with no post, and the frame built it synchronously.
|
|
444
|
+
* Its target holds memory only while the scale is below 1. */
|
|
445
|
+
const scales = presentationScale !== undefined;
|
|
446
|
+
const plainPane = useMemo(() => (scales ? buildPlainPane() : null), [gl, scales]);
|
|
308
447
|
useEffect(
|
|
309
448
|
() => () => {
|
|
310
|
-
if (
|
|
311
|
-
wpost.chain.dispose();
|
|
312
|
-
wpost.pipeline.dispose();
|
|
313
|
-
wpost.src.dispose();
|
|
314
|
-
wpost.target.dispose();
|
|
315
|
-
wpost.material.dispose();
|
|
449
|
+
if (plainPane !== null) releasePlainPane(plainPane);
|
|
316
450
|
},
|
|
317
|
-
[
|
|
451
|
+
[plainPane],
|
|
318
452
|
);
|
|
453
|
+
useLayoutEffect(() => {
|
|
454
|
+
if (plainPane === null) return;
|
|
455
|
+
return registerSupplementalFrameCompile(
|
|
456
|
+
gl,
|
|
457
|
+
plainPane,
|
|
458
|
+
() => compileObjectForTarget(gl, plainPane.blit, plainPane.blit.camera, null),
|
|
459
|
+
[plainPane.blit],
|
|
460
|
+
);
|
|
461
|
+
}, [gl, plainPane]);
|
|
462
|
+
|
|
463
|
+
useLayoutEffect(() => {
|
|
464
|
+
if (wpost === null) return;
|
|
465
|
+
return registerSupplementalFrameCompile(
|
|
466
|
+
gl,
|
|
467
|
+
wpost,
|
|
468
|
+
async () => {
|
|
469
|
+
await compileRenderPipelineForTarget(gl, wpost.pipeline, wpost.target);
|
|
470
|
+
await compileObjectForTarget(gl, wpost.blit, wpost.blit.camera, null);
|
|
471
|
+
},
|
|
472
|
+
[wpost.blit],
|
|
473
|
+
);
|
|
474
|
+
}, [gl, wpost]);
|
|
475
|
+
|
|
476
|
+
// ONE READING OF THE PLAN for both the compile mode and the compile's own
|
|
477
|
+
// override below. They were two hand-written copies of shading.ts's
|
|
478
|
+
// `override` arm, so a fifth mode (or moving an existing one's override)
|
|
479
|
+
// compiled clean here and left the warm path disagreeing with the draw path,
|
|
480
|
+
// which shows up as a first-frame stall rather than as an error.
|
|
481
|
+
const workingOverride = ((tag) =>
|
|
482
|
+
tag === "wire" ? overrides.wire : tag === "clay" ? overrides.clay : null)(
|
|
483
|
+
workingDrawPlan(shading, wpost !== null).override,
|
|
484
|
+
);
|
|
485
|
+
const workingCompileMode: object = workingOverride ?? wpost ?? NORMAL_WORKING_COMPILE_MODE;
|
|
486
|
+
const workingCompileIdentity = useRef<{
|
|
487
|
+
camera: Camera | null;
|
|
488
|
+
mode: object;
|
|
489
|
+
identity: object;
|
|
490
|
+
}>({ camera: null, mode: NORMAL_WORKING_COMPILE_MODE, identity: {} });
|
|
491
|
+
useLayoutEffect(() => {
|
|
492
|
+
if (panes.working === null) return;
|
|
493
|
+
const workingCamera = feed.prepareWorkingCamera?.() ?? feed.workingCamera;
|
|
494
|
+
if (workingCamera === null) return;
|
|
495
|
+
if (
|
|
496
|
+
workingCompileIdentity.current.camera !== workingCamera ||
|
|
497
|
+
workingCompileIdentity.current.mode !== workingCompileMode
|
|
498
|
+
) {
|
|
499
|
+
workingCompileIdentity.current = {
|
|
500
|
+
camera: workingCamera,
|
|
501
|
+
mode: workingCompileMode,
|
|
502
|
+
identity: {},
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const identity = workingCompileIdentity.current.identity;
|
|
506
|
+
const override = workingOverride;
|
|
507
|
+
const source = wpost;
|
|
508
|
+
// One compile shape for the live scene and for the upcoming one: the same
|
|
509
|
+
// targets, the same masks, the same override, so a cut finds every
|
|
510
|
+
// working-pane pipeline resident (see `WarmCompile`).
|
|
511
|
+
const compileScene = async (target: Scene, camera: Camera, subject: Object3D): Promise<void> => {
|
|
512
|
+
if (source !== null) {
|
|
513
|
+
// This is the target the priority-1 draw actually fills before the
|
|
514
|
+
// working post chain reads it. A canvas compile is not equivalent:
|
|
515
|
+
// target format and samples are part of Three's pipeline context.
|
|
516
|
+
source.src.samples = gl.samples;
|
|
517
|
+
source.src.texture.type = gl.getOutputBufferType();
|
|
518
|
+
await compileObjectForTarget(
|
|
519
|
+
gl,
|
|
520
|
+
subject,
|
|
521
|
+
camera,
|
|
522
|
+
source.src,
|
|
523
|
+
target,
|
|
524
|
+
() => configureWorkingSceneCompile(target, camera, 1, null),
|
|
525
|
+
);
|
|
526
|
+
// Rendered shading overpaints the editor layer after the graded blit.
|
|
527
|
+
// It is a separate canvas draw and therefore a separate compile
|
|
528
|
+
// context from the half-float source above.
|
|
529
|
+
await compileObjectForTarget(
|
|
530
|
+
gl,
|
|
531
|
+
subject,
|
|
532
|
+
camera,
|
|
533
|
+
null,
|
|
534
|
+
target,
|
|
535
|
+
() => configureWorkingSceneCompile(target, camera, 1 << EDITOR_LAYER, null),
|
|
536
|
+
);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const mask = workingCamera.layers.mask;
|
|
540
|
+
await compileObjectForTarget(
|
|
541
|
+
gl,
|
|
542
|
+
subject,
|
|
543
|
+
camera,
|
|
544
|
+
null,
|
|
545
|
+
target,
|
|
546
|
+
() => configureWorkingSceneCompile(target, camera, mask, override),
|
|
547
|
+
);
|
|
548
|
+
};
|
|
549
|
+
return registerSupplementalFrameCompile(
|
|
550
|
+
gl,
|
|
551
|
+
identity,
|
|
552
|
+
() => compileScene(scene, workingCamera, scene),
|
|
553
|
+
[],
|
|
554
|
+
(warmScene, warmCamera, subject, side) =>
|
|
555
|
+
side === "outgoing" ? Promise.resolve() : compileScene(warmScene, warmCamera, subject),
|
|
556
|
+
);
|
|
557
|
+
// Deliberately every commit. StageTools publishes the current camera in a
|
|
558
|
+
// layout effect rather than React state. Re-registering the SAME identity
|
|
559
|
+
// updates this closure without forcing a compile; a camera or draw-mode
|
|
560
|
+
// change mints an identity above and makes ClockDriver compile once.
|
|
561
|
+
});
|
|
319
562
|
|
|
320
563
|
// The props are read straight out of the closure. r3f re-points `useFrame`'s
|
|
321
564
|
// callback ref in a layout effect on every render, and `ClockDriver` — the
|
|
322
565
|
// LAST child of the scene — is what calls `advance`, so the callback that
|
|
323
566
|
// draws is always the one this render produced.
|
|
324
567
|
useFrame(() => {
|
|
568
|
+
// A subscriber in this tick threw (the error is parked, ClockDriver
|
|
569
|
+
// consumes it right after advance): do NOT present the crashed frame.
|
|
570
|
+
// The canvas keeps the last good picture, matching the v9 latch.
|
|
571
|
+
// Isolating, the throw is one piece (ClockDriver records it) and the
|
|
572
|
+
// frame draws around it, so the rest of the Shot keeps moving.
|
|
573
|
+
if (pieces === null && pendingFrameError(store)) return;
|
|
325
574
|
const working = panes.working;
|
|
326
575
|
const camera = panes.camera;
|
|
327
|
-
|
|
328
|
-
|
|
576
|
+
// The values of THIS draw's frame, when the stage drives by clock; the
|
|
577
|
+
// props' otherwise. The chain's shape came from the props either way.
|
|
578
|
+
const now = live?.at();
|
|
579
|
+
const fx = now === undefined ? effects : effects === null ? null : now.effects;
|
|
580
|
+
const at = now === undefined ? frame : now.masterFrame;
|
|
581
|
+
const bins = now === undefined ? snapshotBins : now.bins;
|
|
329
582
|
const workingCamera = feed.workingCamera;
|
|
330
583
|
|
|
331
584
|
// The shot's own clear colour is ShotBackground's, written on a layout
|
|
@@ -341,33 +594,59 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
341
594
|
// for the same reason, at the PANE's size so the blend is not stretched
|
|
342
595
|
// across a frustum it was not rendered for. Without this the editor
|
|
343
596
|
// would show hard cuts in split view where the export dissolves.
|
|
344
|
-
|
|
597
|
+
// A capture that threw answers false (the compositor reported it as
|
|
598
|
+
// the transition): the pane shows the incoming Shot, a hard cut.
|
|
599
|
+
let blend = camera === null ? null : transitionTap();
|
|
600
|
+
// The camera pane's playback scale (header): asked once per played frame,
|
|
601
|
+
// and held through a transition window by the schedule.
|
|
602
|
+
let scale = 1;
|
|
603
|
+
if (presentationScale !== undefined && playing && camera !== null) {
|
|
604
|
+
scale = schedule.next(presentationScale, now?.sceneId ?? sceneId, blend !== null, at);
|
|
605
|
+
} else schedule.stop();
|
|
345
606
|
if (blend !== null && camera !== null) {
|
|
346
607
|
gl.setScissorTest(false);
|
|
347
|
-
blend
|
|
608
|
+
const tap = blend;
|
|
609
|
+
let captured = true;
|
|
610
|
+
withPresentationScale(gl, scale, () => {
|
|
611
|
+
captured = tap.capture(camera.width, camera.height);
|
|
612
|
+
});
|
|
613
|
+
if (!captured) blend = null;
|
|
348
614
|
}
|
|
349
615
|
|
|
616
|
+
// A post pass that throws while drawing leaves its pane ungraded for this
|
|
617
|
+
// frame, and is reported (see `postBuild`). Without the log it throws.
|
|
618
|
+
// Inline try blocks and module functions, not closures: this runs every
|
|
619
|
+
// frame. The one closure per offscreen draw is the scale's seam
|
|
620
|
+
// (`withPresentationScale`), which at 1 only calls it.
|
|
621
|
+
let drawError: unknown = null;
|
|
622
|
+
|
|
350
623
|
// 1. The camera pane's post chain, OFFSCREEN and UNSCISSORED. See the
|
|
351
624
|
// header for why this cannot go straight to the canvas. Skipped inside a
|
|
352
625
|
// window: the compositor rendered this scene into its own operand target
|
|
353
626
|
// already, with the same chain.
|
|
627
|
+
let graded = post !== null && fx !== null;
|
|
354
628
|
if (blend === null && camera !== null && post !== null && fx !== null) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
629
|
+
try {
|
|
630
|
+
withPresentationScale(gl, scale, () => drawCameraPost(gl, post, camera, fx, at, bins));
|
|
631
|
+
} catch (error) {
|
|
632
|
+
if (pieces === null) throw error;
|
|
633
|
+
drawError ??= error;
|
|
634
|
+
gl.setRenderTarget(null);
|
|
635
|
+
graded = false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// 1a. A pane with no grade to blit, while the scale is below 1: the plain
|
|
640
|
+
// draw goes offscreen too, into a pane-sized target at the scale, and
|
|
641
|
+
// step 3 blits it up, because a canvas pass cannot draw below the
|
|
642
|
+
// canvas's density. At 1 it stays step 3's direct draw.
|
|
643
|
+
let plain: PlainPane | null = null;
|
|
644
|
+
if (scale < 1 && blend === null && camera !== null && !graded && plainPane !== null) {
|
|
645
|
+
withPresentationScale(gl, scale, () => drawPlainPane(gl, plainPane, camera, scene, shotCamera, bins));
|
|
646
|
+
plain = plainPane;
|
|
647
|
+
} else if (scale === 1 && plainPane?.held === true) {
|
|
648
|
+
plainPane.held = false;
|
|
649
|
+
plainPane.target.dispose();
|
|
371
650
|
}
|
|
372
651
|
|
|
373
652
|
// 1b. The WORKING pane's grade, offscreen and unscissored — rendered
|
|
@@ -379,28 +658,29 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
379
658
|
// this camera's mask is per-draw state we restore. The clear colour is
|
|
380
659
|
// still the shot's own (`_prevClear`): a graded working pane previews
|
|
381
660
|
// the export's image, so it sits on the export's background.
|
|
661
|
+
let workingGraded = wpost !== null;
|
|
382
662
|
if (working !== null && workingCamera !== null && wpost !== null && fx !== null) {
|
|
383
|
-
const dpr = gl.getPixelRatio();
|
|
384
|
-
const w = Math.max(1, Math.round(working.width * dpr));
|
|
385
|
-
const h = Math.max(1, Math.round(working.height * dpr));
|
|
386
|
-
// Mirrors binSceneSource's target discipline: the renderer's own sample
|
|
387
|
-
// count and output buffer type, so the image is scene-referred with MSAA
|
|
388
|
-
// on the model silhouettes.
|
|
389
|
-
wpost.src.samples = gl.samples;
|
|
390
|
-
wpost.src.texture.type = gl.getOutputBufferType();
|
|
391
|
-
wpost.src.setSize(w, h);
|
|
392
|
-
wpost.target.setSize(w, h);
|
|
393
|
-
wpost.chain.write(fx, at);
|
|
394
|
-
gl.setScissorTest(false);
|
|
395
663
|
const prevMask = workingCamera.layers.mask;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
664
|
+
try {
|
|
665
|
+
drawWorkingPost(gl, wpost, working, workingCamera, fx, at, scene, bins);
|
|
666
|
+
} catch (error) {
|
|
667
|
+
if (pieces === null) throw error;
|
|
668
|
+
drawError ??= error;
|
|
669
|
+
gl.setRenderTarget(null);
|
|
670
|
+
workingGraded = false;
|
|
671
|
+
}
|
|
400
672
|
workingCamera.layers.mask = prevMask;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// What this frame's grade did, reported once for both panes.
|
|
676
|
+
if (pieces !== null) {
|
|
677
|
+
if (drawError !== null) {
|
|
678
|
+
drawFailed.current = true;
|
|
679
|
+
pieces.set(drawOwner, [postPiece(now?.sceneId ?? sceneId, drawError)]);
|
|
680
|
+
} else if (drawFailed.current) {
|
|
681
|
+
drawFailed.current = false;
|
|
682
|
+
pieces.set(drawOwner);
|
|
683
|
+
}
|
|
404
684
|
}
|
|
405
685
|
|
|
406
686
|
gl.setScissorTest(true);
|
|
@@ -430,9 +710,9 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
430
710
|
// plan, which is what `workingDrawPlan` returns for it.
|
|
431
711
|
// The camera pane is always the one that matches the export.
|
|
432
712
|
if (working !== null && workingCamera !== null) {
|
|
433
|
-
const plan = workingDrawPlan(shading, wpost !== null);
|
|
713
|
+
const plan = workingDrawPlan(shading, wpost !== null && workingGraded);
|
|
434
714
|
applyRect(gl, working);
|
|
435
|
-
if (plan.graded && wpost !== null) {
|
|
715
|
+
if (plan.graded && wpost !== null && workingGraded) {
|
|
436
716
|
// The graded image already carries the shot's own background, so the
|
|
437
717
|
// pane clears with it rather than the clay backdrop.
|
|
438
718
|
gl.setClearColor(_prevClear, prevAlpha);
|
|
@@ -449,9 +729,25 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
449
729
|
gl.autoClear = prevAutoClear;
|
|
450
730
|
workingCamera.layers.mask = prevMask;
|
|
451
731
|
} else if (plan.override !== null) {
|
|
452
|
-
gl.setClearColor(
|
|
732
|
+
gl.setClearColor(clearBackdrop, 1);
|
|
453
733
|
scene.overrideMaterial = plan.override === "wire" ? overrides.wire : overrides.clay;
|
|
454
|
-
|
|
734
|
+
// An override draws every object with ITS side (three 0.185.1 copies
|
|
735
|
+
// the source material's side onto an override only for shadow
|
|
736
|
+
// passes), so a `BackSide` sky dome becomes a front-faced clay ball
|
|
737
|
+
// around the whole world. It is hidden for this draw and put back.
|
|
738
|
+
const insideOut = hideInsideOut(scene);
|
|
739
|
+
try {
|
|
740
|
+
// ONE PASS FOR ONE COLOUR. Clay and wire are depth-off and the
|
|
741
|
+
// same colour everywhere, so drawing the bins in order paints
|
|
742
|
+
// nothing one pass of all of them does not (one colour blended over
|
|
743
|
+
// itself commutes), and each bin was a whole render: a projection
|
|
744
|
+
// walk, a canvas-size MSAA pass and an output quad. A matte or a
|
|
745
|
+
// blur still cuts what it covers, so a plan with one keeps its steps.
|
|
746
|
+
renderSteps(gl, scene, workingCamera, bins.some(isCompositeStep) ? bins : NO_STEPS);
|
|
747
|
+
} finally {
|
|
748
|
+
for (const o of insideOut) o.visible = true;
|
|
749
|
+
insideOut.length = 0;
|
|
750
|
+
}
|
|
455
751
|
scene.overrideMaterial = null;
|
|
456
752
|
} else {
|
|
457
753
|
// RENDERED WITHOUT A CHAIN keeps the SHOT's backdrop, not the clay
|
|
@@ -463,22 +759,23 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
463
759
|
// Material mode keeps the clay backdrop on purpose: it previews the
|
|
464
760
|
// MATERIALS against the workspace, not the shot.
|
|
465
761
|
if (shading === "rendered") gl.setClearColor(_prevClear, prevAlpha);
|
|
466
|
-
else gl.setClearColor(
|
|
467
|
-
|
|
762
|
+
else gl.setClearColor(clearBackdrop, 1);
|
|
763
|
+
renderSteps(gl, scene, workingCamera, bins);
|
|
468
764
|
}
|
|
469
765
|
}
|
|
470
766
|
|
|
471
767
|
// 3. The camera pane: the blit, or a plain render when the scene has no
|
|
472
768
|
// post at all (no pass, no target, no cost — the same call ShotRuntime
|
|
473
|
-
// would have made).
|
|
769
|
+
// would have made), or below 1 the plain draw's blit (step 1a).
|
|
474
770
|
if (camera !== null) {
|
|
475
771
|
applyRect(gl, camera);
|
|
476
772
|
gl.setClearColor(_prevClear, prevAlpha);
|
|
477
773
|
// `QuadMesh.render` is `renderer.render(quad, itsOwnCamera)` — the same
|
|
478
774
|
// call, through the object that owns the right camera for it.
|
|
479
775
|
if (blend !== null) blend.quad.render(gl);
|
|
480
|
-
else if (post !== null && fx !== null) post.blit.render(gl);
|
|
481
|
-
else
|
|
776
|
+
else if (post !== null && fx !== null && graded) post.blit.render(gl);
|
|
777
|
+
else if (plain !== null) plain.blit.render(gl);
|
|
778
|
+
else renderSteps(gl, scene, shotCamera, bins);
|
|
482
779
|
}
|
|
483
780
|
|
|
484
781
|
// 4. Hand the renderer back exactly as it was found. Every one of these is
|
|
@@ -489,11 +786,300 @@ export function SplitRenderer({ feed, panes, effects, bins, hasMesh, frame, shad
|
|
|
489
786
|
gl.setViewport(0, 0, size.width, size.height);
|
|
490
787
|
gl.setScissor(0, 0, size.width, size.height);
|
|
491
788
|
gl.setClearColor(_prevClear, prevAlpha);
|
|
492
|
-
},
|
|
789
|
+
}, { phase: "render" });
|
|
493
790
|
|
|
494
791
|
return null;
|
|
495
792
|
}
|
|
496
793
|
|
|
794
|
+
interface CameraPost {
|
|
795
|
+
source: SceneSource;
|
|
796
|
+
chain: ReturnType<typeof buildPostChain>;
|
|
797
|
+
pipeline: RenderPipeline;
|
|
798
|
+
target: RenderTarget;
|
|
799
|
+
material: MeshBasicNodeMaterial;
|
|
800
|
+
blit: QuadMesh;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* The CAMERA pane's post rig: its scene source, the chain, the pipeline that
|
|
805
|
+
* renders it offscreen and the blit that brings it into the pane.
|
|
806
|
+
*
|
|
807
|
+
* Layer 0 only. This source is what feeds the graded image, so pinning it is
|
|
808
|
+
* what makes "an editor-only object can never appear in the camera pane" true
|
|
809
|
+
* by construction rather than by everyone remembering to set a mask. Both
|
|
810
|
+
* sources honour it identically. A build that throws part-way disposes the
|
|
811
|
+
* source it made (the caller isolates the throw, see `postBuild`).
|
|
812
|
+
*/
|
|
813
|
+
function buildCameraPost(
|
|
814
|
+
gl: WebGPURenderer,
|
|
815
|
+
scene: Scene,
|
|
816
|
+
shotCamera: Camera,
|
|
817
|
+
needsBins: boolean,
|
|
818
|
+
effects: SceneEffects,
|
|
819
|
+
): CameraPost {
|
|
820
|
+
const source = needsBins
|
|
821
|
+
? binSceneSource(scene, shotCamera, new Layers())
|
|
822
|
+
: passSceneSource(scene, shotCamera, new Layers());
|
|
823
|
+
let chain: ReturnType<typeof buildPostChain>;
|
|
824
|
+
try {
|
|
825
|
+
chain = buildPostChain(source.textureNode, effects);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
source.dispose();
|
|
828
|
+
throw error;
|
|
829
|
+
}
|
|
830
|
+
const pipeline = new RenderPipeline(gl as unknown as ConstructorParameters<typeof RenderPipeline>[0]);
|
|
831
|
+
pipeline.outputNode = chain.outputNode;
|
|
832
|
+
pipeline.outputColorTransform = false; // see the colour note in the header
|
|
833
|
+
// Half float, working space: the chain's output is scene-referred light and
|
|
834
|
+
// an 8-bit sRGB hop here would band every gradient the bloom touches.
|
|
835
|
+
const target = new RenderTarget(1, 1, {
|
|
836
|
+
type: HalfFloatType,
|
|
837
|
+
// Read off ColorManagement rather than written as `LinearSRGBColorSpace`:
|
|
838
|
+
// the pipeline forces the WORKING space for the chain, so this target has
|
|
839
|
+
// to be whatever that is, not whatever it is today.
|
|
840
|
+
colorSpace: ColorManagement.workingColorSpace as ColorSpace,
|
|
841
|
+
});
|
|
842
|
+
return { source, chain, pipeline, target, ...blitOf(target) };
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/** A target brought into the current viewport: drawn with no depth and no
|
|
846
|
+
* tone mapping of its own, so the canvas's one output pass encodes it. */
|
|
847
|
+
function blitOf(target: RenderTarget): { material: MeshBasicNodeMaterial; blit: QuadMesh } {
|
|
848
|
+
const material = new MeshBasicNodeMaterial();
|
|
849
|
+
material.colorNode = texture(target.texture, uv());
|
|
850
|
+
material.depthTest = false;
|
|
851
|
+
material.depthWrite = false;
|
|
852
|
+
material.toneMapped = false;
|
|
853
|
+
// THREE'S OWN full-screen primitive, not a hand-rolled plane. Its geometry
|
|
854
|
+
// carries the v-FLIPPED uv three uses everywhere it blits a render target
|
|
855
|
+
// (`QuadGeometry`: the bottom-left vertex is uv 0,1), and its camera covers
|
|
856
|
+
// NDC exactly — so uv 0..1 lands on the current VIEWPORT, whatever pane that
|
|
857
|
+
// is, and the image comes out the same way up on both backends. A
|
|
858
|
+
// `PlaneGeometry` looks equivalent and is upside down.
|
|
859
|
+
return { material, blit: new QuadMesh(material) };
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
interface WorkingPost {
|
|
863
|
+
src: RenderTarget;
|
|
864
|
+
chain: ReturnType<typeof buildPostChain>;
|
|
865
|
+
pipeline: RenderPipeline;
|
|
866
|
+
target: RenderTarget;
|
|
867
|
+
material: MeshBasicNodeMaterial;
|
|
868
|
+
blit: QuadMesh;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/** The WORKING pane's post rig (rendered shading): its own source target,
|
|
872
|
+
* the chain over it, the pipeline and the blit. See `wpost`. */
|
|
873
|
+
function buildWorkingPost(gl: WebGPURenderer, effects: SceneEffects): WorkingPost {
|
|
874
|
+
// In the WORKING space, the canvas frame-buffer target's own label: WebGPU
|
|
875
|
+
// keys a pipeline on its target's colour space, so a material drawn here
|
|
876
|
+
// and on the canvas builds one pipeline instead of two. The format stays
|
|
877
|
+
// half float, and sampling a working-space texture into the working space
|
|
878
|
+
// converts nothing, so no byte of the pane moves.
|
|
879
|
+
const src = new RenderTarget(1, 1, {
|
|
880
|
+
type: HalfFloatType,
|
|
881
|
+
colorSpace: ColorManagement.workingColorSpace as ColorSpace,
|
|
882
|
+
});
|
|
883
|
+
let chain: ReturnType<typeof buildPostChain>;
|
|
884
|
+
try {
|
|
885
|
+
chain = buildPostChain(texture(src.texture, uv()), effects);
|
|
886
|
+
} catch (error) {
|
|
887
|
+
src.dispose();
|
|
888
|
+
throw error;
|
|
889
|
+
}
|
|
890
|
+
const pipeline = new RenderPipeline(gl as unknown as ConstructorParameters<typeof RenderPipeline>[0]);
|
|
891
|
+
pipeline.outputNode = chain.outputNode;
|
|
892
|
+
pipeline.outputColorTransform = false; // one encode, at the blit — header
|
|
893
|
+
const target = new RenderTarget(1, 1, {
|
|
894
|
+
type: HalfFloatType,
|
|
895
|
+
colorSpace: ColorManagement.workingColorSpace as ColorSpace,
|
|
896
|
+
});
|
|
897
|
+
return { src, chain, pipeline, target, ...blitOf(target) };
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/** The camera pane's plain draw below 1 (the draw's step 1a): its target and
|
|
901
|
+
* the blit that brings it into the pane. */
|
|
902
|
+
interface PlainPane {
|
|
903
|
+
target: RenderTarget;
|
|
904
|
+
material: MeshBasicNodeMaterial;
|
|
905
|
+
blit: QuadMesh;
|
|
906
|
+
/** The target holds memory: drawn since the scale was last at 1. */
|
|
907
|
+
held: boolean;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/** Of the canvas frame-buffer target's own kind (half float, the working
|
|
911
|
+
* space, depth, and the renderer's samples at the draw), so every pipeline
|
|
912
|
+
* the scene built for the canvas draws into it too. */
|
|
913
|
+
function buildPlainPane(): PlainPane {
|
|
914
|
+
const target = new RenderTarget(1, 1, {
|
|
915
|
+
type: HalfFloatType,
|
|
916
|
+
colorSpace: ColorManagement.workingColorSpace as ColorSpace,
|
|
917
|
+
});
|
|
918
|
+
return { target, ...blitOf(target), held: false };
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function releasePlainPane(pane: PlainPane): void {
|
|
922
|
+
pane.target.dispose();
|
|
923
|
+
pane.material.dispose();
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function releaseCameraPost(post: CameraPost): void {
|
|
927
|
+
post.chain.dispose();
|
|
928
|
+
post.pipeline.dispose();
|
|
929
|
+
post.source.dispose();
|
|
930
|
+
post.target.dispose();
|
|
931
|
+
post.material.dispose();
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function releaseWorkingPost(wpost: WorkingPost): void {
|
|
935
|
+
wpost.chain.dispose();
|
|
936
|
+
wpost.pipeline.dispose();
|
|
937
|
+
wpost.src.dispose();
|
|
938
|
+
wpost.target.dispose();
|
|
939
|
+
wpost.material.dispose();
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* The split rigs by shape, least recently used first: `PresenterRigCache`
|
|
944
|
+
* (ShotRuntime.tsx) for this file's two rigs, with the release passed in.
|
|
945
|
+
* The mounted rig is always the most recent acquire, so it is never the one
|
|
946
|
+
* evicted while the cache holds two or more.
|
|
947
|
+
*
|
|
948
|
+
* ponytail: the third copy of this LRU (with `TransitionRigCache` and
|
|
949
|
+
* `PresenterRigCache`), because the runtime's are typed to their own rigs
|
|
950
|
+
* and not exported. One generic cache in the shot package retires all three.
|
|
951
|
+
*/
|
|
952
|
+
class SplitRigCache<R> {
|
|
953
|
+
readonly #rigs = new Map<string, R>();
|
|
954
|
+
readonly #release: (rig: R) => void;
|
|
955
|
+
#capacity = 1;
|
|
956
|
+
|
|
957
|
+
constructor(release: (rig: R) => void) {
|
|
958
|
+
this.#release = release;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
resize(capacity: number): void {
|
|
962
|
+
this.#capacity = Math.max(1, capacity);
|
|
963
|
+
this.#evict();
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
acquire(key: string, make: () => R): R {
|
|
967
|
+
const hit = this.#rigs.get(key);
|
|
968
|
+
if (hit !== undefined) {
|
|
969
|
+
this.#rigs.delete(key);
|
|
970
|
+
this.#rigs.set(key, hit);
|
|
971
|
+
return hit;
|
|
972
|
+
}
|
|
973
|
+
const rig = make();
|
|
974
|
+
this.#rigs.set(key, rig);
|
|
975
|
+
this.#evict();
|
|
976
|
+
return rig;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
dispose(): void {
|
|
980
|
+
for (const rig of this.#rigs.values()) this.#release(rig);
|
|
981
|
+
this.#rigs.clear();
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
#evict(): void {
|
|
985
|
+
while (this.#rigs.size > this.#capacity) {
|
|
986
|
+
const oldest = this.#rigs.keys().next().value as string;
|
|
987
|
+
const victim = this.#rigs.get(oldest)!;
|
|
988
|
+
this.#rigs.delete(oldest);
|
|
989
|
+
this.#release(victim);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/** A Shot's post stack, as a failed piece. */
|
|
995
|
+
/** The camera pane's grade, offscreen (see the draw's step 1). */
|
|
996
|
+
function drawCameraPost(
|
|
997
|
+
gl: WebGPURenderer,
|
|
998
|
+
post: CameraPost,
|
|
999
|
+
camera: PaneRect,
|
|
1000
|
+
fx: SceneEffects,
|
|
1001
|
+
at: number,
|
|
1002
|
+
bins: readonly RenderStep[],
|
|
1003
|
+
): void {
|
|
1004
|
+
// The target is PANE sized, in device px: the pane gets its own
|
|
1005
|
+
// resolution for free, and `setSize` already no-ops when it matches. Sized
|
|
1006
|
+
// as three sizes the pane's viewport (`devicePixels`), so the blit draws it
|
|
1007
|
+
// back one texel to a pixel; rounded, it was a pixel wider than the pane at
|
|
1008
|
+
// a fractional density.
|
|
1009
|
+
const dpr = gl.getPixelRatio();
|
|
1010
|
+
post.target.setSize(devicePixels(camera.width, dpr), devicePixels(camera.height, dpr));
|
|
1011
|
+
// The pixel-sized knobs at the pane's playback scale (`drawScaleOf`).
|
|
1012
|
+
post.chain.write(fx, at, drawScaleOf(gl));
|
|
1013
|
+
gl.setScissorTest(false);
|
|
1014
|
+
// The bins first, into whatever the source owns. A `pass()`-backed
|
|
1015
|
+
// source no-ops here and renders itself from inside
|
|
1016
|
+
// `pipeline.render()` below, which is the untouched single-render path.
|
|
1017
|
+
post.source.draw(gl, bins);
|
|
1018
|
+
gl.setRenderTarget(post.target);
|
|
1019
|
+
post.pipeline.render();
|
|
1020
|
+
gl.setRenderTarget(null);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/** The camera pane's plain draw, offscreen and pane sized like the grade's
|
|
1024
|
+
* (see the draw's step 1a). The clear colour is still the Shot's. */
|
|
1025
|
+
function drawPlainPane(
|
|
1026
|
+
gl: WebGPURenderer,
|
|
1027
|
+
pane: PlainPane,
|
|
1028
|
+
camera: PaneRect,
|
|
1029
|
+
scene: Scene,
|
|
1030
|
+
shotCamera: Camera,
|
|
1031
|
+
bins: readonly RenderStep[],
|
|
1032
|
+
): void {
|
|
1033
|
+
const dpr = gl.getPixelRatio();
|
|
1034
|
+
pane.target.samples = gl.samples;
|
|
1035
|
+
pane.target.texture.type = gl.getOutputBufferType();
|
|
1036
|
+
pane.target.setSize(devicePixels(camera.width, dpr), devicePixels(camera.height, dpr));
|
|
1037
|
+
pane.held = true;
|
|
1038
|
+
gl.setScissorTest(false);
|
|
1039
|
+
gl.setRenderTarget(pane.target);
|
|
1040
|
+
renderSteps(gl, scene, shotCamera, bins);
|
|
1041
|
+
gl.setRenderTarget(null);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/** The working pane's grade, offscreen (see the draw's step 1b). The caller
|
|
1045
|
+
* restores the camera's mask, thrown or not. */
|
|
1046
|
+
function drawWorkingPost(
|
|
1047
|
+
gl: WebGPURenderer,
|
|
1048
|
+
wpost: WorkingPost,
|
|
1049
|
+
working: PaneRect,
|
|
1050
|
+
workingCamera: Camera,
|
|
1051
|
+
fx: SceneEffects,
|
|
1052
|
+
at: number,
|
|
1053
|
+
scene: Scene,
|
|
1054
|
+
bins: readonly RenderStep[],
|
|
1055
|
+
): void {
|
|
1056
|
+
const prevMask = workingCamera.layers.mask;
|
|
1057
|
+
const dpr = gl.getPixelRatio();
|
|
1058
|
+
const w = devicePixels(working.width, dpr);
|
|
1059
|
+
const h = devicePixels(working.height, dpr);
|
|
1060
|
+
// Mirrors binSceneSource's target discipline: the renderer's own sample
|
|
1061
|
+
// count and output buffer type, so the image is scene-referred with
|
|
1062
|
+
// MSAA on the model silhouettes.
|
|
1063
|
+
wpost.src.samples = gl.samples;
|
|
1064
|
+
wpost.src.texture.type = gl.getOutputBufferType();
|
|
1065
|
+
wpost.src.setSize(w, h);
|
|
1066
|
+
wpost.target.setSize(w, h);
|
|
1067
|
+
wpost.chain.write(fx, at);
|
|
1068
|
+
gl.setScissorTest(false);
|
|
1069
|
+
workingCamera.layers.mask = 1; // bit 0 only
|
|
1070
|
+
gl.setRenderTarget(wpost.src);
|
|
1071
|
+
renderSteps(gl, scene, workingCamera, bins);
|
|
1072
|
+
gl.setRenderTarget(null);
|
|
1073
|
+
workingCamera.layers.mask = prevMask;
|
|
1074
|
+
gl.setRenderTarget(wpost.target);
|
|
1075
|
+
wpost.pipeline.render();
|
|
1076
|
+
gl.setRenderTarget(null);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function postPiece(sceneId: string | null, error: unknown): PieceFailureInput {
|
|
1080
|
+
return { key: "post", kind: "post", sceneId: sceneId ?? "", fieldPath: "effects", error };
|
|
1081
|
+
}
|
|
1082
|
+
|
|
497
1083
|
/**
|
|
498
1084
|
* Point the renderer at one pane.
|
|
499
1085
|
*
|
|
@@ -509,3 +1095,64 @@ function applyRect(gl: WebGPURenderer, rect: PaneRect): void {
|
|
|
509
1095
|
gl.setViewport(rect.x, rect.y, rect.width, rect.height);
|
|
510
1096
|
gl.setScissor(rect.x, rect.y, rect.width, rect.height);
|
|
511
1097
|
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Make compileAsync see the working draw's exact scene state, then hand back a
|
|
1101
|
+
* synchronous restore. `compileObjectForTarget` invokes both around its
|
|
1102
|
+
* synchronous scene walk, so no camera, scene, object, or renderer state is
|
|
1103
|
+
* held while shader compilation awaits the backend.
|
|
1104
|
+
*
|
|
1105
|
+
* Frustum culling is disabled for the walk on purpose. An orbit can reveal an
|
|
1106
|
+
* already-mounted object without changing its material signature, and that
|
|
1107
|
+
* later first draw must not be where its pipeline is created.
|
|
1108
|
+
*/
|
|
1109
|
+
/** Scratch for `hideInsideOut`: the meshes hidden for one override draw. */
|
|
1110
|
+
const _insideOut: Object3D[] = [];
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Hide every showing mesh whose material(s) are ALL `BackSide` and return
|
|
1114
|
+
* them, so the caller can put them back after the draw. Such a mesh is a
|
|
1115
|
+
* shell meant to be seen from inside (a sky dome, an environment sphere);
|
|
1116
|
+
* under a single-sided override it is a wall around everything else. The same
|
|
1117
|
+
* predicate the camera body's bounds walk skips by (sceneExtent).
|
|
1118
|
+
*
|
|
1119
|
+
* WALKED AT EVERY DRAW, over what shows (a shell under a hidden parent draws
|
|
1120
|
+
* nothing anyway). A list kept between draws went stale on changes no key it
|
|
1121
|
+
* had could see: a cut or a seek into a resident Shot only re-parents that
|
|
1122
|
+
* Shot's group, and a code component's module, its own Suspense or a child it
|
|
1123
|
+
* renders on a prop commits a subtree with no layer mounting. A shell the
|
|
1124
|
+
* list missed drew as a clay ball over the whole pane.
|
|
1125
|
+
*/
|
|
1126
|
+
export function hideInsideOut(scene: Scene): Object3D[] {
|
|
1127
|
+
_insideOut.length = 0;
|
|
1128
|
+
scene.traverseVisible((o) => {
|
|
1129
|
+
if (!isInsideOutShell(o)) return;
|
|
1130
|
+
o.visible = false;
|
|
1131
|
+
_insideOut.push(o);
|
|
1132
|
+
});
|
|
1133
|
+
return _insideOut;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
export function configureWorkingSceneCompile(
|
|
1137
|
+
scene: Scene,
|
|
1138
|
+
camera: Camera,
|
|
1139
|
+
mask: number,
|
|
1140
|
+
override: Material | null,
|
|
1141
|
+
): () => void {
|
|
1142
|
+
const previousMask = camera.layers.mask;
|
|
1143
|
+
const previousOverride = scene.overrideMaterial;
|
|
1144
|
+
const culled: Array<{ object: Object3D & { frustumCulled: boolean }; value: boolean }> = [];
|
|
1145
|
+
camera.layers.mask = mask;
|
|
1146
|
+
scene.overrideMaterial = override;
|
|
1147
|
+
scene.traverse((object) => {
|
|
1148
|
+
if (!("frustumCulled" in object) || typeof object.frustumCulled !== "boolean") return;
|
|
1149
|
+
const renderable = object as Object3D & { frustumCulled: boolean };
|
|
1150
|
+
culled.push({ object: renderable, value: renderable.frustumCulled });
|
|
1151
|
+
renderable.frustumCulled = false;
|
|
1152
|
+
});
|
|
1153
|
+
return () => {
|
|
1154
|
+
for (const entry of culled) entry.object.frustumCulled = entry.value;
|
|
1155
|
+
scene.overrideMaterial = previousOverride;
|
|
1156
|
+
camera.layers.mask = previousMask;
|
|
1157
|
+
};
|
|
1158
|
+
}
|