@dreamerbird/camera-studio 0.1.0 → 0.3.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/LICENSE +866 -0
- package/assets/models/Y Bot.glb +0 -0
- package/dist/CameraScene.d.ts +1 -1
- package/dist/CameraScene.d.ts.map +1 -1
- package/dist/CameraScene.js +100 -9
- package/dist/PlacementManager.d.ts +15 -1
- package/dist/PlacementManager.d.ts.map +1 -1
- package/dist/PlacementManager.js +151 -15
- package/dist/agent/api.d.ts +5 -0
- package/dist/agent/api.d.ts.map +1 -1
- package/dist/agent/api.js +25 -0
- package/dist/agent/api.test.js +75 -1
- package/dist/agent/commands/assets.d.ts +5 -0
- package/dist/agent/commands/assets.d.ts.map +1 -1
- package/dist/agent/commands/assets.js +93 -21
- package/dist/agent/commands/assets.test.js +53 -0
- package/dist/agent/commands/materials.d.ts +18 -0
- package/dist/agent/commands/materials.d.ts.map +1 -0
- package/dist/agent/commands/materials.js +192 -0
- package/dist/agent/commands/materials.test.d.ts +2 -0
- package/dist/agent/commands/materials.test.d.ts.map +1 -0
- package/dist/agent/commands/materials.test.js +294 -0
- package/dist/agent/commands/modelRecovery.test.d.ts +2 -0
- package/dist/agent/commands/modelRecovery.test.d.ts.map +1 -0
- package/dist/agent/commands/modelRecovery.test.js +122 -0
- package/dist/agent/commands/models.d.ts +13 -0
- package/dist/agent/commands/models.d.ts.map +1 -1
- package/dist/agent/commands/models.js +98 -1
- package/dist/agent/commands/primitives.d.ts +27 -0
- package/dist/agent/commands/primitives.d.ts.map +1 -0
- package/dist/agent/commands/primitives.js +117 -0
- package/dist/agent/commands/primitives.test.d.ts +2 -0
- package/dist/agent/commands/primitives.test.d.ts.map +1 -0
- package/dist/agent/commands/primitives.test.js +135 -0
- package/dist/agent/expectedCommands.d.ts +1 -1
- package/dist/agent/expectedCommands.d.ts.map +1 -1
- package/dist/agent/expectedCommands.js +6 -0
- package/dist/agent/propAccess.d.ts +2 -0
- package/dist/agent/propAccess.d.ts.map +1 -1
- package/dist/agent/propAccess.js +21 -0
- package/dist/agent/registerCommands.d.ts.map +1 -1
- package/dist/agent/registerCommands.js +5 -1
- package/dist/agent/runtime/objectRuntime.d.ts +2 -0
- package/dist/agent/runtime/objectRuntime.d.ts.map +1 -1
- package/dist/agent/runtime/objectRuntime.js +15 -1
- package/dist/agent/runtime.d.ts +1 -0
- package/dist/agent/runtime.d.ts.map +1 -1
- package/dist/agent/runtime.js +2 -0
- package/dist/assetLibrary/dispose.d.ts +4 -2
- package/dist/assetLibrary/dispose.d.ts.map +1 -1
- package/dist/assetLibrary/dispose.js +7 -5
- package/dist/assetLibrary/exportGLB.d.ts +25 -0
- package/dist/assetLibrary/exportGLB.d.ts.map +1 -1
- package/dist/assetLibrary/exportGLB.js +43 -1
- package/dist/assetLibrary/exportGLB.test.js +56 -2
- package/dist/assetLibrary/gltfLoader.d.ts +2 -1
- package/dist/assetLibrary/gltfLoader.d.ts.map +1 -1
- package/dist/assetLibrary/gltfLoader.js +2 -2
- package/dist/assetLibrary/gltfResourceSet.d.ts +6 -0
- package/dist/assetLibrary/gltfResourceSet.d.ts.map +1 -0
- package/dist/assetLibrary/gltfResourceSet.js +27 -0
- package/dist/assetLibrary/gltfResourceSet.test.d.ts +2 -0
- package/dist/assetLibrary/gltfResourceSet.test.d.ts.map +1 -0
- package/dist/assetLibrary/gltfResourceSet.test.js +40 -0
- package/dist/assetLibrary/importModelFile.d.ts +5 -0
- package/dist/assetLibrary/importModelFile.d.ts.map +1 -1
- package/dist/assetLibrary/importModelFile.js +3 -16
- package/dist/assetLibrary/importModelFiles.d.ts +15 -0
- package/dist/assetLibrary/importModelFiles.d.ts.map +1 -0
- package/dist/assetLibrary/importModelFiles.js +58 -0
- package/dist/assetLibrary/importModelFiles.test.d.ts +2 -0
- package/dist/assetLibrary/importModelFiles.test.d.ts.map +1 -0
- package/dist/assetLibrary/importModelFiles.test.js +217 -0
- package/dist/assetLibrary/importPipeline.d.ts +15 -1
- package/dist/assetLibrary/importPipeline.d.ts.map +1 -1
- package/dist/assetLibrary/importPipeline.js +74 -38
- package/dist/assetLibrary/importPipeline.test.js +25 -0
- package/dist/assetLibrary/library.d.ts +1 -0
- package/dist/assetLibrary/library.d.ts.map +1 -1
- package/dist/assetLibrary/library.js +22 -1
- package/dist/assetLibrary/library.test.js +76 -0
- package/dist/assetLibrary/seedAssetsManifest.js +1 -1
- package/dist/bootstrap.d.ts +2 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +69 -7
- package/dist/bootstrap.test.js +111 -0
- package/dist/config.d.ts +10 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/firstScreenGate.d.ts +28 -0
- package/dist/firstScreenGate.d.ts.map +1 -0
- package/dist/firstScreenGate.js +67 -0
- package/dist/firstScreenGate.test.d.ts +2 -0
- package/dist/firstScreenGate.test.d.ts.map +1 -0
- package/dist/firstScreenGate.test.js +83 -0
- package/dist/hostFlows.d.ts +1 -0
- package/dist/hostFlows.d.ts.map +1 -1
- package/dist/hostFlows.js +9 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/materials/modelMaterials.d.ts +19 -0
- package/dist/materials/modelMaterials.d.ts.map +1 -0
- package/dist/materials/modelMaterials.js +253 -0
- package/dist/materials/modelMaterials.test.d.ts +2 -0
- package/dist/materials/modelMaterials.test.d.ts.map +1 -0
- package/dist/materials/modelMaterials.test.js +188 -0
- package/dist/materials/types.d.ts +20 -0
- package/dist/materials/types.d.ts.map +1 -0
- package/dist/materials/types.js +1 -0
- package/dist/materials/useModelMaterials.d.ts +10 -0
- package/dist/materials/useModelMaterials.d.ts.map +1 -0
- package/dist/materials/useModelMaterials.js +26 -0
- package/dist/materials/useModelMaterials.test.d.ts +2 -0
- package/dist/materials/useModelMaterials.test.d.ts.map +1 -0
- package/dist/materials/useModelMaterials.test.js +75 -0
- package/dist/poses.d.ts +38 -0
- package/dist/poses.d.ts.map +1 -0
- package/dist/poses.js +41 -0
- package/dist/poses.test.d.ts +2 -0
- package/dist/poses.test.d.ts.map +1 -0
- package/dist/poses.test.js +38 -0
- package/dist/primitives.d.ts +5 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +15 -0
- package/dist/primitives.test.d.ts +2 -0
- package/dist/primitives.test.d.ts.map +1 -0
- package/dist/primitives.test.js +27 -0
- package/dist/reset.realStudio.test.d.ts +2 -0
- package/dist/reset.realStudio.test.d.ts.map +1 -0
- package/dist/reset.realStudio.test.js +60 -0
- package/dist/studioRoot.d.ts +4 -0
- package/dist/studioRoot.d.ts.map +1 -0
- package/dist/studioRoot.js +36 -0
- package/dist/studioRoot.test.d.ts +2 -0
- package/dist/studioRoot.test.d.ts.map +1 -0
- package/dist/studioRoot.test.js +62 -0
- package/dist/useCameraStudio.d.ts.map +1 -1
- package/dist/useCameraStudio.js +3 -2
- package/package.json +18 -10
package/assets/models/Y Bot.glb
CHANGED
|
Binary file
|
package/dist/CameraScene.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare function captureAnchorSeed(mainCam: ISheetObject<any> | undefined
|
|
|
25
25
|
* UI 装配可配见 ./uiConfig:停靠框 portal 容器 / z-index 基准 / 语言
|
|
26
26
|
* (宿主已包 I18nProvider 时语言以宿主为准,ui.locale 仅自包时生效)。
|
|
27
27
|
*/
|
|
28
|
-
export declare function CameraScene({ project, onFlows, onError }: CameraSceneProps): React.JSX.Element;
|
|
28
|
+
export declare function CameraScene({ project, onFlows, onError, includeDemoModel, }: CameraSceneProps): React.JSX.Element;
|
|
29
29
|
/**
|
|
30
30
|
* i18n 嵌套判定(spec §7):宿主树已包 studio 的 I18nProvider →
|
|
31
31
|
* 原样渲染,语言以宿主为准(ui.locale 不生效);否则自包一层,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraScene.d.ts","sourceRoot":"","sources":["../src/CameraScene.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAiB,YAAY,EAAC,MAAM,mBAAmB,CAAA;AACnE,OAAO,KASN,MAAM,OAAO,CAAA;AAsDd,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAA;AAiBjD,OAAO,KAAK,EAAc,UAAU,EAAC,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"CameraScene.d.ts","sourceRoot":"","sources":["../src/CameraScene.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAiB,YAAY,EAAC,MAAM,mBAAmB,CAAA;AACnE,OAAO,KASN,MAAM,OAAO,CAAA;AAsDd,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAA;AAiBjD,OAAO,KAAK,EAAc,UAAU,EAAC,MAAM,kBAAkB,CAAA;AA4D7D,OAAO,KAAK,EAAC,WAAW,EAAe,MAAM,aAAa,CAAA;AAG1D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,UAAU,CAAA;AAwmB9C;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,SAAS,GACrC,UAAU,GAAG,SAAS,CAMxB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,SAAS,GACrC,UAAU,CAcZ;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,OAAO,EACP,OAAO,EACP,gBAAuB,GACxB,EAAE,gBAAgB,qBAolDlB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAAC,QAAQ,EAAC,EAAE;IAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAC,qBAIpE;AAuFD,8BAA8B;AAC9B,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,WAAW,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,IAAI,CAAA;CACnC,QAOA"}
|
package/dist/CameraScene.js
CHANGED
|
@@ -46,16 +46,29 @@ import { createSceneExportController, registerSceneExportController, } from './a
|
|
|
46
46
|
import { rigIdOf, collectBoneNames } from './assetLibrary/rig';
|
|
47
47
|
import { cameraOutlineMenuExtensionId, cameraOutlineObjectMenu, setCameraOutlineMenuCtx, } from './outlineObjectMenu';
|
|
48
48
|
import { pruneObjectFromOutlineCollections } from '@dreamerbird/studio/panels/OutlinePanel/outlineOps/stateOps';
|
|
49
|
-
import { createHostFlows, setDropPointContext, setHostFlowsHandle, setPlacingSnapshot, } from './hostFlows';
|
|
49
|
+
import { createHostFlows, dropPointFromClient, isDropPointInsideCanvas, setDropPointContext, setHostFlowsHandle, setPlacingSnapshot, } from './hostFlows';
|
|
50
50
|
import { importModelFile } from './assetLibrary/importModelFile';
|
|
51
51
|
import { resolveControlSpec } from './poseMode/controlSpec';
|
|
52
52
|
import { getUiConfig } from './uiConfig';
|
|
53
|
+
import { createFirstScreenGate, getFirstScreenGate, installFirstScreenGate, resetFirstScreenGate, } from './firstScreenGate';
|
|
53
54
|
import { enterPoseSession, getPoseSession, isModelDoubleClick, poseHitTest, requestPoseExit, usePoseSessionKey, } from './poseMode/poseSession';
|
|
54
55
|
import { getPoseKeyframeCount, setPoseValue, writePoseKeyframe, clearPoseValue, } from './poseMode/poseTrack';
|
|
55
56
|
import { savePoseFromInstance } from './poseMode/savePose';
|
|
56
57
|
import { parsePoseValue, serializeLibPose } from './poseMode/poseValue';
|
|
57
58
|
import { isPoseExitGuardedTarget } from './poseMode/poseExitGuard';
|
|
58
59
|
import { poseStatusText } from './poseStatusText';
|
|
60
|
+
/** 首屏闸门里场景内置模型(scene.glb)的登记 key */
|
|
61
|
+
const SCENE_MODEL_GATE_KEY = 'scene-glb';
|
|
62
|
+
/**
|
|
63
|
+
* 加载探针:Suspense 未解决时子树不挂载,mount effect 触发即
|
|
64
|
+
* 「这个资源落定了」,上报首屏闸门(firstScreenGate 机制)
|
|
65
|
+
*/
|
|
66
|
+
function InitialLoadProbe({ gateKey, children, }) {
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
getFirstScreenGate().settle(gateKey);
|
|
69
|
+
}, [gateKey]);
|
|
70
|
+
return React.createElement(React.Fragment, null, children);
|
|
71
|
+
}
|
|
59
72
|
function Model({ url, selectLocked }) {
|
|
60
73
|
const { nodes } = useGLTF(url);
|
|
61
74
|
const thingyObjRef = useRef(undefined);
|
|
@@ -100,10 +113,11 @@ function Model({ url, selectLocked }) {
|
|
|
100
113
|
/**
|
|
101
114
|
* 导演视角初始机位/瞄准点:PerspectiveCamera/Target 的 JSX position 与
|
|
102
115
|
* Agent view.reset(MainOrbitControls 登记的导演控制器)同源,改一处
|
|
103
|
-
*
|
|
116
|
+
* 两处一致。场景单位 = 米:机位 4.5 米外、1.7 米高,瞄准胸口(0.9 米),
|
|
117
|
+
* 一个 1.8 米的人占画面约四成高,是舒服的工作取景。
|
|
104
118
|
*/
|
|
105
|
-
const DIRECTOR_HOME_CAMERA_POSITION = [0,
|
|
106
|
-
const DIRECTOR_HOME_TARGET_POSITION = [0, 0, 0];
|
|
119
|
+
const DIRECTOR_HOME_CAMERA_POSITION = [0, 1.7, 4.5];
|
|
120
|
+
const DIRECTOR_HOME_TARGET_POSITION = [0, 0.9, 0];
|
|
107
121
|
/**
|
|
108
122
|
* 主画面的视角控制,操作方式与 Blender 默认键位一致:
|
|
109
123
|
* 中键拖动 = 旋转视角;Shift+中键 = 平移;Ctrl/Cmd+中键 = 平滑缩放;
|
|
@@ -496,6 +510,7 @@ const BACKGROUND = new Color().setRGB(0x45 / 255, 0x45 / 255, 0x45 / 255, Linear
|
|
|
496
510
|
function GroundGrid() {
|
|
497
511
|
const ref = useRef(null);
|
|
498
512
|
useEffect(() => registerEditorOnly(ref.current), []);
|
|
513
|
+
// 场景单位 = 米:小格 1 米、粗线 5 米
|
|
499
514
|
return (React.createElement(Grid, { ref: ref, position: [0, 0.01, 0], args: [100, 100], cellSize: 1, cellColor: "#9b9ba1", sectionSize: 5, sectionColor: "#aeaeb6", fadeDistance: 80, fadeStrength: 1, infiniteGrid: true, raycast: () => null }));
|
|
500
515
|
}
|
|
501
516
|
/**
|
|
@@ -539,7 +554,7 @@ export function captureAnchorSeed(mainCam) {
|
|
|
539
554
|
* UI 装配可配见 ./uiConfig:停靠框 portal 容器 / z-index 基准 / 语言
|
|
540
555
|
* (宿主已包 I18nProvider 时语言以宿主为准,ui.locale 仅自包时生效)。
|
|
541
556
|
*/
|
|
542
|
-
export function CameraScene({ project, onFlows, onError }) {
|
|
557
|
+
export function CameraScene({ project, onFlows, onError, includeDemoModel = true, }) {
|
|
543
558
|
const t = useT();
|
|
544
559
|
const cameraTargetRef = useRef(null);
|
|
545
560
|
const cameraObjRef = useRef(undefined);
|
|
@@ -780,7 +795,7 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
780
795
|
const s = getStudio();
|
|
781
796
|
return s?._store?._saaz ?? s?._saaz ?? 'no saaz';
|
|
782
797
|
};
|
|
783
|
-
const { placing, pendingEntry, startPlacingFromLibrary, placeLibraryEntryAt, cancelPlacing, confirmPlacing, clearPending, } = usePlacementController(project, sheet);
|
|
798
|
+
const { placing, pendingEntry, startPlacingFromLibrary, placeLibraryEntryAt, placePrimitiveAt, cancelPlacing, confirmPlacing, clearPending, } = usePlacementController(project, sheet);
|
|
784
799
|
const [gizmoMode, setGizmoMode] = useGizmoMode();
|
|
785
800
|
// 正在摆姿势的 placementKey(无会话为 null):Esc/双击空白/播放自动退出都靠它
|
|
786
801
|
const poseSessionKey = usePoseSessionKey();
|
|
@@ -807,6 +822,33 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
807
822
|
setModelDragging(false);
|
|
808
823
|
});
|
|
809
824
|
}, [startPlacingFromLibrary]);
|
|
825
|
+
useEffect(() => {
|
|
826
|
+
if (!modelDragging)
|
|
827
|
+
return;
|
|
828
|
+
const onDragOver = (ev) => {
|
|
829
|
+
if (isDropPointInsideCanvas(ev.clientX, ev.clientY))
|
|
830
|
+
ev.preventDefault();
|
|
831
|
+
};
|
|
832
|
+
const onDrop = (ev) => {
|
|
833
|
+
ev.preventDefault();
|
|
834
|
+
if (isDropPointInsideCanvas(ev.clientX, ev.clientY)) {
|
|
835
|
+
const point = dropPointFromClient(ev.clientX, ev.clientY);
|
|
836
|
+
if (point) {
|
|
837
|
+
confirmPlacing(point);
|
|
838
|
+
setModelDragging(false);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
cancelPlacing();
|
|
843
|
+
setModelDragging(false);
|
|
844
|
+
};
|
|
845
|
+
window.addEventListener('dragover', onDragOver);
|
|
846
|
+
window.addEventListener('drop', onDrop);
|
|
847
|
+
return () => {
|
|
848
|
+
window.removeEventListener('dragover', onDragOver);
|
|
849
|
+
window.removeEventListener('drop', onDrop);
|
|
850
|
+
};
|
|
851
|
+
}, [modelDragging, confirmPlacing, cancelPlacing]);
|
|
810
852
|
// 右键已放置模型弹出的操作菜单:记录目标条目和光标位置
|
|
811
853
|
const [objectMenu, setObjectMenu] = useState(null);
|
|
812
854
|
// 菜单关闭回调:稳定的引用,避免 CanvasContextMenu 不必要的重渲染
|
|
@@ -1348,6 +1390,7 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1348
1390
|
gizmoMode,
|
|
1349
1391
|
selectedPathPoint,
|
|
1350
1392
|
placeLibraryEntryAt,
|
|
1393
|
+
placePrimitiveAt,
|
|
1351
1394
|
enterPathEdit,
|
|
1352
1395
|
exitPathEditOnBlur,
|
|
1353
1396
|
afterSceneObjectDeleted,
|
|
@@ -1358,6 +1401,7 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1358
1401
|
// 模型:转发 placeLibraryEntryAt(Task 8 已返回 {key, persisted},
|
|
1359
1402
|
// 与 UI 点击放置同一条落地管线)
|
|
1360
1403
|
createModel: ({ entry, position }) => deps().placeLibraryEntryAt(entry, position),
|
|
1404
|
+
createPrimitive: ({ kind, position }) => deps().placePrimitiveAt(kind, position),
|
|
1361
1405
|
// 相机:Task 15 决策——生产绑定不自动 setActiveCameraKey。
|
|
1362
1406
|
// create/setActive 是 spec §9 的两条显式命令,Agent 调用
|
|
1363
1407
|
// cameras.create 不得隐性改变会话态;自动激活是 UI 顶栏
|
|
@@ -1419,6 +1463,31 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1419
1463
|
const handleTogglePath = useMemo(() => () => toggleFrame('path'), [toggleFrame]);
|
|
1420
1464
|
// 稳定引用:行为不变(setPipVisible 取反),与 handleToggleModel/Path 同模式
|
|
1421
1465
|
const handleTogglePip = useCallback(() => setPipVisible((v) => !v), []);
|
|
1466
|
+
// 首屏闸门(firstScreenGate 机制):创建即安装——子树 effect 先于父 effect
|
|
1467
|
+
// 运行,useState 初始化器(渲染期)是子树 effect 能拿到闸门的最早时机;
|
|
1468
|
+
// 卸载时若单值仍是本闸门则复位(重挂载由新实例重装)
|
|
1469
|
+
const [firstScreenReady, setFirstScreenReady] = useState(false);
|
|
1470
|
+
const [coverGone, setCoverGone] = useState(false);
|
|
1471
|
+
const [firstScreenGate] = useState(() => {
|
|
1472
|
+
const gate = createFirstScreenGate(() => setFirstScreenReady(true));
|
|
1473
|
+
if (includeDemoModel)
|
|
1474
|
+
gate.expect(SCENE_MODEL_GATE_KEY);
|
|
1475
|
+
installFirstScreenGate(gate);
|
|
1476
|
+
return gate;
|
|
1477
|
+
});
|
|
1478
|
+
useEffect(() => {
|
|
1479
|
+
return () => {
|
|
1480
|
+
if (getFirstScreenGate() === firstScreenGate)
|
|
1481
|
+
resetFirstScreenGate();
|
|
1482
|
+
};
|
|
1483
|
+
}, [firstScreenGate]);
|
|
1484
|
+
// 遮罩淡出(0.3s)+ 余量后卸载,不依赖 transitionend 单一路径
|
|
1485
|
+
useEffect(() => {
|
|
1486
|
+
if (!firstScreenReady)
|
|
1487
|
+
return;
|
|
1488
|
+
const t = setTimeout(() => setCoverGone(true), 600);
|
|
1489
|
+
return () => clearTimeout(t);
|
|
1490
|
+
}, [firstScreenReady]);
|
|
1422
1491
|
return (React.createElement(SceneI18nRoot, null,
|
|
1423
1492
|
React.createElement("div", { style: {
|
|
1424
1493
|
height: '100vh',
|
|
@@ -1594,8 +1663,9 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1594
1663
|
React.createElement(e.mesh, { ref: cameraTargetRef, theatreKey: "Camera / Target", objRef: targetObjRef, hideFromOutline: true, position: DIRECTOR_HOME_TARGET_POSITION, visible: "editor" },
|
|
1595
1664
|
React.createElement("boxGeometry", { attach: "geometry" }),
|
|
1596
1665
|
React.createElement("meshPhongMaterial", { attach: "material", color: "red" })),
|
|
1597
|
-
React.createElement(Suspense, { fallback: null },
|
|
1598
|
-
React.createElement(
|
|
1666
|
+
includeDemoModel && (React.createElement(Suspense, { fallback: null },
|
|
1667
|
+
React.createElement(InitialLoadProbe, { gateKey: SCENE_MODEL_GATE_KEY },
|
|
1668
|
+
React.createElement(Model, { url: sceneGLB, selectLocked: !!placing })))),
|
|
1599
1669
|
placing && (React.createElement(Suspense, { fallback: null },
|
|
1600
1670
|
React.createElement(PlacementRigErrorBoundary, { onCancel: () => {
|
|
1601
1671
|
cancelPlacing();
|
|
@@ -1614,7 +1684,28 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1614
1684
|
placePresetPath(presetDrag, p);
|
|
1615
1685
|
setPresetDrag(null);
|
|
1616
1686
|
}, onCancel: () => setPresetDrag(null) })),
|
|
1617
|
-
React.createElement(GroundGrid, null)))
|
|
1687
|
+
React.createElement(GroundGrid, null))),
|
|
1688
|
+
!coverGone && (React.createElement("div", { "aria-hidden": true, style: {
|
|
1689
|
+
position: 'absolute',
|
|
1690
|
+
inset: 0,
|
|
1691
|
+
zIndex: getUiConfig().zIndexBase + 100,
|
|
1692
|
+
background: '#454545',
|
|
1693
|
+
display: 'flex',
|
|
1694
|
+
alignItems: 'center',
|
|
1695
|
+
justifyContent: 'center',
|
|
1696
|
+
opacity: firstScreenReady ? 0 : 1,
|
|
1697
|
+
transition: 'opacity 0.3s ease',
|
|
1698
|
+
pointerEvents: firstScreenReady ? 'none' : 'auto',
|
|
1699
|
+
} },
|
|
1700
|
+
React.createElement("style", null, `@keyframes cameraStudioFirstScreenSpin{to{transform:rotate(360deg)}}`),
|
|
1701
|
+
React.createElement("div", { style: {
|
|
1702
|
+
width: 28,
|
|
1703
|
+
height: 28,
|
|
1704
|
+
borderRadius: '50%',
|
|
1705
|
+
border: '3px solid rgba(255,255,255,0.25)',
|
|
1706
|
+
borderTopColor: '#ffffff',
|
|
1707
|
+
animation: 'cameraStudioFirstScreenSpin 0.8s linear infinite',
|
|
1708
|
+
} }))))));
|
|
1618
1709
|
}
|
|
1619
1710
|
/**
|
|
1620
1711
|
* i18n 嵌套判定(spec §7):宿主树已包 studio 的 I18nProvider →
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { IProject, ISheet, ISheetObject } from '@dreamerbird/core';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import
|
|
3
|
+
import { Group } from 'three';
|
|
4
|
+
import type { Object3D, AnimationClip } from 'three';
|
|
4
5
|
import type { Library } from './assetLibrary/library';
|
|
5
6
|
import type { ModelEntry } from './assetLibrary/types';
|
|
7
|
+
import type { PrimitiveKind } from './primitives';
|
|
6
8
|
/**
|
|
7
9
|
* FBX 模型放置:页面顶部按钮选文件进入放置模式,半透明 ghost 跟随光标
|
|
8
10
|
* (落点取物体表面优先,y=0 地面兜底),左键确认、Esc/右键取消。
|
|
@@ -33,6 +35,8 @@ export type PlacementEntry = {
|
|
|
33
35
|
assetId?: string;
|
|
34
36
|
/** 库来源条目的 id;存在时优先从资产库加载 GLB */
|
|
35
37
|
libraryEntryId?: string;
|
|
38
|
+
primitive?: PrimitiveKind;
|
|
39
|
+
loadAttempt?: number;
|
|
36
40
|
position: {
|
|
37
41
|
x: number;
|
|
38
42
|
y: number;
|
|
@@ -159,6 +163,14 @@ export declare function usePlacementController(project: IProject, sheet: ISheet)
|
|
|
159
163
|
key: string;
|
|
160
164
|
persisted: Promise<void>;
|
|
161
165
|
};
|
|
166
|
+
placePrimitiveAt: (kind: PrimitiveKind, point: {
|
|
167
|
+
x: number;
|
|
168
|
+
y: number;
|
|
169
|
+
z: number;
|
|
170
|
+
}) => {
|
|
171
|
+
key: string;
|
|
172
|
+
persisted: Promise<void>;
|
|
173
|
+
};
|
|
162
174
|
clearPending: () => void;
|
|
163
175
|
};
|
|
164
176
|
/**
|
|
@@ -214,6 +226,7 @@ export declare function loadLibraryModel(entryId: string, lib?: Library): Promis
|
|
|
214
226
|
group: Group;
|
|
215
227
|
animations: AnimationClip[];
|
|
216
228
|
}>;
|
|
229
|
+
export declare function createFailedModelPlaceholder(key: string): Group;
|
|
217
230
|
/** gizmo 的操作模式(标签文案在 menuModel 里经 t() 取);select = 只点选不挂手柄 */
|
|
218
231
|
export type GizmoMode = 'select' | 'translate' | 'rotate' | 'scale';
|
|
219
232
|
/**
|
|
@@ -232,6 +245,7 @@ export declare function useGizmoMode(): [GizmoMode, (mode: GizmoMode) => void];
|
|
|
232
245
|
* 空壳在大纲里,刷新后消失——和撤销放置的已知行为一样。FBX 文件本体
|
|
233
246
|
* 留在 IndexedDB。
|
|
234
247
|
*/
|
|
248
|
+
export declare function retryPlacement(sheet: ISheet, key: string, replacementEntryId?: string): boolean;
|
|
235
249
|
export declare function deletePlacement(sheet: ISheet, key: string): void;
|
|
236
250
|
/**
|
|
237
251
|
* 选中对象的操作手柄:studio 选中状态里出现登记过的对象时,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlacementManager.d.ts","sourceRoot":"","sources":["../src/PlacementManager.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,QAAQ,EAAU,MAAM,EAAE,YAAY,EAAC,MAAM,mBAAmB,CAAA;AAW7E,OAAO,KAQN,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"PlacementManager.d.ts","sourceRoot":"","sources":["../src/PlacementManager.tsx"],"names":[],"mappings":"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"}
|
package/dist/PlacementManager.js
CHANGED
|
@@ -9,11 +9,13 @@ import getStudio from '@dreamerbird/studio/getStudio';
|
|
|
9
9
|
import { TransformControls, useFBX } from '@react-three/drei';
|
|
10
10
|
import { useFrame, useThree } from '@react-three/fiber';
|
|
11
11
|
import React, { Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
12
|
-
import { AnimationMixer, Box3, BoxGeometry, Mesh, MeshBasicMaterial, Raycaster, Vector3, } from 'three';
|
|
12
|
+
import { AnimationMixer, Box3, BoxGeometry, Group, Mesh, MeshBasicMaterial, Raycaster, Vector3, } from 'three';
|
|
13
13
|
import { SkeletonUtils } from 'three-stdlib';
|
|
14
14
|
import { registerBoneAnchors, unregisterBoneAnchors } from './boneAnchors';
|
|
15
15
|
import { notifyTargetRegistryChange, sheetObjectExists } from './aimTargets';
|
|
16
16
|
import { createGLTFLoader } from './assetLibrary/gltfLoader';
|
|
17
|
+
import { metricScaleFactor } from './assetLibrary/exportGLB';
|
|
18
|
+
import { getFirstScreenGate } from './firstScreenGate';
|
|
17
19
|
import { getLibrary } from './assetLibrary/library';
|
|
18
20
|
import { LibraryError } from './assetLibrary/source';
|
|
19
21
|
import { entryOptions } from './assetLibrary/options';
|
|
@@ -33,6 +35,9 @@ import { collectLibPoseIdsForProps, getPoseTrackId, getTrackIdForProp, } from '.
|
|
|
33
35
|
import { gesturePropConfigShape, resolveGestureBones } from './gestureProps';
|
|
34
36
|
import { computeLegacyPoseMigration } from './poseMode/poseMigration';
|
|
35
37
|
import { parsePoseValue } from './poseMode/poseValue';
|
|
38
|
+
import { createPrimitive } from './primitives';
|
|
39
|
+
import { cloneModelMaterials, collectMaterialSlots, createMaterialOverridesConfig, } from './materials/modelMaterials';
|
|
40
|
+
import { useModelMaterials } from './materials/useModelMaterials';
|
|
36
41
|
import { formatSceneObjectId } from './agent/objectIds';
|
|
37
42
|
import { registerSceneSheetObject, unregisterSceneSheetObject, } from './agent/runtime/objectRuntime';
|
|
38
43
|
import { createPlacementWaiter, rejectPlacement, resolvePlacementPersisted, } from './agent/runtime/placementLifecycle';
|
|
@@ -327,14 +332,18 @@ function usePlacementsList(sheet) {
|
|
|
327
332
|
}, [registry]);
|
|
328
333
|
return list;
|
|
329
334
|
}
|
|
330
|
-
/**
|
|
335
|
+
/** 米制兜底:入库时源单位已换算成米烘进资产(exportGLB.bakeMetricScale),
|
|
336
|
+
* 这里只对未烘焙来源(local 直接拖文件、旧库存)纠偏——已烘焙资产算出
|
|
337
|
+
* 的系数恰好是 1,theatre 缩放属性得以 1 为干净基线。底部对齐落点。
|
|
338
|
+
* ⚠️ 旧库存是「归一化成 5 单位」时代的资产(2026-10-21 前),量出来
|
|
339
|
+
* maxDim=5 不会触发厘米换算,放出来是 5 米高的巨人——重新导入即得米制。 */
|
|
331
340
|
function useNormalizedModel(fbx) {
|
|
332
341
|
return useMemo(() => {
|
|
333
342
|
const box = new Box3().setFromObject(fbx);
|
|
334
343
|
const size = box.getSize(new Vector3());
|
|
335
344
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
336
345
|
return {
|
|
337
|
-
scale: maxDim > 0 ?
|
|
346
|
+
scale: maxDim > 0 ? metricScaleFactor(maxDim) : 1,
|
|
338
347
|
// lift 是模型局部单位,放进 scale 过的组里后底部正好落在 y=0
|
|
339
348
|
lift: -box.min.y,
|
|
340
349
|
};
|
|
@@ -460,6 +469,13 @@ export function usePlacementController(project, sheet) {
|
|
|
460
469
|
});
|
|
461
470
|
return { key, persisted };
|
|
462
471
|
}, [sheet]);
|
|
472
|
+
const placePrimitiveAt = useCallback((kind, point) => {
|
|
473
|
+
const key = uniquePlacedModelKey(sheet, kind);
|
|
474
|
+
const persisted = createPlacementWaiter(key);
|
|
475
|
+
persisted.catch(() => { });
|
|
476
|
+
setPendingEntry({ key, primitive: kind, position: point, pending: true });
|
|
477
|
+
return { key, persisted };
|
|
478
|
+
}, [sheet]);
|
|
463
479
|
const clearPending = useCallback(() => setPendingEntry(null), []);
|
|
464
480
|
return {
|
|
465
481
|
placing,
|
|
@@ -469,6 +485,7 @@ export function usePlacementController(project, sheet) {
|
|
|
469
485
|
cancelPlacing,
|
|
470
486
|
confirmPlacing,
|
|
471
487
|
placeLibraryEntryAt,
|
|
488
|
+
placePrimitiveAt,
|
|
472
489
|
clearPending,
|
|
473
490
|
};
|
|
474
491
|
}
|
|
@@ -614,8 +631,9 @@ function PlacementRigCore({ model, onConfirm, onCancel, confirmOnPointerUp, }) {
|
|
|
614
631
|
if (!confirmOnPointerUp)
|
|
615
632
|
return;
|
|
616
633
|
const el = gl.domElement;
|
|
634
|
+
let nativeDragOverCanvas = false;
|
|
617
635
|
const onUp = (ev) => {
|
|
618
|
-
if (ev.button !== 0)
|
|
636
|
+
if (ev.button !== 0 || nativeDragOverCanvas)
|
|
619
637
|
return;
|
|
620
638
|
// 画布铺满全窗口、垫在面板之下:松手在面板/工具栏等 DOM 上时 target 不是画布,
|
|
621
639
|
// 必须取消(spec:松手在面板区域 = 静默取消)。提示条 pointerEvents:none 不挡
|
|
@@ -630,9 +648,45 @@ function PlacementRigCore({ model, onConfirm, onCancel, confirmOnPointerUp, }) {
|
|
|
630
648
|
}
|
|
631
649
|
onConfirm({ x: p.x, y: p.y, z: p.z });
|
|
632
650
|
};
|
|
651
|
+
const onDragOver = (ev) => {
|
|
652
|
+
ev.preventDefault();
|
|
653
|
+
nativeDragOverCanvas = true;
|
|
654
|
+
pendingMoveRef.current = { x: ev.clientX, y: ev.clientY };
|
|
655
|
+
invalidate();
|
|
656
|
+
};
|
|
657
|
+
const onDragLeave = () => {
|
|
658
|
+
nativeDragOverCanvas = false;
|
|
659
|
+
};
|
|
660
|
+
const onDrop = (ev) => {
|
|
661
|
+
ev.preventDefault();
|
|
662
|
+
ev.stopPropagation();
|
|
663
|
+
nativeDragOverCanvas = false;
|
|
664
|
+
const p = computePoint(ev.clientX, ev.clientY);
|
|
665
|
+
if (p.distanceTo(camera.position) > 300) {
|
|
666
|
+
onCancel();
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
onConfirm({ x: p.x, y: p.y, z: p.z });
|
|
670
|
+
};
|
|
633
671
|
window.addEventListener('pointerup', onUp);
|
|
634
|
-
|
|
635
|
-
|
|
672
|
+
el.addEventListener('dragover', onDragOver);
|
|
673
|
+
el.addEventListener('dragleave', onDragLeave);
|
|
674
|
+
el.addEventListener('drop', onDrop);
|
|
675
|
+
return () => {
|
|
676
|
+
window.removeEventListener('pointerup', onUp);
|
|
677
|
+
el.removeEventListener('dragover', onDragOver);
|
|
678
|
+
el.removeEventListener('dragleave', onDragLeave);
|
|
679
|
+
el.removeEventListener('drop', onDrop);
|
|
680
|
+
};
|
|
681
|
+
}, [
|
|
682
|
+
confirmOnPointerUp,
|
|
683
|
+
gl,
|
|
684
|
+
camera,
|
|
685
|
+
computePoint,
|
|
686
|
+
invalidate,
|
|
687
|
+
onConfirm,
|
|
688
|
+
onCancel,
|
|
689
|
+
]);
|
|
636
690
|
return (
|
|
637
691
|
// 首次 pointermove 之前不显示,避免在原地闪一下
|
|
638
692
|
React.createElement("group", { ref: ghostRef, scale: scale, visible: false },
|
|
@@ -664,17 +718,41 @@ export function PlacedModels({ sheet, project, pendingEntry, onPersisted, select
|
|
|
664
718
|
const entries = pendingEntry && !list.some((e) => e.key === pendingEntry.key)
|
|
665
719
|
? [...list, pendingEntry]
|
|
666
720
|
: list;
|
|
721
|
+
// 首屏闸门(firstScreenGate 机制):先登记本批要等的模型、再报工程就绪,
|
|
722
|
+
// 顺序不可换——先报就绪而 pending 为空会提前揭幕。已落定的 key gate
|
|
723
|
+
// 内部防回加,清单变化重跑只补登新条目;落定后全部调用是 no-op
|
|
724
|
+
const entriesKey = entries.map((e) => e.key).join('|');
|
|
725
|
+
useEffect(() => {
|
|
726
|
+
if (!ready)
|
|
727
|
+
return;
|
|
728
|
+
const gate = getFirstScreenGate();
|
|
729
|
+
for (const e of entries)
|
|
730
|
+
gate.expect(e.key);
|
|
731
|
+
gate.setProjectReady();
|
|
732
|
+
// entries 用 join 串做依赖:内容没变不重复跑
|
|
733
|
+
}, [ready, entriesKey]);
|
|
667
734
|
if (!ready)
|
|
668
735
|
return null;
|
|
669
|
-
return (React.createElement(React.Fragment, null, entries.map((entry) => (React.createElement(Suspense, { key: entry.key
|
|
736
|
+
return (React.createElement(React.Fragment, null, entries.map((entry) => (React.createElement(Suspense, { key: `${entry.key}:${entry.loadAttempt ?? 0}`, fallback: null },
|
|
670
737
|
React.createElement(PlacedModelErrorBoundary, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }))))));
|
|
671
738
|
}
|
|
672
739
|
function PlacedModel({ entry, project, sheet, onPersisted, selectLocked, onObjectMenu, }) {
|
|
740
|
+
if (entry.primitive) {
|
|
741
|
+
return (React.createElement(PrimitivePlacedModel, { entry: entry, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
|
|
742
|
+
}
|
|
673
743
|
if (entry.libraryEntryId) {
|
|
674
744
|
return (React.createElement(LibraryPlacedModel, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
|
|
675
745
|
}
|
|
676
746
|
return (React.createElement(FBXPlacedModel, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
|
|
677
747
|
}
|
|
748
|
+
function PrimitivePlacedModel({ entry, sheet, onPersisted, selectLocked, onObjectMenu, }) {
|
|
749
|
+
const group = useMemo(() => {
|
|
750
|
+
const primitiveGroup = new Group();
|
|
751
|
+
primitiveGroup.add(createPrimitive(entry.primitive));
|
|
752
|
+
return primitiveGroup;
|
|
753
|
+
}, [entry.primitive]);
|
|
754
|
+
return (React.createElement(PlacedModelCore, { entry: entry, sheet: sheet, group: group, animations: [], sourceType: "primitive", onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
|
|
755
|
+
}
|
|
678
756
|
/** 旧本地 FBX 路径:通过 theatre assetStorage 取 URL,再用 useFBX 加载 */
|
|
679
757
|
function FBXPlacedModel({ entry, project, sheet, onPersisted, selectLocked, onObjectMenu, }) {
|
|
680
758
|
const url = useMemo(() => {
|
|
@@ -745,6 +823,19 @@ function LibraryPlacedModel({ entry, project, sheet, onPersisted, selectLocked,
|
|
|
745
823
|
}
|
|
746
824
|
/** 已加载 Group 之后的公共逻辑:克隆、归一化、动画 mixer、点选菜单、持久化 */
|
|
747
825
|
function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersisted, selectLocked, onObjectMenu, }) {
|
|
826
|
+
// 三条来源路径(库 GLB / 本地 FBX / 基础几何体)都在这里汇合:挂载即「这个模型
|
|
827
|
+
// 落定了」,上报首屏闸门(失败路径由 PlacedModelErrorBoundary 上报)
|
|
828
|
+
useEffect(() => {
|
|
829
|
+
getFirstScreenGate().settle(entry.key);
|
|
830
|
+
// entry.key 挂载期内不变
|
|
831
|
+
}, []);
|
|
832
|
+
const instance = useMemo(() => {
|
|
833
|
+
const cloned = SkeletonUtils.clone(group);
|
|
834
|
+
cloneModelMaterials(cloned);
|
|
835
|
+
return cloned;
|
|
836
|
+
}, [group]);
|
|
837
|
+
const materialSlots = useMemo(() => collectMaterialSlots(instance), [instance]);
|
|
838
|
+
const materialOverrides = useMemo(() => createMaterialOverridesConfig(materialSlots), [materialSlots]);
|
|
748
839
|
const additionalProps = useMemo(() => {
|
|
749
840
|
const modelEntryId = sourceType === 'library' ? entry.libraryEntryId : undefined;
|
|
750
841
|
const props = {
|
|
@@ -762,12 +853,13 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
762
853
|
label: () => translate('pg.props.poseOverride'),
|
|
763
854
|
hidden: true,
|
|
764
855
|
}),
|
|
856
|
+
materialOverrides,
|
|
765
857
|
};
|
|
766
858
|
if (sourceType === 'local' && entry.assetId) {
|
|
767
859
|
props.source = fileConfigFor(entry.assetId);
|
|
768
860
|
}
|
|
769
861
|
return props;
|
|
770
|
-
}, [sourceType, entry.assetId, entry.libraryEntryId]);
|
|
862
|
+
}, [sourceType, entry.assetId, entry.libraryEntryId, materialOverrides]);
|
|
771
863
|
const { scale, lift } = useNormalizedModel(group);
|
|
772
864
|
const objRef = useRef(undefined);
|
|
773
865
|
const [sheetObject, setSheetObject] = useState(undefined);
|
|
@@ -794,6 +886,7 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
794
886
|
// 登记为选中 gizmo 的附着目标:被选中时身上挂三轴移动箭头
|
|
795
887
|
useSelectionGizmoTarget(objRef, groupRef);
|
|
796
888
|
const invalidate = useThree((s) => s.invalidate);
|
|
889
|
+
useModelMaterials({ instance, sheetObject, slots: materialSlots, invalidate });
|
|
797
890
|
// 主画面点选:和 App 里 Model 的 selectThingy 同款。放置模式下点击是
|
|
798
891
|
// 确认落点,不选中、不拦截;点 gizmo 箭头穿透过来的误点也要吞掉。
|
|
799
892
|
const selectSelf = (event) => {
|
|
@@ -841,10 +934,6 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
841
934
|
event.stopPropagation();
|
|
842
935
|
enterPoseSession(entry.key);
|
|
843
936
|
};
|
|
844
|
-
// 骨架动画:加载结果按 URL 缓存,同一文件放多个会共享同一份骨架,
|
|
845
|
-
// 互相打架(多个 <primitive> 挂同一个 Object3D 也只有最后一个能显示)。
|
|
846
|
-
// 所以每个实例克隆一份(SkinnedMesh 安全),配独立的 AnimationMixer。
|
|
847
|
-
const instance = useMemo(() => SkeletonUtils.clone(group), [group]);
|
|
848
937
|
const mixer = useMemo(() => new AnimationMixer(instance), [instance]);
|
|
849
938
|
// 登记进 placedInstances 表:锚点父级绑定的 model: 解析用
|
|
850
939
|
usePlacedInstance(entry.key, instance);
|
|
@@ -1040,7 +1129,7 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
1040
1129
|
pendingUncachesRef.current = [];
|
|
1041
1130
|
mixer.stopAllAction();
|
|
1042
1131
|
mixer.uncacheRoot(instance);
|
|
1043
|
-
disposeObjectTree(instance);
|
|
1132
|
+
disposeObjectTree(instance, { disposeTextures: false });
|
|
1044
1133
|
};
|
|
1045
1134
|
}, [mixer, instance]);
|
|
1046
1135
|
// 只在 theatre 序列播放时驱动动画:编辑/摆姿势时模型保持静止。
|
|
@@ -1165,7 +1254,20 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
1165
1254
|
React.createElement(PoseModeRig, { instance: instance, sheetObject: sheetObject, placementKey: entry.key, sheet: sheet }),
|
|
1166
1255
|
React.createElement(PoseModeBar, { instance: instance })))));
|
|
1167
1256
|
}
|
|
1168
|
-
|
|
1257
|
+
export function createFailedModelPlaceholder(key) {
|
|
1258
|
+
const group = new Group();
|
|
1259
|
+
group.name = `Failed/${key}`;
|
|
1260
|
+
const marker = new Mesh(new BoxGeometry(1, 1, 1), new MeshBasicMaterial({ color: 0xcc4444, wireframe: true }));
|
|
1261
|
+
marker.name = `Failed/${key}/Placeholder`;
|
|
1262
|
+
group.add(marker);
|
|
1263
|
+
return group;
|
|
1264
|
+
}
|
|
1265
|
+
function FailedPlacedModel({ entry }) {
|
|
1266
|
+
const group = useMemo(() => createFailedModelPlaceholder(entry.key), [entry.key]);
|
|
1267
|
+
useEffect(() => () => disposeObjectTree(group), [group]);
|
|
1268
|
+
return (React.createElement("group", { position: [entry.position.x, entry.position.y, entry.position.z] },
|
|
1269
|
+
React.createElement("primitive", { object: group })));
|
|
1270
|
+
}
|
|
1169
1271
|
class PlacedModelErrorBoundary extends React.Component {
|
|
1170
1272
|
constructor() {
|
|
1171
1273
|
super(...arguments);
|
|
@@ -1176,12 +1278,18 @@ class PlacedModelErrorBoundary extends React.Component {
|
|
|
1176
1278
|
}
|
|
1177
1279
|
componentDidCatch(err) {
|
|
1178
1280
|
console.warn(`[placement] 模型 ${this.props.entry.key} 加载失败`, err);
|
|
1281
|
+
// 失败也是落定:首屏闸门不能因一个坏模型一直挂遮罩
|
|
1282
|
+
getFirstScreenGate().settle(this.props.entry.key);
|
|
1179
1283
|
// Agent models.create 正等着挂载完成(Task 8):失败必须 reject waiter,
|
|
1180
1284
|
// 不能悬挂;UI 路径没有 waiter,rejectPlacement 是 no-op
|
|
1181
1285
|
rejectPlacement(this.props.entry.key, err);
|
|
1182
1286
|
}
|
|
1183
1287
|
render() {
|
|
1184
|
-
|
|
1288
|
+
if (!this.state.failed)
|
|
1289
|
+
return React.createElement(PlacedModel, { ...this.props });
|
|
1290
|
+
if ('pending' in this.props.entry && this.props.entry.pending)
|
|
1291
|
+
return null;
|
|
1292
|
+
return React.createElement(FailedPlacedModel, { entry: this.props.entry });
|
|
1185
1293
|
}
|
|
1186
1294
|
}
|
|
1187
1295
|
/**
|
|
@@ -1231,6 +1339,34 @@ export function useGizmoMode() {
|
|
|
1231
1339
|
* 空壳在大纲里,刷新后消失——和撤销放置的已知行为一样。FBX 文件本体
|
|
1232
1340
|
* 留在 IndexedDB。
|
|
1233
1341
|
*/
|
|
1342
|
+
export function retryPlacement(sheet, key, replacementEntryId) {
|
|
1343
|
+
const studio = getStudio();
|
|
1344
|
+
if (!studio)
|
|
1345
|
+
return false;
|
|
1346
|
+
const registry = getRegistry(sheet);
|
|
1347
|
+
const list = readRegistry(registry);
|
|
1348
|
+
const current = list.find((entry) => entry.key === key);
|
|
1349
|
+
if (!current)
|
|
1350
|
+
return false;
|
|
1351
|
+
const next = {
|
|
1352
|
+
key: current.key,
|
|
1353
|
+
position: current.position,
|
|
1354
|
+
loadAttempt: (current.loadAttempt ?? 0) + 1,
|
|
1355
|
+
...(replacementEntryId === undefined
|
|
1356
|
+
? current.libraryEntryId
|
|
1357
|
+
? { libraryEntryId: current.libraryEntryId }
|
|
1358
|
+
: current.assetId
|
|
1359
|
+
? { assetId: current.assetId }
|
|
1360
|
+
: current.primitive
|
|
1361
|
+
? { primitive: current.primitive }
|
|
1362
|
+
: {}
|
|
1363
|
+
: { libraryEntryId: replacementEntryId }),
|
|
1364
|
+
};
|
|
1365
|
+
studio.transaction(({ set }) => {
|
|
1366
|
+
set(registry.props.list, JSON.stringify(list.map((entry) => (entry.key === key ? next : entry))));
|
|
1367
|
+
});
|
|
1368
|
+
return true;
|
|
1369
|
+
}
|
|
1234
1370
|
export function deletePlacement(sheet, key) {
|
|
1235
1371
|
const studio = getStudio();
|
|
1236
1372
|
if (!studio)
|
package/dist/agent/api.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { AgentRuntime, AgentSceneRuntimeHandlers } from './runtime';
|
|
|
4
4
|
import type { SceneApi, ObjectsApi } from './commands/scene';
|
|
5
5
|
import type { SelectionApi } from './commands/selection';
|
|
6
6
|
import type { ModelsApi } from './commands/models';
|
|
7
|
+
import type { PrimitivesApi } from './commands/primitives';
|
|
8
|
+
import type { MaterialsApi } from './commands/materials';
|
|
7
9
|
import type { AssetsApi } from './commands/assets';
|
|
8
10
|
import type { PosesApi, PosesSessionApi } from './commands/poses';
|
|
9
11
|
import type { AnimationsApi } from './commands/animations';
|
|
@@ -28,10 +30,13 @@ export type CameraStudioApi = {
|
|
|
28
30
|
readonly ready: Promise<void>;
|
|
29
31
|
/** 当前会话的命令清单与元数据(spec 8.1);未激活 API_NOT_READY,已拆解 API_DISPOSED */
|
|
30
32
|
describe(): RegistryDescription;
|
|
33
|
+
subscribe(listener: () => void): () => void;
|
|
31
34
|
readonly scene: SceneApi;
|
|
32
35
|
readonly selection: SelectionApi;
|
|
33
36
|
readonly objects: ObjectsApi;
|
|
34
37
|
readonly models: ModelsApi;
|
|
38
|
+
readonly primitives: PrimitivesApi;
|
|
39
|
+
readonly materials: MaterialsApi;
|
|
35
40
|
readonly assets: AssetsApi;
|
|
36
41
|
readonly poses: PosesApi & {
|
|
37
42
|
readonly session: PosesSessionApi;
|
package/dist/agent/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/agent/api.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAC,oBAAoB,EAAC,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAC,YAAY,EAAE,yBAAyB,EAAC,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/agent/api.ts"],"names":[],"mappings":"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"}
|