@dreamerbird/camera-studio 0.2.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/dist/CameraScene.d.ts +1 -1
- package/dist/CameraScene.d.ts.map +1 -1
- package/dist/CameraScene.js +36 -6
- package/dist/PlacementManager.d.ts +15 -1
- package/dist/PlacementManager.d.ts.map +1 -1
- package/dist/PlacementManager.js +123 -14
- 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/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/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/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 +9 -1
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,7 +46,7 @@ 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';
|
|
@@ -554,7 +554,7 @@ export function captureAnchorSeed(mainCam) {
|
|
|
554
554
|
* UI 装配可配见 ./uiConfig:停靠框 portal 容器 / z-index 基准 / 语言
|
|
555
555
|
* (宿主已包 I18nProvider 时语言以宿主为准,ui.locale 仅自包时生效)。
|
|
556
556
|
*/
|
|
557
|
-
export function CameraScene({ project, onFlows, onError }) {
|
|
557
|
+
export function CameraScene({ project, onFlows, onError, includeDemoModel = true, }) {
|
|
558
558
|
const t = useT();
|
|
559
559
|
const cameraTargetRef = useRef(null);
|
|
560
560
|
const cameraObjRef = useRef(undefined);
|
|
@@ -795,7 +795,7 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
795
795
|
const s = getStudio();
|
|
796
796
|
return s?._store?._saaz ?? s?._saaz ?? 'no saaz';
|
|
797
797
|
};
|
|
798
|
-
const { placing, pendingEntry, startPlacingFromLibrary, placeLibraryEntryAt, cancelPlacing, confirmPlacing, clearPending, } = usePlacementController(project, sheet);
|
|
798
|
+
const { placing, pendingEntry, startPlacingFromLibrary, placeLibraryEntryAt, placePrimitiveAt, cancelPlacing, confirmPlacing, clearPending, } = usePlacementController(project, sheet);
|
|
799
799
|
const [gizmoMode, setGizmoMode] = useGizmoMode();
|
|
800
800
|
// 正在摆姿势的 placementKey(无会话为 null):Esc/双击空白/播放自动退出都靠它
|
|
801
801
|
const poseSessionKey = usePoseSessionKey();
|
|
@@ -822,6 +822,33 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
822
822
|
setModelDragging(false);
|
|
823
823
|
});
|
|
824
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]);
|
|
825
852
|
// 右键已放置模型弹出的操作菜单:记录目标条目和光标位置
|
|
826
853
|
const [objectMenu, setObjectMenu] = useState(null);
|
|
827
854
|
// 菜单关闭回调:稳定的引用,避免 CanvasContextMenu 不必要的重渲染
|
|
@@ -1363,6 +1390,7 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1363
1390
|
gizmoMode,
|
|
1364
1391
|
selectedPathPoint,
|
|
1365
1392
|
placeLibraryEntryAt,
|
|
1393
|
+
placePrimitiveAt,
|
|
1366
1394
|
enterPathEdit,
|
|
1367
1395
|
exitPathEditOnBlur,
|
|
1368
1396
|
afterSceneObjectDeleted,
|
|
@@ -1373,6 +1401,7 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1373
1401
|
// 模型:转发 placeLibraryEntryAt(Task 8 已返回 {key, persisted},
|
|
1374
1402
|
// 与 UI 点击放置同一条落地管线)
|
|
1375
1403
|
createModel: ({ entry, position }) => deps().placeLibraryEntryAt(entry, position),
|
|
1404
|
+
createPrimitive: ({ kind, position }) => deps().placePrimitiveAt(kind, position),
|
|
1376
1405
|
// 相机:Task 15 决策——生产绑定不自动 setActiveCameraKey。
|
|
1377
1406
|
// create/setActive 是 spec §9 的两条显式命令,Agent 调用
|
|
1378
1407
|
// cameras.create 不得隐性改变会话态;自动激活是 UI 顶栏
|
|
@@ -1441,7 +1470,8 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1441
1470
|
const [coverGone, setCoverGone] = useState(false);
|
|
1442
1471
|
const [firstScreenGate] = useState(() => {
|
|
1443
1472
|
const gate = createFirstScreenGate(() => setFirstScreenReady(true));
|
|
1444
|
-
|
|
1473
|
+
if (includeDemoModel)
|
|
1474
|
+
gate.expect(SCENE_MODEL_GATE_KEY);
|
|
1445
1475
|
installFirstScreenGate(gate);
|
|
1446
1476
|
return gate;
|
|
1447
1477
|
});
|
|
@@ -1633,9 +1663,9 @@ export function CameraScene({ project, onFlows, onError }) {
|
|
|
1633
1663
|
React.createElement(e.mesh, { ref: cameraTargetRef, theatreKey: "Camera / Target", objRef: targetObjRef, hideFromOutline: true, position: DIRECTOR_HOME_TARGET_POSITION, visible: "editor" },
|
|
1634
1664
|
React.createElement("boxGeometry", { attach: "geometry" }),
|
|
1635
1665
|
React.createElement("meshPhongMaterial", { attach: "material", color: "red" })),
|
|
1636
|
-
React.createElement(Suspense, { fallback: null },
|
|
1666
|
+
includeDemoModel && (React.createElement(Suspense, { fallback: null },
|
|
1637
1667
|
React.createElement(InitialLoadProbe, { gateKey: SCENE_MODEL_GATE_KEY },
|
|
1638
|
-
React.createElement(Model, { url: sceneGLB, selectLocked: !!placing }))),
|
|
1668
|
+
React.createElement(Model, { url: sceneGLB, selectLocked: !!placing })))),
|
|
1639
1669
|
placing && (React.createElement(Suspense, { fallback: null },
|
|
1640
1670
|
React.createElement(PlacementRigErrorBoundary, { onCancel: () => {
|
|
1641
1671
|
cancelPlacing();
|
|
@@ -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,7 +9,7 @@ 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';
|
|
@@ -35,6 +35,9 @@ import { collectLibPoseIdsForProps, getPoseTrackId, getTrackIdForProp, } from '.
|
|
|
35
35
|
import { gesturePropConfigShape, resolveGestureBones } from './gestureProps';
|
|
36
36
|
import { computeLegacyPoseMigration } from './poseMode/poseMigration';
|
|
37
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';
|
|
38
41
|
import { formatSceneObjectId } from './agent/objectIds';
|
|
39
42
|
import { registerSceneSheetObject, unregisterSceneSheetObject, } from './agent/runtime/objectRuntime';
|
|
40
43
|
import { createPlacementWaiter, rejectPlacement, resolvePlacementPersisted, } from './agent/runtime/placementLifecycle';
|
|
@@ -466,6 +469,13 @@ export function usePlacementController(project, sheet) {
|
|
|
466
469
|
});
|
|
467
470
|
return { key, persisted };
|
|
468
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]);
|
|
469
479
|
const clearPending = useCallback(() => setPendingEntry(null), []);
|
|
470
480
|
return {
|
|
471
481
|
placing,
|
|
@@ -475,6 +485,7 @@ export function usePlacementController(project, sheet) {
|
|
|
475
485
|
cancelPlacing,
|
|
476
486
|
confirmPlacing,
|
|
477
487
|
placeLibraryEntryAt,
|
|
488
|
+
placePrimitiveAt,
|
|
478
489
|
clearPending,
|
|
479
490
|
};
|
|
480
491
|
}
|
|
@@ -620,8 +631,9 @@ function PlacementRigCore({ model, onConfirm, onCancel, confirmOnPointerUp, }) {
|
|
|
620
631
|
if (!confirmOnPointerUp)
|
|
621
632
|
return;
|
|
622
633
|
const el = gl.domElement;
|
|
634
|
+
let nativeDragOverCanvas = false;
|
|
623
635
|
const onUp = (ev) => {
|
|
624
|
-
if (ev.button !== 0)
|
|
636
|
+
if (ev.button !== 0 || nativeDragOverCanvas)
|
|
625
637
|
return;
|
|
626
638
|
// 画布铺满全窗口、垫在面板之下:松手在面板/工具栏等 DOM 上时 target 不是画布,
|
|
627
639
|
// 必须取消(spec:松手在面板区域 = 静默取消)。提示条 pointerEvents:none 不挡
|
|
@@ -636,9 +648,45 @@ function PlacementRigCore({ model, onConfirm, onCancel, confirmOnPointerUp, }) {
|
|
|
636
648
|
}
|
|
637
649
|
onConfirm({ x: p.x, y: p.y, z: p.z });
|
|
638
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
|
+
};
|
|
639
671
|
window.addEventListener('pointerup', onUp);
|
|
640
|
-
|
|
641
|
-
|
|
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
|
+
]);
|
|
642
690
|
return (
|
|
643
691
|
// 首次 pointermove 之前不显示,避免在原地闪一下
|
|
644
692
|
React.createElement("group", { ref: ghostRef, scale: scale, visible: false },
|
|
@@ -685,15 +733,26 @@ export function PlacedModels({ sheet, project, pendingEntry, onPersisted, select
|
|
|
685
733
|
}, [ready, entriesKey]);
|
|
686
734
|
if (!ready)
|
|
687
735
|
return null;
|
|
688
|
-
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 },
|
|
689
737
|
React.createElement(PlacedModelErrorBoundary, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }))))));
|
|
690
738
|
}
|
|
691
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
|
+
}
|
|
692
743
|
if (entry.libraryEntryId) {
|
|
693
744
|
return (React.createElement(LibraryPlacedModel, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
|
|
694
745
|
}
|
|
695
746
|
return (React.createElement(FBXPlacedModel, { entry: entry, project: project, sheet: sheet, onPersisted: onPersisted, selectLocked: selectLocked, onObjectMenu: onObjectMenu }));
|
|
696
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
|
+
}
|
|
697
756
|
/** 旧本地 FBX 路径:通过 theatre assetStorage 取 URL,再用 useFBX 加载 */
|
|
698
757
|
function FBXPlacedModel({ entry, project, sheet, onPersisted, selectLocked, onObjectMenu, }) {
|
|
699
758
|
const url = useMemo(() => {
|
|
@@ -764,12 +823,19 @@ function LibraryPlacedModel({ entry, project, sheet, onPersisted, selectLocked,
|
|
|
764
823
|
}
|
|
765
824
|
/** 已加载 Group 之后的公共逻辑:克隆、归一化、动画 mixer、点选菜单、持久化 */
|
|
766
825
|
function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersisted, selectLocked, onObjectMenu, }) {
|
|
767
|
-
//
|
|
826
|
+
// 三条来源路径(库 GLB / 本地 FBX / 基础几何体)都在这里汇合:挂载即「这个模型
|
|
768
827
|
// 落定了」,上报首屏闸门(失败路径由 PlacedModelErrorBoundary 上报)
|
|
769
828
|
useEffect(() => {
|
|
770
829
|
getFirstScreenGate().settle(entry.key);
|
|
771
830
|
// entry.key 挂载期内不变
|
|
772
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]);
|
|
773
839
|
const additionalProps = useMemo(() => {
|
|
774
840
|
const modelEntryId = sourceType === 'library' ? entry.libraryEntryId : undefined;
|
|
775
841
|
const props = {
|
|
@@ -787,12 +853,13 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
787
853
|
label: () => translate('pg.props.poseOverride'),
|
|
788
854
|
hidden: true,
|
|
789
855
|
}),
|
|
856
|
+
materialOverrides,
|
|
790
857
|
};
|
|
791
858
|
if (sourceType === 'local' && entry.assetId) {
|
|
792
859
|
props.source = fileConfigFor(entry.assetId);
|
|
793
860
|
}
|
|
794
861
|
return props;
|
|
795
|
-
}, [sourceType, entry.assetId, entry.libraryEntryId]);
|
|
862
|
+
}, [sourceType, entry.assetId, entry.libraryEntryId, materialOverrides]);
|
|
796
863
|
const { scale, lift } = useNormalizedModel(group);
|
|
797
864
|
const objRef = useRef(undefined);
|
|
798
865
|
const [sheetObject, setSheetObject] = useState(undefined);
|
|
@@ -819,6 +886,7 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
819
886
|
// 登记为选中 gizmo 的附着目标:被选中时身上挂三轴移动箭头
|
|
820
887
|
useSelectionGizmoTarget(objRef, groupRef);
|
|
821
888
|
const invalidate = useThree((s) => s.invalidate);
|
|
889
|
+
useModelMaterials({ instance, sheetObject, slots: materialSlots, invalidate });
|
|
822
890
|
// 主画面点选:和 App 里 Model 的 selectThingy 同款。放置模式下点击是
|
|
823
891
|
// 确认落点,不选中、不拦截;点 gizmo 箭头穿透过来的误点也要吞掉。
|
|
824
892
|
const selectSelf = (event) => {
|
|
@@ -866,10 +934,6 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
866
934
|
event.stopPropagation();
|
|
867
935
|
enterPoseSession(entry.key);
|
|
868
936
|
};
|
|
869
|
-
// 骨架动画:加载结果按 URL 缓存,同一文件放多个会共享同一份骨架,
|
|
870
|
-
// 互相打架(多个 <primitive> 挂同一个 Object3D 也只有最后一个能显示)。
|
|
871
|
-
// 所以每个实例克隆一份(SkinnedMesh 安全),配独立的 AnimationMixer。
|
|
872
|
-
const instance = useMemo(() => SkeletonUtils.clone(group), [group]);
|
|
873
937
|
const mixer = useMemo(() => new AnimationMixer(instance), [instance]);
|
|
874
938
|
// 登记进 placedInstances 表:锚点父级绑定的 model: 解析用
|
|
875
939
|
usePlacedInstance(entry.key, instance);
|
|
@@ -1065,7 +1129,7 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
1065
1129
|
pendingUncachesRef.current = [];
|
|
1066
1130
|
mixer.stopAllAction();
|
|
1067
1131
|
mixer.uncacheRoot(instance);
|
|
1068
|
-
disposeObjectTree(instance);
|
|
1132
|
+
disposeObjectTree(instance, { disposeTextures: false });
|
|
1069
1133
|
};
|
|
1070
1134
|
}, [mixer, instance]);
|
|
1071
1135
|
// 只在 theatre 序列播放时驱动动画:编辑/摆姿势时模型保持静止。
|
|
@@ -1190,7 +1254,20 @@ function PlacedModelCore({ entry, sheet, group, animations, sourceType, onPersis
|
|
|
1190
1254
|
React.createElement(PoseModeRig, { instance: instance, sheetObject: sheetObject, placementKey: entry.key, sheet: sheet }),
|
|
1191
1255
|
React.createElement(PoseModeBar, { instance: instance })))));
|
|
1192
1256
|
}
|
|
1193
|
-
|
|
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
|
+
}
|
|
1194
1271
|
class PlacedModelErrorBoundary extends React.Component {
|
|
1195
1272
|
constructor() {
|
|
1196
1273
|
super(...arguments);
|
|
@@ -1208,7 +1285,11 @@ class PlacedModelErrorBoundary extends React.Component {
|
|
|
1208
1285
|
rejectPlacement(this.props.entry.key, err);
|
|
1209
1286
|
}
|
|
1210
1287
|
render() {
|
|
1211
|
-
|
|
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 });
|
|
1212
1293
|
}
|
|
1213
1294
|
}
|
|
1214
1295
|
/**
|
|
@@ -1258,6 +1339,34 @@ export function useGizmoMode() {
|
|
|
1258
1339
|
* 空壳在大纲里,刷新后消失——和撤销放置的已知行为一样。FBX 文件本体
|
|
1259
1340
|
* 留在 IndexedDB。
|
|
1260
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
|
+
}
|
|
1261
1370
|
export function deletePlacement(sheet, key) {
|
|
1262
1371
|
const studio = getStudio();
|
|
1263
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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"}
|
package/dist/agent/api.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { AgentApiError } from './errors';
|
|
24
24
|
import { AgentCommandRegistry } from './registry';
|
|
25
25
|
import { isGenerationSupersededError } from './runtime';
|
|
26
|
+
import { subscribeSceneSheetObjects } from './runtime/objectRuntime';
|
|
26
27
|
import { registerAllAgentCommands } from './registerCommands';
|
|
27
28
|
const defaultRegistryFactory = (runtime) => {
|
|
28
29
|
const registry = new AgentCommandRegistry();
|
|
@@ -120,6 +121,20 @@ export function createCameraStudioApi(options) {
|
|
|
120
121
|
}
|
|
121
122
|
throw new AgentApiError('API_NOT_READY', 'Camera Studio API 尚未激活(场景未挂载)');
|
|
122
123
|
},
|
|
124
|
+
subscribe(listener) {
|
|
125
|
+
const captured = session;
|
|
126
|
+
if (!captured)
|
|
127
|
+
return () => { };
|
|
128
|
+
const studio = captured.runtime.getStudio();
|
|
129
|
+
const offSelection = studio?.onSelectionChange(() => listener()) ?? (() => { });
|
|
130
|
+
const offHistoric = captured.runtime.subscribeHistoric(listener);
|
|
131
|
+
const offSceneObjects = subscribeSceneSheetObjects(listener);
|
|
132
|
+
return () => {
|
|
133
|
+
offSelection();
|
|
134
|
+
offHistoric();
|
|
135
|
+
offSceneObjects();
|
|
136
|
+
};
|
|
137
|
+
},
|
|
123
138
|
scene: {
|
|
124
139
|
getSnapshot: () => invoke('scene.getSnapshot', undefined),
|
|
125
140
|
},
|
|
@@ -136,11 +151,21 @@ export function createCameraStudioApi(options) {
|
|
|
136
151
|
},
|
|
137
152
|
models: {
|
|
138
153
|
create: (args) => invoke('models.create', args),
|
|
154
|
+
retry: (args) => invoke('models.retry', args),
|
|
155
|
+
replace: (args) => invoke('models.replace', args),
|
|
156
|
+
},
|
|
157
|
+
primitives: {
|
|
158
|
+
create: (args) => invoke('primitives.create', args),
|
|
159
|
+
},
|
|
160
|
+
materials: {
|
|
161
|
+
list: (args) => invoke('materials.list', args),
|
|
162
|
+
update: (args) => invoke('materials.update', args),
|
|
139
163
|
},
|
|
140
164
|
assets: {
|
|
141
165
|
list: (args) => invoke('assets.list', args),
|
|
142
166
|
get: (args) => invoke('assets.get', args),
|
|
143
167
|
importModel: (args) => invoke('assets.importModel', args),
|
|
168
|
+
importModels: (args) => invoke('assets.importModels', args),
|
|
144
169
|
rename: (args) => invoke('assets.rename', args),
|
|
145
170
|
delete: (args) => invoke('assets.delete', args),
|
|
146
171
|
},
|