@vvfx/sdk 0.2.8 → 0.2.10
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/config.js +231 -0
- package/dist/exporter/config.js +18 -0
- package/dist/exporter/const.js +71 -0
- package/dist/exporter/export-media.js +878 -0
- package/dist/exporter/exporter.js +121 -0
- package/dist/exporter/index.js +1 -0
- package/dist/exporter/types.js +1 -0
- package/dist/exporter/utils.js +190 -0
- package/dist/exporter/wav-audio.js +50 -0
- package/dist/gesture-handler/gizmo/adsorption-gizmo.js +227 -0
- package/dist/gesture-handler/gizmo/control-gizmo.js +149 -0
- package/dist/gesture-handler/gizmo/gizmo.js +17 -0
- package/dist/gesture-handler/gizmo/icon-gizmo.js +189 -0
- package/dist/gesture-handler/gizmo/index.js +6 -0
- package/dist/gesture-handler/gizmo/item-create-gizmo.js +256 -0
- package/dist/gesture-handler/gizmo/loading-gizmo.js +320 -0
- package/dist/gesture-handler/gizmo/mask-gizmo.js +648 -0
- package/dist/gesture-handler/gizmo/picture-cut-gizmo.js +550 -0
- package/dist/gesture-handler/gizmo/picture-expand-gizmo.js +491 -0
- package/dist/gesture-handler/gizmo/preference-gizmo.js +148 -0
- package/dist/gesture-handler/gizmo/selector-gizmo.d.ts +1 -1
- package/dist/gesture-handler/gizmo/selector-gizmo.js +468 -0
- package/dist/gesture-handler/gizmo/sprite-text-edit-gizmo.js +247 -0
- package/dist/gesture-handler/gizmo/text-gizmo.js +1033 -0
- package/dist/gesture-handler/gizmo/transform-gizmo.js +1199 -0
- package/dist/gesture-handler/gizmo/type.js +18 -0
- package/dist/gesture-handler/index.js +3 -0
- package/dist/gesture-handler/src/gesture-handler.js +770 -0
- package/dist/gesture-handler/src/icons.js +24 -0
- package/dist/gesture-handler/utils.js +40 -0
- package/dist/html-overlay/anchor-navigation-runtime.js +27 -0
- package/dist/html-overlay/auto-height-layout.d.ts +6 -0
- package/dist/html-overlay/auto-height-layout.js +7 -0
- package/dist/html-overlay/auto-height-runtime.js +132 -0
- package/dist/html-overlay/document-runtime.js +71 -0
- package/dist/html-overlay/document-transform.js +96 -0
- package/dist/html-overlay/dom-order.js +8 -0
- package/dist/html-overlay/host-message-runtime.js +24 -0
- package/dist/html-overlay/html-capture-runtime.js +282 -0
- package/dist/html-overlay/html-viewport-style.js +10 -0
- package/dist/html-overlay/index.js +1 -0
- package/dist/html-overlay/manager.d.ts +1 -0
- package/dist/html-overlay/manager.js +1677 -0
- package/dist/html-overlay/mime-utils.js +21 -0
- package/dist/html-overlay/overlay-transform.js +57 -0
- package/dist/html-overlay/path-utils.js +35 -0
- package/dist/html-overlay/shell-runtime.js +12 -0
- package/dist/index.cjs +34 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -75311
- package/dist/index.mjs +75337 -0
- package/dist/index.mjs.map +1 -0
- package/dist/layer-order.js +8 -0
- package/dist/math/box2.js +396 -0
- package/dist/math/circle.js +131 -0
- package/dist/math/euler.js +35 -0
- package/dist/math/index.js +13 -0
- package/dist/math/line2.js +153 -0
- package/dist/math/line3.js +36 -0
- package/dist/math/matrix4.js +207 -0
- package/dist/math/plane.js +52 -0
- package/dist/math/quaternion.js +3 -0
- package/dist/math/ray-caster.js +25 -0
- package/dist/math/ray.js +47 -0
- package/dist/math/type.js +1 -0
- package/dist/math/utils.js +77 -0
- package/dist/math/vector2.js +70 -0
- package/dist/math/vector3.js +60 -0
- package/dist/screen-shot/index.js +1 -0
- package/dist/screen-shot/screen-shot.js +113 -0
- package/dist/sdk-item/base-item.js +160 -0
- package/dist/sdk-item/card-item.js +131 -0
- package/dist/sdk-item/effects-item.js +144 -0
- package/dist/sdk-item/frame-item.js +217 -0
- package/dist/sdk-item/generator-item.js +188 -0
- package/dist/sdk-item/group-item.js +116 -0
- package/dist/sdk-item/index.js +39 -0
- package/dist/sdk-item/sprite-item.js +147 -0
- package/dist/sdk-item/text-item.js +223 -0
- package/dist/sdk-item/types.d.ts +1 -1
- package/dist/sdk-item/types.js +15 -0
- package/dist/sdk-item/video-item.js +174 -0
- package/dist/sdk.js +1908 -0
- package/dist/service/UndoRedo.js +79 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/player.d.ts +1 -1
- package/dist/shared/player.js +9 -0
- package/dist/shared/spec.d.ts +1 -0
- package/dist/shared/spec.js +1 -0
- package/dist/size-adapte/index.js +1 -0
- package/dist/size-adapte/size-adapt.js +395 -0
- package/dist/types.d.ts +5 -1
- package/dist/types.js +17 -0
- package/dist/utils/background-manager.js +189 -0
- package/dist/utils/common-utils.js +183 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/interaction-utils.js +205 -0
- package/dist/utils/json-data-utils.js +1529 -0
- package/dist/utils/layout-utils.js +1217 -0
- package/dist/utils/page-data-utils.js +5397 -0
- package/dist/utils/player-data-utils.js +48 -0
- package/dist/utils/types.js +8 -0
- package/dist/wireframe/common/box.js +18 -0
- package/dist/wireframe/common/dashed-line.js +21 -0
- package/dist/wireframe/common/line.js +17 -0
- package/dist/wireframe/common/pixi-ext.js +1 -0
- package/dist/wireframe/common/pixi.js +31 -0
- package/dist/wireframe/index.js +4 -0
- package/package.json +10 -3
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,1529 @@
|
|
|
1
|
+
import { Vector2, Vector3 } from '../math';
|
|
2
|
+
import { spec, generateGUID, PlayerVersion } from '../shared';
|
|
3
|
+
/**
|
|
4
|
+
* @description 创建 Frame 元素的内部合成场景
|
|
5
|
+
* @param frameId Frame 元素ID(用于标识)
|
|
6
|
+
* @param size 场景尺寸 [width, height]
|
|
7
|
+
* @param name 场景名称
|
|
8
|
+
* @returns Frame 内部的合成场景
|
|
9
|
+
*/
|
|
10
|
+
export function createFrameCompositionScene(frameId, size, name) {
|
|
11
|
+
const compositionId = generateGUID();
|
|
12
|
+
const timelineAssetId = generateGUID();
|
|
13
|
+
const compositionComponentId = generateGUID();
|
|
14
|
+
const timelineAsset = {
|
|
15
|
+
id: timelineAssetId,
|
|
16
|
+
dataType: spec.DataType.TimelineAsset,
|
|
17
|
+
tracks: [],
|
|
18
|
+
};
|
|
19
|
+
const compositionComponent = {
|
|
20
|
+
id: compositionComponentId,
|
|
21
|
+
item: { id: compositionId },
|
|
22
|
+
dataType: spec.DataType.CompositionComponent,
|
|
23
|
+
items: [],
|
|
24
|
+
timelineAsset: { id: timelineAssetId },
|
|
25
|
+
sceneBindings: [],
|
|
26
|
+
};
|
|
27
|
+
const [width, height] = size;
|
|
28
|
+
const composition = {
|
|
29
|
+
id: compositionId,
|
|
30
|
+
name: name + '_inner',
|
|
31
|
+
duration: 999,
|
|
32
|
+
startTime: 0,
|
|
33
|
+
endBehavior: spec.EndBehavior.forward,
|
|
34
|
+
previewSize: [...size],
|
|
35
|
+
camera: {
|
|
36
|
+
fov: 60,
|
|
37
|
+
far: 40,
|
|
38
|
+
near: 0.1,
|
|
39
|
+
clipMode: 1,
|
|
40
|
+
position: [0, 0, 8],
|
|
41
|
+
rotation: [0, 0, 0],
|
|
42
|
+
// @ts-expect-error: aspect 属性在 CameraOptions 上有定义但类型声明不完善
|
|
43
|
+
aspect: width / height,
|
|
44
|
+
},
|
|
45
|
+
components: [{ id: compositionComponentId }],
|
|
46
|
+
};
|
|
47
|
+
const scene = {
|
|
48
|
+
version: spec.JSONSceneVersion['3_6'],
|
|
49
|
+
playerVersion: {
|
|
50
|
+
web: PlayerVersion,
|
|
51
|
+
native: '10.7.6',
|
|
52
|
+
},
|
|
53
|
+
type: 'ge',
|
|
54
|
+
compositionId: compositionId,
|
|
55
|
+
compositions: [composition],
|
|
56
|
+
images: [],
|
|
57
|
+
plugins: [],
|
|
58
|
+
bins: [],
|
|
59
|
+
textures: [],
|
|
60
|
+
items: [],
|
|
61
|
+
components: [compositionComponent],
|
|
62
|
+
materials: [],
|
|
63
|
+
shaders: [],
|
|
64
|
+
geometries: [],
|
|
65
|
+
animations: [],
|
|
66
|
+
miscs: [timelineAsset],
|
|
67
|
+
};
|
|
68
|
+
return scene;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* @description 创建基础场景信息
|
|
72
|
+
* @param viewSize 画布大小
|
|
73
|
+
* @returns 场景信息
|
|
74
|
+
*/
|
|
75
|
+
export function getBasicScene(viewSize = [750, 750], name = 'AI 设计师画布') {
|
|
76
|
+
const compositionId = generateGUID();
|
|
77
|
+
const timelineAsset = {
|
|
78
|
+
id: generateGUID(),
|
|
79
|
+
dataType: spec.DataType.TimelineAsset,
|
|
80
|
+
tracks: [],
|
|
81
|
+
};
|
|
82
|
+
const basicCompositionComponent = {
|
|
83
|
+
id: generateGUID(),
|
|
84
|
+
item: { id: compositionId },
|
|
85
|
+
dataType: spec.DataType.CompositionComponent,
|
|
86
|
+
items: [],
|
|
87
|
+
timelineAsset: { id: timelineAsset.id },
|
|
88
|
+
sceneBindings: [],
|
|
89
|
+
};
|
|
90
|
+
const basicComposition = {
|
|
91
|
+
id: compositionId,
|
|
92
|
+
name,
|
|
93
|
+
duration: 999,
|
|
94
|
+
startTime: 0,
|
|
95
|
+
endBehavior: spec.EndBehavior.forward,
|
|
96
|
+
previewSize: [...viewSize],
|
|
97
|
+
camera: {
|
|
98
|
+
fov: 60,
|
|
99
|
+
far: 40,
|
|
100
|
+
near: 0.1,
|
|
101
|
+
clipMode: 1,
|
|
102
|
+
position: [0, 0, 8],
|
|
103
|
+
rotation: [0, 0, 0],
|
|
104
|
+
//@ts-expect-error @赤芍 to fix4
|
|
105
|
+
aspect: viewSize[0] / viewSize[1]
|
|
106
|
+
},
|
|
107
|
+
components: [{ id: basicCompositionComponent.id }],
|
|
108
|
+
};
|
|
109
|
+
const scene = {
|
|
110
|
+
version: spec.JSONSceneVersion['3_6'],
|
|
111
|
+
playerVersion: {
|
|
112
|
+
web: PlayerVersion,
|
|
113
|
+
native: '10.7.6'
|
|
114
|
+
},
|
|
115
|
+
type: 'ge',
|
|
116
|
+
compositionId: compositionId,
|
|
117
|
+
compositions: [],
|
|
118
|
+
images: [],
|
|
119
|
+
plugins: [],
|
|
120
|
+
bins: [],
|
|
121
|
+
textures: [],
|
|
122
|
+
items: [],
|
|
123
|
+
components: [],
|
|
124
|
+
materials: [],
|
|
125
|
+
shaders: [],
|
|
126
|
+
geometries: [],
|
|
127
|
+
animations: [],
|
|
128
|
+
miscs: [],
|
|
129
|
+
};
|
|
130
|
+
scene.miscs.push(timelineAsset);
|
|
131
|
+
scene.compositions.push(basicComposition);
|
|
132
|
+
scene.components.push(basicCompositionComponent);
|
|
133
|
+
return scene;
|
|
134
|
+
}
|
|
135
|
+
export function createSpriteItemJSON(spriteId, position, rotation, size, scale, name, texture) {
|
|
136
|
+
const componentId = generateGUID();
|
|
137
|
+
const spriteItem = {
|
|
138
|
+
id: spriteId,
|
|
139
|
+
name,
|
|
140
|
+
dataType: spec.DataType.VFXItemData,
|
|
141
|
+
components: [{ id: componentId }],
|
|
142
|
+
delay: 0,
|
|
143
|
+
duration: 999,
|
|
144
|
+
endBehavior: spec.EndBehavior.restart,
|
|
145
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
146
|
+
type: spec.ItemType.sprite,
|
|
147
|
+
visible: true,
|
|
148
|
+
transform: {
|
|
149
|
+
position: { x: position.x, y: position.y, z: position.z },
|
|
150
|
+
eulerHint: { x: rotation.x, y: rotation.y, z: rotation.z },
|
|
151
|
+
size: { x: size.x, y: size.y },
|
|
152
|
+
scale: { x: scale.x, y: scale.y, z: scale.z },
|
|
153
|
+
anchor: { x: 0, y: 0 }
|
|
154
|
+
},
|
|
155
|
+
content: undefined,
|
|
156
|
+
};
|
|
157
|
+
const component = {
|
|
158
|
+
id: componentId,
|
|
159
|
+
item: { id: spriteId },
|
|
160
|
+
dataType: spec.DataType.SpriteComponent,
|
|
161
|
+
options: {
|
|
162
|
+
startColor: [1, 1, 1, 1],
|
|
163
|
+
},
|
|
164
|
+
renderer: {
|
|
165
|
+
renderMode: spec.RenderMode.MESH,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
if (texture) {
|
|
169
|
+
component.renderer.texture = { id: texture };
|
|
170
|
+
}
|
|
171
|
+
const activationPlayableAsset = {
|
|
172
|
+
id: generateGUID(),
|
|
173
|
+
dataType: spec.DataType.ActivationPlayableAsset,
|
|
174
|
+
};
|
|
175
|
+
const activationTrack = {
|
|
176
|
+
id: generateGUID(),
|
|
177
|
+
dataType: spec.DataType.ActivationTrack,
|
|
178
|
+
children: [],
|
|
179
|
+
clips: [{
|
|
180
|
+
start: 0,
|
|
181
|
+
duration: 999,
|
|
182
|
+
endBehavior: spec.EndBehavior.restart,
|
|
183
|
+
asset: {
|
|
184
|
+
id: activationPlayableAsset.id,
|
|
185
|
+
},
|
|
186
|
+
}],
|
|
187
|
+
};
|
|
188
|
+
const spriteColorPlayableAsset = {
|
|
189
|
+
id: generateGUID(),
|
|
190
|
+
dataType: spec.DataType.SpriteColorPlayableAsset,
|
|
191
|
+
startColor: [1, 1, 1, 1],
|
|
192
|
+
};
|
|
193
|
+
const spriteColorTrack = {
|
|
194
|
+
id: generateGUID(),
|
|
195
|
+
dataType: spec.DataType.SpriteColorTrack,
|
|
196
|
+
children: [],
|
|
197
|
+
clips: [{
|
|
198
|
+
start: 0,
|
|
199
|
+
duration: 999,
|
|
200
|
+
endBehavior: spec.EndBehavior.restart,
|
|
201
|
+
asset: {
|
|
202
|
+
id: spriteColorPlayableAsset.id,
|
|
203
|
+
},
|
|
204
|
+
}]
|
|
205
|
+
};
|
|
206
|
+
const tracks = [activationTrack, spriteColorTrack];
|
|
207
|
+
const playableAssets = [activationPlayableAsset, spriteColorPlayableAsset];
|
|
208
|
+
return {
|
|
209
|
+
item: spriteItem,
|
|
210
|
+
components: [component],
|
|
211
|
+
tracks,
|
|
212
|
+
playableAssets,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* @description 创建视频生成器 Item JSON
|
|
217
|
+
* @description 以 SpriteItem 形式渲染,但设置透明颜色
|
|
218
|
+
* @description 在 content 中存储 isVideoGenerator 标记
|
|
219
|
+
*/
|
|
220
|
+
export function createVideoGeneratorItemJSON(id, position, rotation, size, scale, name) {
|
|
221
|
+
const componentId = generateGUID();
|
|
222
|
+
const vfxItem = {
|
|
223
|
+
id,
|
|
224
|
+
name,
|
|
225
|
+
dataType: spec.DataType.VFXItemData,
|
|
226
|
+
components: [{ id: componentId }],
|
|
227
|
+
delay: 0,
|
|
228
|
+
duration: 999,
|
|
229
|
+
endBehavior: spec.EndBehavior.restart,
|
|
230
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
231
|
+
type: spec.ItemType.sprite,
|
|
232
|
+
visible: true,
|
|
233
|
+
transform: {
|
|
234
|
+
position: { x: position.x, y: position.y, z: position.z },
|
|
235
|
+
eulerHint: { x: rotation.x, y: rotation.y, z: rotation.z },
|
|
236
|
+
size: { x: size.x, y: size.y },
|
|
237
|
+
scale: { x: scale.x, y: scale.y, z: scale.z },
|
|
238
|
+
anchor: { x: 0, y: 0 }
|
|
239
|
+
},
|
|
240
|
+
// 存储视频生成器标记
|
|
241
|
+
content: {
|
|
242
|
+
isVideoGenerator: true
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
// 使用透明颜色 [1, 1, 1, 0]
|
|
246
|
+
const component = {
|
|
247
|
+
id: componentId,
|
|
248
|
+
item: { id },
|
|
249
|
+
dataType: spec.DataType.SpriteComponent,
|
|
250
|
+
options: {
|
|
251
|
+
startColor: [1, 1, 1, 0], // 完全透明
|
|
252
|
+
},
|
|
253
|
+
renderer: {
|
|
254
|
+
renderMode: spec.RenderMode.MESH,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
const activationPlayableAsset = {
|
|
258
|
+
id: generateGUID(),
|
|
259
|
+
dataType: spec.DataType.ActivationPlayableAsset,
|
|
260
|
+
};
|
|
261
|
+
const activationTrack = {
|
|
262
|
+
id: generateGUID(),
|
|
263
|
+
dataType: spec.DataType.ActivationTrack,
|
|
264
|
+
children: [],
|
|
265
|
+
clips: [{
|
|
266
|
+
start: 0,
|
|
267
|
+
duration: 999,
|
|
268
|
+
endBehavior: spec.EndBehavior.restart,
|
|
269
|
+
asset: {
|
|
270
|
+
id: activationPlayableAsset.id,
|
|
271
|
+
},
|
|
272
|
+
}],
|
|
273
|
+
};
|
|
274
|
+
// 透明颜色 playable asset
|
|
275
|
+
const spriteColorPlayableAsset = {
|
|
276
|
+
id: generateGUID(),
|
|
277
|
+
dataType: spec.DataType.SpriteColorPlayableAsset,
|
|
278
|
+
startColor: [1, 1, 1, 0], // 完全透明
|
|
279
|
+
};
|
|
280
|
+
const spriteColorTrack = {
|
|
281
|
+
id: generateGUID(),
|
|
282
|
+
dataType: spec.DataType.SpriteColorTrack,
|
|
283
|
+
children: [],
|
|
284
|
+
clips: [{
|
|
285
|
+
start: 0,
|
|
286
|
+
duration: 999,
|
|
287
|
+
endBehavior: spec.EndBehavior.restart,
|
|
288
|
+
asset: {
|
|
289
|
+
id: spriteColorPlayableAsset.id,
|
|
290
|
+
},
|
|
291
|
+
}],
|
|
292
|
+
};
|
|
293
|
+
const tracks = [activationTrack, spriteColorTrack];
|
|
294
|
+
const playableAssets = [activationPlayableAsset, spriteColorPlayableAsset];
|
|
295
|
+
return {
|
|
296
|
+
item: vfxItem,
|
|
297
|
+
components: [component],
|
|
298
|
+
tracks,
|
|
299
|
+
playableAssets,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
export function createNullItemJSON(nullId, name, position, scale = new Vector2(1, 1), rotation = new Vector3(), worldSize = new Vector2(1, 1)) {
|
|
303
|
+
const positionInfo = JSON.parse(JSON.stringify(position));
|
|
304
|
+
const nullItem = {
|
|
305
|
+
id: nullId,
|
|
306
|
+
name: name,
|
|
307
|
+
duration: 999,
|
|
308
|
+
type: spec.ItemType.null,
|
|
309
|
+
visible: true,
|
|
310
|
+
endBehavior: spec.EndBehavior.restart,
|
|
311
|
+
delay: 0,
|
|
312
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
313
|
+
content: {
|
|
314
|
+
options: {
|
|
315
|
+
startColor: [1, 1, 1, 1],
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
components: [],
|
|
319
|
+
transform: {
|
|
320
|
+
position: positionInfo,
|
|
321
|
+
eulerHint: { x: rotation.x, y: rotation.y, z: rotation.z },
|
|
322
|
+
size: JSON.parse(JSON.stringify(worldSize)),
|
|
323
|
+
scale: { x: scale.x, y: scale.y, z: 1 },
|
|
324
|
+
},
|
|
325
|
+
dataType: spec.DataType.VFXItemData,
|
|
326
|
+
};
|
|
327
|
+
const activationPlayableAsset = {
|
|
328
|
+
id: generateGUID(),
|
|
329
|
+
dataType: spec.DataType.ActivationPlayableAsset,
|
|
330
|
+
};
|
|
331
|
+
const activationTrack = {
|
|
332
|
+
id: generateGUID(),
|
|
333
|
+
dataType: spec.DataType.ActivationTrack,
|
|
334
|
+
children: [],
|
|
335
|
+
clips: [{
|
|
336
|
+
start: 0,
|
|
337
|
+
duration: 999,
|
|
338
|
+
endBehavior: spec.EndBehavior.restart,
|
|
339
|
+
asset: {
|
|
340
|
+
id: activationPlayableAsset.id,
|
|
341
|
+
},
|
|
342
|
+
}],
|
|
343
|
+
};
|
|
344
|
+
const tracks = [activationTrack];
|
|
345
|
+
const playableAssets = [activationPlayableAsset];
|
|
346
|
+
return {
|
|
347
|
+
item: nullItem,
|
|
348
|
+
components: [],
|
|
349
|
+
tracks,
|
|
350
|
+
playableAssets,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
export function createTextItemJSON(textId, name, position, rotation, size, fontInfo) {
|
|
354
|
+
const componentId = generateGUID();
|
|
355
|
+
const textItem = {
|
|
356
|
+
id: textId,
|
|
357
|
+
name,
|
|
358
|
+
dataType: spec.DataType.VFXItemData,
|
|
359
|
+
components: [{ id: componentId }],
|
|
360
|
+
delay: 0,
|
|
361
|
+
duration: 999,
|
|
362
|
+
endBehavior: spec.EndBehavior.restart,
|
|
363
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
364
|
+
type: spec.ItemType.text,
|
|
365
|
+
visible: true,
|
|
366
|
+
transform: {
|
|
367
|
+
position: { x: position.x, y: position.y, z: position.z },
|
|
368
|
+
eulerHint: { x: rotation.x, y: rotation.y, z: rotation.z },
|
|
369
|
+
scale: { x: size.x, y: size.y, z: 1 },
|
|
370
|
+
anchor: { x: 0, y: 0 }
|
|
371
|
+
},
|
|
372
|
+
content: undefined,
|
|
373
|
+
};
|
|
374
|
+
const component = {
|
|
375
|
+
id: componentId,
|
|
376
|
+
item: { id: textId },
|
|
377
|
+
dataType: spec.DataType.TextComponent,
|
|
378
|
+
options: fontInfo,
|
|
379
|
+
renderer: { renderMode: 1 }
|
|
380
|
+
};
|
|
381
|
+
const activationPlayableAsset = {
|
|
382
|
+
id: generateGUID(),
|
|
383
|
+
dataType: spec.DataType.ActivationPlayableAsset,
|
|
384
|
+
};
|
|
385
|
+
const activationTrack = {
|
|
386
|
+
id: generateGUID(),
|
|
387
|
+
dataType: spec.DataType.ActivationTrack,
|
|
388
|
+
children: [],
|
|
389
|
+
clips: [{
|
|
390
|
+
start: 0,
|
|
391
|
+
duration: 999,
|
|
392
|
+
endBehavior: spec.EndBehavior.restart,
|
|
393
|
+
asset: {
|
|
394
|
+
id: activationPlayableAsset.id,
|
|
395
|
+
},
|
|
396
|
+
}],
|
|
397
|
+
};
|
|
398
|
+
const tracks = [activationTrack];
|
|
399
|
+
const playableAssets = [activationPlayableAsset];
|
|
400
|
+
return {
|
|
401
|
+
item: textItem,
|
|
402
|
+
components: [component],
|
|
403
|
+
tracks,
|
|
404
|
+
playableAssets,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
export function createVideoItemJSON(videoId, position, rotation, size, scale, name, isMuted = false, transparent = false, volume = 1, playbackRate = 1, texture, video) {
|
|
408
|
+
const componentId = generateGUID();
|
|
409
|
+
const videoItem = {
|
|
410
|
+
id: videoId,
|
|
411
|
+
name,
|
|
412
|
+
dataType: spec.DataType.VFXItemData,
|
|
413
|
+
components: [{ id: componentId }],
|
|
414
|
+
delay: 0,
|
|
415
|
+
duration: 999,
|
|
416
|
+
endBehavior: spec.EndBehavior.restart,
|
|
417
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
418
|
+
type: spec.ItemType.video,
|
|
419
|
+
visible: true,
|
|
420
|
+
transform: {
|
|
421
|
+
position: { x: position.x, y: position.y, z: position.z },
|
|
422
|
+
eulerHint: { x: rotation.x, y: rotation.y, z: rotation.z },
|
|
423
|
+
size: { x: size.x, y: size.y },
|
|
424
|
+
scale: { x: scale.x, y: scale.y, z: scale.z },
|
|
425
|
+
anchor: { x: 0, y: 0 }
|
|
426
|
+
},
|
|
427
|
+
content: undefined,
|
|
428
|
+
};
|
|
429
|
+
const component = {
|
|
430
|
+
id: componentId,
|
|
431
|
+
item: { id: videoId },
|
|
432
|
+
dataType: spec.DataType.VideoComponent,
|
|
433
|
+
options: {
|
|
434
|
+
startColor: [1, 1, 1, 1],
|
|
435
|
+
muted: isMuted,
|
|
436
|
+
video: {
|
|
437
|
+
id: video
|
|
438
|
+
},
|
|
439
|
+
volume,
|
|
440
|
+
playbackRate,
|
|
441
|
+
transparent
|
|
442
|
+
},
|
|
443
|
+
renderer: {
|
|
444
|
+
renderMode: spec.RenderMode.MESH,
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
if (texture) {
|
|
448
|
+
component.renderer.texture = { id: texture };
|
|
449
|
+
}
|
|
450
|
+
const activationPlayableAsset = {
|
|
451
|
+
id: generateGUID(),
|
|
452
|
+
dataType: spec.DataType.ActivationPlayableAsset,
|
|
453
|
+
};
|
|
454
|
+
const activationTrack = {
|
|
455
|
+
id: generateGUID(),
|
|
456
|
+
dataType: spec.DataType.ActivationTrack,
|
|
457
|
+
children: [],
|
|
458
|
+
clips: [{
|
|
459
|
+
start: 0,
|
|
460
|
+
duration: 999,
|
|
461
|
+
endBehavior: spec.EndBehavior.restart,
|
|
462
|
+
asset: {
|
|
463
|
+
id: activationPlayableAsset.id,
|
|
464
|
+
},
|
|
465
|
+
}],
|
|
466
|
+
};
|
|
467
|
+
const spriteColorPlayableAsset = {
|
|
468
|
+
id: generateGUID(),
|
|
469
|
+
dataType: spec.DataType.SpriteColorPlayableAsset,
|
|
470
|
+
startColor: [1, 1, 1, 1],
|
|
471
|
+
};
|
|
472
|
+
const spriteColorTrack = {
|
|
473
|
+
id: generateGUID(),
|
|
474
|
+
dataType: spec.DataType.SpriteColorTrack,
|
|
475
|
+
children: [],
|
|
476
|
+
clips: [{
|
|
477
|
+
start: 0,
|
|
478
|
+
duration: 999,
|
|
479
|
+
endBehavior: spec.EndBehavior.restart,
|
|
480
|
+
asset: {
|
|
481
|
+
id: spriteColorPlayableAsset.id,
|
|
482
|
+
},
|
|
483
|
+
}]
|
|
484
|
+
};
|
|
485
|
+
const tracks = [activationTrack, spriteColorTrack];
|
|
486
|
+
const playableAssets = [activationPlayableAsset, spriteColorPlayableAsset];
|
|
487
|
+
return {
|
|
488
|
+
item: videoItem,
|
|
489
|
+
components: [component],
|
|
490
|
+
tracks,
|
|
491
|
+
playableAssets,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* @description 创建预合成元素
|
|
496
|
+
* @param effectsId 预合成元素ID
|
|
497
|
+
* @param position 位置信息
|
|
498
|
+
* @param rotation 旋转信息
|
|
499
|
+
* @param scale 缩放信息
|
|
500
|
+
* @param name 名称
|
|
501
|
+
*/
|
|
502
|
+
export function createCompositionItemJSON(effectsId, position, rotation, scale, name) {
|
|
503
|
+
const positionInfo = JSON.parse(JSON.stringify(position));
|
|
504
|
+
const compositionItem = {
|
|
505
|
+
id: effectsId,
|
|
506
|
+
name: name,
|
|
507
|
+
duration: 999,
|
|
508
|
+
type: spec.ItemType.null,
|
|
509
|
+
visible: true,
|
|
510
|
+
endBehavior: spec.EndBehavior.restart,
|
|
511
|
+
delay: 0,
|
|
512
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
513
|
+
content: {
|
|
514
|
+
options: {
|
|
515
|
+
startColor: [1, 1, 1, 1],
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
components: [],
|
|
519
|
+
transform: {
|
|
520
|
+
position: positionInfo,
|
|
521
|
+
eulerHint: { x: rotation.x, y: rotation.y, z: rotation.z },
|
|
522
|
+
size: { x: 1, y: 1 },
|
|
523
|
+
scale: { x: scale.x, y: scale.y, z: 1 },
|
|
524
|
+
},
|
|
525
|
+
dataType: spec.DataType.VFXItemData,
|
|
526
|
+
};
|
|
527
|
+
const activationPlayableAsset = {
|
|
528
|
+
id: generateGUID(),
|
|
529
|
+
dataType: spec.DataType.ActivationPlayableAsset,
|
|
530
|
+
};
|
|
531
|
+
const activationTrack = {
|
|
532
|
+
id: generateGUID(),
|
|
533
|
+
dataType: spec.DataType.ActivationTrack,
|
|
534
|
+
children: [],
|
|
535
|
+
clips: [{
|
|
536
|
+
start: 0,
|
|
537
|
+
duration: 999,
|
|
538
|
+
endBehavior: spec.EndBehavior.restart,
|
|
539
|
+
asset: {
|
|
540
|
+
id: activationPlayableAsset.id,
|
|
541
|
+
},
|
|
542
|
+
}],
|
|
543
|
+
};
|
|
544
|
+
const tracks = [activationTrack];
|
|
545
|
+
const playableAssets = [activationPlayableAsset];
|
|
546
|
+
return {
|
|
547
|
+
item: compositionItem,
|
|
548
|
+
components: [],
|
|
549
|
+
tracks,
|
|
550
|
+
playableAssets,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* @description 添加元素信息到JSON
|
|
555
|
+
* @param scene 场景信息
|
|
556
|
+
* @param itemInfo 元素信息
|
|
557
|
+
*/
|
|
558
|
+
export function addItemInfoToScene(scene, itemInfo) {
|
|
559
|
+
scene.items.push(itemInfo.item);
|
|
560
|
+
scene.miscs.push(...itemInfo.tracks, ...itemInfo.playableAssets);
|
|
561
|
+
scene.components.push(...itemInfo.components);
|
|
562
|
+
const mainComposition = scene.compositions.find(c => c.id === scene.compositionId) ?? scene.compositions[0];
|
|
563
|
+
const mainCompositionComponent = scene.components.find(c => c.id === mainComposition.components[0]?.id);
|
|
564
|
+
if (mainCompositionComponent) {
|
|
565
|
+
mainCompositionComponent.items.push({ id: itemInfo.item.id });
|
|
566
|
+
const bindingTrackData = {
|
|
567
|
+
id: generateGUID(),
|
|
568
|
+
dataType: spec.DataType.ObjectBindingTrack,
|
|
569
|
+
children: [],
|
|
570
|
+
clips: [],
|
|
571
|
+
};
|
|
572
|
+
for (const track of itemInfo.tracks) {
|
|
573
|
+
bindingTrackData.children.push({ id: track.id });
|
|
574
|
+
}
|
|
575
|
+
const timelineAsset = scene.miscs.find(t => t.id === mainCompositionComponent.timelineAsset.id);
|
|
576
|
+
timelineAsset.tracks.push({ id: bindingTrackData.id });
|
|
577
|
+
mainCompositionComponent.sceneBindings.push({
|
|
578
|
+
key: { id: bindingTrackData.id },
|
|
579
|
+
value: { id: itemInfo.item.id },
|
|
580
|
+
});
|
|
581
|
+
scene.miscs.push(bindingTrackData);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* @description 设置元素父节点信息
|
|
586
|
+
* @param scene 场景信息
|
|
587
|
+
* @param itemId 元素Id
|
|
588
|
+
* @param parentId 父节点Id
|
|
589
|
+
*/
|
|
590
|
+
export function setItemJSONParentId(scene, itemId, parentId) {
|
|
591
|
+
const targetItemIndex = scene.items.findIndex(item => item.id === itemId);
|
|
592
|
+
if (targetItemIndex < 0) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
clearItemParentInfo(scene, itemId);
|
|
596
|
+
const targetParentItem = scene.items.find(item => item.id === parentId);
|
|
597
|
+
if (!targetParentItem) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
scene.items[targetItemIndex].parentId = parentId;
|
|
601
|
+
const { position, eulerHint, scale } = targetParentItem.transform;
|
|
602
|
+
scene.items[targetItemIndex].transform ??= {
|
|
603
|
+
position: { x: 0, y: 0, z: 0 },
|
|
604
|
+
eulerHint: { x: 0, y: 0, z: 0 },
|
|
605
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
606
|
+
size: { x: 1, y: 1 },
|
|
607
|
+
};
|
|
608
|
+
scene.items[targetItemIndex].transform.position.x -= position.x;
|
|
609
|
+
scene.items[targetItemIndex].transform.position.y -= position.y;
|
|
610
|
+
scene.items[targetItemIndex].transform.position.z -= position.z;
|
|
611
|
+
scene.items[targetItemIndex].transform.eulerHint.x -= eulerHint.x;
|
|
612
|
+
scene.items[targetItemIndex].transform.eulerHint.y -= eulerHint.y;
|
|
613
|
+
scene.items[targetItemIndex].transform.eulerHint.z -= eulerHint.z;
|
|
614
|
+
scene.items[targetItemIndex].transform.scale.x /= (scale.x === 0 ? 1 : scale.x);
|
|
615
|
+
scene.items[targetItemIndex].transform.scale.y /= (scale.y === 0 ? 1 : scale.y);
|
|
616
|
+
scene.items[targetItemIndex].transform.scale.z /= (scale.z === 0 ? 1 : scale.z);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* @description 清理元素父节点信息
|
|
620
|
+
* @param scene 场景信息
|
|
621
|
+
* @param itemId 元素Id
|
|
622
|
+
*/
|
|
623
|
+
export function clearItemParentInfo(scene, itemId) {
|
|
624
|
+
const targetItemIndex = scene.items.findIndex(item => item.id === itemId);
|
|
625
|
+
if (!scene.items[targetItemIndex]?.parentId) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const targetParentItem = scene.items.find(item => item.id === scene.items[targetItemIndex].parentId);
|
|
629
|
+
if (!targetParentItem) {
|
|
630
|
+
delete scene.items[targetItemIndex].parentId;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const { position, eulerHint, scale } = targetParentItem.transform;
|
|
634
|
+
scene.items[targetItemIndex].transform ??= {
|
|
635
|
+
position: { x: 0, y: 0, z: 0 },
|
|
636
|
+
eulerHint: { x: 0, y: 0, z: 0 },
|
|
637
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
638
|
+
size: { x: 1, y: 1 },
|
|
639
|
+
};
|
|
640
|
+
scene.items[targetItemIndex].transform.position.x += position.x;
|
|
641
|
+
scene.items[targetItemIndex].transform.position.y += position.y;
|
|
642
|
+
scene.items[targetItemIndex].transform.position.z += position.z;
|
|
643
|
+
scene.items[targetItemIndex].transform.eulerHint.x += eulerHint.x;
|
|
644
|
+
scene.items[targetItemIndex].transform.eulerHint.y += eulerHint.y;
|
|
645
|
+
scene.items[targetItemIndex].transform.eulerHint.z += eulerHint.z;
|
|
646
|
+
scene.items[targetItemIndex].transform.scale.x *= (scale.x === 0 ? 1 : scale.x);
|
|
647
|
+
scene.items[targetItemIndex].transform.scale.y *= (scale.y === 0 ? 1 : scale.y);
|
|
648
|
+
scene.items[targetItemIndex].transform.scale.z *= (scale.z === 0 ? 1 : scale.z);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* @description 删除元素信息
|
|
652
|
+
* @param scene 场景信息
|
|
653
|
+
* @param itemId 元素Id
|
|
654
|
+
*/
|
|
655
|
+
export function deleteItemInfoFromScene(scene, itemId) {
|
|
656
|
+
const item = scene.items.find(item => item.id === itemId);
|
|
657
|
+
if (!item) {
|
|
658
|
+
console.warn(`item ${itemId} is not existed.`);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
// 删除 元素 信息
|
|
662
|
+
scene.items = scene.items.filter(item => item.id !== itemId);
|
|
663
|
+
// 刷新 父子级 元素信息
|
|
664
|
+
scene.items.forEach(child => {
|
|
665
|
+
if (child.parentId === itemId) {
|
|
666
|
+
child.transform ??= {
|
|
667
|
+
position: { x: 0, y: 0, z: 0 },
|
|
668
|
+
eulerHint: { x: 0, y: 0, z: 0 },
|
|
669
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
670
|
+
size: { x: 1, y: 1 },
|
|
671
|
+
};
|
|
672
|
+
child.transform.position.x += item.transform.position.x;
|
|
673
|
+
child.transform.position.y += item.transform.position.y;
|
|
674
|
+
child.transform.position.z += item.transform.position.z;
|
|
675
|
+
child.transform.eulerHint.x += item.transform.eulerHint.x;
|
|
676
|
+
child.transform.eulerHint.y += item.transform.eulerHint.y;
|
|
677
|
+
child.transform.eulerHint.z += item.transform.eulerHint.z;
|
|
678
|
+
child.transform.scale.x *= (item.transform.scale.x !== 0) ? item.transform.scale.x : 1;
|
|
679
|
+
child.transform.scale.y *= (item.transform.scale.y !== 0) ? item.transform.scale.y : 1;
|
|
680
|
+
child.transform.scale.z *= (item.transform.scale.z !== 0) ? item.transform.scale.z : 1;
|
|
681
|
+
}
|
|
682
|
+
delete child.parentId;
|
|
683
|
+
});
|
|
684
|
+
// 删除 Component 信息
|
|
685
|
+
const targetComponents = scene.components.filter(c => c.item.id === itemId);
|
|
686
|
+
scene.components = scene.components.filter(c => !(targetComponents.map(component => component.id).includes(c.id)));
|
|
687
|
+
targetComponents.forEach(component => {
|
|
688
|
+
switch (component.dataType) {
|
|
689
|
+
case spec.DataType.SpriteComponent: {
|
|
690
|
+
// 删除 Texture 信息
|
|
691
|
+
if (component.renderer.texture) {
|
|
692
|
+
const textureHasOtherUsed = scene.components.find(c => c.renderer?.texture?.id === component.renderer.texture.id);
|
|
693
|
+
if (!textureHasOtherUsed) {
|
|
694
|
+
const targetTexture = scene.textures?.find(t => t.id === component.renderer.texture?.id);
|
|
695
|
+
if (targetTexture) {
|
|
696
|
+
scene.textures = scene.textures?.filter(t => t.id !== targetTexture.id);
|
|
697
|
+
scene.images = scene.images.filter(image => image.id !== targetTexture.source.id);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
case spec.DataType.VideoComponent: {
|
|
704
|
+
if (component.renderer.texture) {
|
|
705
|
+
const textureHasOtherUsed = scene.components.find(c => c.renderer?.texture?.id === component.renderer.texture.id);
|
|
706
|
+
if (!textureHasOtherUsed) {
|
|
707
|
+
const targetTexture = scene.textures?.find(t => t.id === component.renderer.texture?.id);
|
|
708
|
+
if (targetTexture) {
|
|
709
|
+
scene.textures = scene.textures?.filter(t => t.id !== targetTexture.id);
|
|
710
|
+
scene.videos = scene.videos?.filter(video => video.id !== targetTexture.source.id);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
// 删除 SceneBinding 信息
|
|
718
|
+
const toDeleteMiscIds = [];
|
|
719
|
+
let objectTrackId;
|
|
720
|
+
let timelineAssetId;
|
|
721
|
+
if ((+scene.version) >= 3.3) {
|
|
722
|
+
const compositionComponents = scene.components.filter(c => c.dataType === spec.DataType.CompositionComponent);
|
|
723
|
+
const compositionComponent = compositionComponents.find(c => c.items.find(item => item.id === itemId));
|
|
724
|
+
if (compositionComponent) {
|
|
725
|
+
compositionComponent.items = compositionComponent.items.filter(i => i.id !== itemId);
|
|
726
|
+
timelineAssetId = compositionComponent.timelineAsset.id;
|
|
727
|
+
objectTrackId = compositionComponent.sceneBindings.find(binding => binding.value.id === itemId)?.key.id;
|
|
728
|
+
compositionComponent.sceneBindings = compositionComponent.sceneBindings.filter(binding => binding.value.id !== itemId);
|
|
729
|
+
if (objectTrackId) {
|
|
730
|
+
toDeleteMiscIds.push(objectTrackId);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
const targetComposition = scene.compositions.find((c) => c.items.find((item) => item.id === itemId));
|
|
736
|
+
if (targetComposition) {
|
|
737
|
+
targetComposition.items = targetComposition.items.filter((i) => i.id !== itemId);
|
|
738
|
+
timelineAssetId = targetComposition.timelineAsset.id;
|
|
739
|
+
objectTrackId = targetComposition.sceneBindings.find((binding) => binding.value.id === itemId)?.key.id;
|
|
740
|
+
targetComposition.sceneBindings = targetComposition.sceneBindings.filter((binding) => binding.value.id !== itemId);
|
|
741
|
+
if (objectTrackId) {
|
|
742
|
+
toDeleteMiscIds.push(objectTrackId);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// 删除 miscs 信息
|
|
747
|
+
const timelineAssetIndex = scene.miscs.findIndex(misc => misc.id === timelineAssetId);
|
|
748
|
+
if (timelineAssetIndex >= 0) {
|
|
749
|
+
scene.miscs[timelineAssetIndex].tracks = scene.miscs[timelineAssetIndex].tracks.filter((track) => track.id !== objectTrackId);
|
|
750
|
+
}
|
|
751
|
+
const objectTrack = scene.miscs.find(misc => misc.id === objectTrackId);
|
|
752
|
+
if (objectTrack) {
|
|
753
|
+
objectTrack.children.forEach((child) => {
|
|
754
|
+
toDeleteMiscIds.push(child.id);
|
|
755
|
+
const targetTrack = scene.miscs.find(misc => misc.id === child.id);
|
|
756
|
+
if (targetTrack) {
|
|
757
|
+
toDeleteMiscIds.push(...targetTrack.clips.map((clip) => clip.asset.id));
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
toDeleteMiscIds.push(...objectTrack.children.map((c) => c.id));
|
|
761
|
+
}
|
|
762
|
+
scene.miscs = scene.miscs.filter(misc => !toDeleteMiscIds.includes(misc.id));
|
|
763
|
+
return scene;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* @description 从场景中移除并获取元素的完整 ItemJSONInfo
|
|
767
|
+
* @param scene 场景信息
|
|
768
|
+
* @param itemId 元素ID
|
|
769
|
+
* @returns ItemJSONInfo 或 undefined(如果元素不存在)
|
|
770
|
+
*/
|
|
771
|
+
export function removeItemInfoFromScene(scene, itemId) {
|
|
772
|
+
const item = scene.items.find(item => item.id === itemId);
|
|
773
|
+
if (!item) {
|
|
774
|
+
return undefined;
|
|
775
|
+
}
|
|
776
|
+
// 1. 提取 item
|
|
777
|
+
const itemData = { ...item };
|
|
778
|
+
// 2. 提取 components(包括关联的 texture 等资源)
|
|
779
|
+
const components = scene.components.filter(c => c.item.id === itemId);
|
|
780
|
+
// 3. 查找 compositionComponent
|
|
781
|
+
const compositionComponents = scene.components.filter(c => c.dataType === spec.DataType.CompositionComponent);
|
|
782
|
+
const compositionComponent = compositionComponents.find(c => c.items.find((i) => i.id === itemId));
|
|
783
|
+
// 4. 提取 tracks 和 playableAssets
|
|
784
|
+
const tracks = [];
|
|
785
|
+
const playableAssets = [];
|
|
786
|
+
if (compositionComponent) {
|
|
787
|
+
// 查找 sceneBindings
|
|
788
|
+
const sceneBinding = compositionComponent.sceneBindings.find(binding => binding.value.id === itemId);
|
|
789
|
+
if (sceneBinding) {
|
|
790
|
+
const objectTrackId = sceneBinding.key.id;
|
|
791
|
+
const objectTrack = scene.miscs.find(misc => misc.id === objectTrackId);
|
|
792
|
+
if (objectTrack) {
|
|
793
|
+
// 提取子 tracks
|
|
794
|
+
const childTrackIds = (objectTrack.children || []).map((child) => child.id);
|
|
795
|
+
childTrackIds.forEach(trackId => {
|
|
796
|
+
const track = scene.miscs.find(misc => misc.id === trackId);
|
|
797
|
+
if (track) {
|
|
798
|
+
tracks.push({ ...track });
|
|
799
|
+
// 提取 track 中的 playableAssets
|
|
800
|
+
const trackClips = track.clips;
|
|
801
|
+
if (trackClips) {
|
|
802
|
+
trackClips.forEach((clip) => {
|
|
803
|
+
const asset = scene.miscs.find(misc => misc.id === clip.asset.id);
|
|
804
|
+
if (asset) {
|
|
805
|
+
playableAssets.push({ ...asset });
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
// 添加 objectBindingTrack 本身到 tracks
|
|
812
|
+
tracks.push({ ...objectTrack });
|
|
813
|
+
// objectBindingTrack clips 中的 asset 也需要提取
|
|
814
|
+
const objectTrackClips = objectTrack.clips;
|
|
815
|
+
if (objectTrackClips) {
|
|
816
|
+
objectTrackClips.forEach((clip) => {
|
|
817
|
+
const asset = scene.miscs.find(misc => misc.id === clip.asset.id);
|
|
818
|
+
if (asset) {
|
|
819
|
+
playableAssets.push({ ...asset });
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// 5. 从 scene 中移除数据(使用已有的 deleteItemInfoFromScene 函数)
|
|
827
|
+
deleteItemInfoFromScene(scene, itemId);
|
|
828
|
+
return {
|
|
829
|
+
item: itemData,
|
|
830
|
+
components: components.map(c => ({ ...c })),
|
|
831
|
+
tracks,
|
|
832
|
+
playableAssets,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
export function getTextureUrlById(scene, itemId) {
|
|
836
|
+
let textureUrl;
|
|
837
|
+
scene.components.forEach(component => {
|
|
838
|
+
if (component.item.id === itemId) {
|
|
839
|
+
if (component.dataType === spec.DataType.SpriteComponent && component.renderer.texture) {
|
|
840
|
+
const targetTexture = scene.textures?.find(t => t.id === component.renderer.texture?.id);
|
|
841
|
+
if (targetTexture) {
|
|
842
|
+
textureUrl = scene.images.find(image => image.id === targetTexture.source.id)?.url;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
else if (component.dataType === spec.DataType.VideoComponent && component.renderer.texture) {
|
|
846
|
+
const targetTexture = scene.textures?.find(t => t.id === component.renderer.texture?.id);
|
|
847
|
+
if (targetTexture) {
|
|
848
|
+
textureUrl = scene.videos?.find(video => video.id === targetTexture.source.id)?.url;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
return textureUrl;
|
|
854
|
+
}
|
|
855
|
+
export function addCompositionItemByComposition(main, inputChild, compositionItemId, parentId) {
|
|
856
|
+
const child = JSON.parse(JSON.stringify(inputChild));
|
|
857
|
+
const delay = 0;
|
|
858
|
+
const duration = child.compositions?.[0]?.duration ?? 5;
|
|
859
|
+
const endBehavior = spec.EndBehavior.restart;
|
|
860
|
+
const compositionTransform = {
|
|
861
|
+
position: {
|
|
862
|
+
x: 0,
|
|
863
|
+
y: 0,
|
|
864
|
+
z: 0,
|
|
865
|
+
},
|
|
866
|
+
eulerHint: {
|
|
867
|
+
x: 0,
|
|
868
|
+
y: 0,
|
|
869
|
+
z: 0,
|
|
870
|
+
},
|
|
871
|
+
scale: {
|
|
872
|
+
x: 1,
|
|
873
|
+
y: 1,
|
|
874
|
+
z: 1,
|
|
875
|
+
},
|
|
876
|
+
};
|
|
877
|
+
const compositionOptions = {
|
|
878
|
+
refId: child.compositionId ?? child.compositions[0].id,
|
|
879
|
+
startColor: [1, 1, 1, 1],
|
|
880
|
+
};
|
|
881
|
+
const compositionContent = {
|
|
882
|
+
options: compositionOptions,
|
|
883
|
+
};
|
|
884
|
+
const specificationItem = {
|
|
885
|
+
id: compositionItemId,
|
|
886
|
+
components: [],
|
|
887
|
+
name: 'ref_' + child.compositions[0].name,
|
|
888
|
+
duration,
|
|
889
|
+
dataType: spec.DataType.VFXItemData,
|
|
890
|
+
parentId,
|
|
891
|
+
type: spec.ItemType.composition,
|
|
892
|
+
visible: true,
|
|
893
|
+
endBehavior,
|
|
894
|
+
delay,
|
|
895
|
+
renderLevel: spec.RenderLevel.BPlus,
|
|
896
|
+
content: compositionContent,
|
|
897
|
+
transform: compositionTransform,
|
|
898
|
+
};
|
|
899
|
+
main.items.push(specificationItem);
|
|
900
|
+
const activationPlayableAsset = {
|
|
901
|
+
id: generateGUID(),
|
|
902
|
+
dataType: 'ActivationPlayableAsset',
|
|
903
|
+
};
|
|
904
|
+
const activationTrack = {
|
|
905
|
+
id: generateGUID(),
|
|
906
|
+
dataType: 'ActivationTrack',
|
|
907
|
+
children: [],
|
|
908
|
+
clips: [
|
|
909
|
+
{
|
|
910
|
+
start: delay,
|
|
911
|
+
duration,
|
|
912
|
+
endBehavior,
|
|
913
|
+
asset: {
|
|
914
|
+
id: activationPlayableAsset.id,
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
],
|
|
918
|
+
};
|
|
919
|
+
const subCompositionPlayableAsset = {
|
|
920
|
+
id: generateGUID(),
|
|
921
|
+
dataType: 'SubCompositionPlayableAsset',
|
|
922
|
+
};
|
|
923
|
+
const subCompositionTrack = {
|
|
924
|
+
id: generateGUID(),
|
|
925
|
+
dataType: 'SubCompositionTrack',
|
|
926
|
+
children: [],
|
|
927
|
+
clips: [
|
|
928
|
+
{
|
|
929
|
+
start: 0,
|
|
930
|
+
duration,
|
|
931
|
+
endBehavior,
|
|
932
|
+
asset: {
|
|
933
|
+
id: subCompositionPlayableAsset.id,
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
],
|
|
937
|
+
};
|
|
938
|
+
const bindingTrackData = {
|
|
939
|
+
id: generateGUID(),
|
|
940
|
+
dataType: spec.DataType.ObjectBindingTrack,
|
|
941
|
+
children: [],
|
|
942
|
+
clips: [],
|
|
943
|
+
};
|
|
944
|
+
main.miscs.push(bindingTrackData);
|
|
945
|
+
const tracks = [];
|
|
946
|
+
const playableAssets = [];
|
|
947
|
+
tracks.push(activationTrack, subCompositionTrack);
|
|
948
|
+
playableAssets.push(activationPlayableAsset, subCompositionPlayableAsset);
|
|
949
|
+
if (Number(main.version) <= 3.3) {
|
|
950
|
+
const mainCompositionIndex = main.compositions.findIndex((c) => c.id === main.compositionId);
|
|
951
|
+
if (mainCompositionIndex >= 0) {
|
|
952
|
+
// @ts-expect-error type to fix
|
|
953
|
+
main.compositions[mainCompositionIndex].items.push({ id: specificationItem.id });
|
|
954
|
+
// @ts-expect-error type to fix
|
|
955
|
+
const timelineAsset = main.miscs.find((misc) => misc.id === main.compositions[mainCompositionIndex].timelineAsset.id);
|
|
956
|
+
timelineAsset?.tracks.push({ id: bindingTrackData.id });
|
|
957
|
+
// @ts-expect-error type to fix
|
|
958
|
+
main.compositions[mainCompositionIndex].sceneBindings.push({
|
|
959
|
+
key: { id: bindingTrackData.id },
|
|
960
|
+
value: { id: specificationItem.id },
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
const compositionComponent = main.components.find((c) => c.id === main.compositions[0].components[0].id && c.dataType === spec.DataType.CompositionComponent);
|
|
966
|
+
compositionComponent.items.push({ id: specificationItem.id });
|
|
967
|
+
const timelineAsset = main.miscs.find((misc) => misc.id === compositionComponent.timelineAsset.id);
|
|
968
|
+
timelineAsset?.tracks.push({ id: bindingTrackData.id });
|
|
969
|
+
compositionComponent.sceneBindings.push({
|
|
970
|
+
key: { id: bindingTrackData.id },
|
|
971
|
+
value: { id: specificationItem.id },
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
tracks.forEach((track) => {
|
|
975
|
+
if (!track.path) {
|
|
976
|
+
bindingTrackData.children.push({ id: track.id });
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
main.miscs.push(...tracks);
|
|
980
|
+
main.miscs.push(...playableAssets);
|
|
981
|
+
Object.keys(main).forEach(property => {
|
|
982
|
+
if (property === 'compositionId') {
|
|
983
|
+
main.compositionId ??= main.compositions[0].id;
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
else if (property === 'components') {
|
|
987
|
+
// 补充CompositionComponent.item.id
|
|
988
|
+
child.components.forEach(component => {
|
|
989
|
+
if (component.dataType === spec.DataType.CompositionComponent && !component.item.id) {
|
|
990
|
+
child.compositions.forEach(composition => {
|
|
991
|
+
if (composition.components[0]?.id === component.id) {
|
|
992
|
+
component.item.id = composition.id;
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
// @ts-expect-error type to fix
|
|
999
|
+
if (child[property] && Array.isArray(child[property])) {
|
|
1000
|
+
// @ts-expect-error type to fix
|
|
1001
|
+
main[property] ??= [];
|
|
1002
|
+
// @ts-expect-error type to fix
|
|
1003
|
+
main[property].push(...child[property]);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
return main;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* @description 删除预合成数据
|
|
1010
|
+
* @param scene 场景数据
|
|
1011
|
+
* @param itemId 预合成ID
|
|
1012
|
+
* @returns 场景数据
|
|
1013
|
+
*/
|
|
1014
|
+
export function deleteCompositionItemFromScene(scene, itemIds) {
|
|
1015
|
+
const compositionIds = [];
|
|
1016
|
+
itemIds.forEach(id => {
|
|
1017
|
+
const item = scene.items.find(item => item.id === id);
|
|
1018
|
+
if (!item) {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
deleteItemInfoFromScene(scene, id);
|
|
1022
|
+
if (item.type === spec.ItemType.composition) {
|
|
1023
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1024
|
+
const compositionId = item?.content.options.refId;
|
|
1025
|
+
compositionIds.push(compositionId);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
compositionIds.forEach(compositionId => {
|
|
1029
|
+
deleteCompositionFromScene(scene, compositionId);
|
|
1030
|
+
});
|
|
1031
|
+
return scene;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* @description 删除合成信息
|
|
1035
|
+
* @param scene 场景数据
|
|
1036
|
+
* @param compositionId 合成ID
|
|
1037
|
+
*/
|
|
1038
|
+
export function deleteCompositionFromScene(scene, compositionId) {
|
|
1039
|
+
const compositionComponent = scene.components.find(com => com.item.id === compositionId);
|
|
1040
|
+
if (compositionComponent) {
|
|
1041
|
+
const timelineAssetId = compositionComponent.timelineAsset.id;
|
|
1042
|
+
scene.miscs = scene.miscs.filter(value => value.id !== timelineAssetId);
|
|
1043
|
+
scene.compositions = scene.compositions.filter(com => com.id !== compositionId);
|
|
1044
|
+
scene.components = scene.components.filter(com => com.id !== compositionComponent.id);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* @description 重置元素ID - 加载时会新建guid,需要保持一致
|
|
1049
|
+
* @param scene 场景数据
|
|
1050
|
+
* @param idMap id名称列表
|
|
1051
|
+
*/
|
|
1052
|
+
export function resetSubCompositionItemId(scene, idMap) {
|
|
1053
|
+
scene.items.forEach(item => {
|
|
1054
|
+
const originId = item.id;
|
|
1055
|
+
const newId = idMap.get(originId);
|
|
1056
|
+
if (newId) {
|
|
1057
|
+
item.id = newId;
|
|
1058
|
+
const components = scene.components.filter(c => c.item.id === originId);
|
|
1059
|
+
components.forEach(com => {
|
|
1060
|
+
com.item.id = newId;
|
|
1061
|
+
});
|
|
1062
|
+
const compositionComponents = scene.components.filter(comp => comp.dataType === spec.DataType.CompositionComponent);
|
|
1063
|
+
compositionComponents.forEach(com => {
|
|
1064
|
+
com.items.forEach(comItem => {
|
|
1065
|
+
if (comItem.id === originId) {
|
|
1066
|
+
comItem.id = newId;
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
com.sceneBindings.forEach(bindingValue => {
|
|
1070
|
+
if (bindingValue.value.id === originId) {
|
|
1071
|
+
bindingValue.value.id = newId;
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* @description 设置场景中元素名称
|
|
1080
|
+
* @param scene 场景数据
|
|
1081
|
+
* @param itemId 元素ID
|
|
1082
|
+
* @param name 名称
|
|
1083
|
+
*/
|
|
1084
|
+
export function setJSONItemName(scene, itemId, name) {
|
|
1085
|
+
const item = scene.items.find(item => item.id === itemId);
|
|
1086
|
+
if (!item) {
|
|
1087
|
+
console.warn(`item ${itemId} is not existed.`);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
item.name = name;
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* @description 将元素从一个合成移动到另一个合成
|
|
1094
|
+
* @param scene 场景数据
|
|
1095
|
+
* @param itemId 元素ID
|
|
1096
|
+
* @param targetCompositionId 目标合成ID,为null时表示移动到主合成
|
|
1097
|
+
* @param options 可选的位置、大小、缩放、旋转信息
|
|
1098
|
+
* @returns 是否移动成功
|
|
1099
|
+
*/
|
|
1100
|
+
export function moveItemBetweenCompositions(scene, itemId, targetCompositionId, options) {
|
|
1101
|
+
// 1. 查找元素
|
|
1102
|
+
const item = scene.items.find(item => item.id === itemId);
|
|
1103
|
+
if (!item) {
|
|
1104
|
+
console.warn(`moveItemBetweenCompositions: item ${itemId} not found`);
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
// 2. 查找元素当前所在的合成(源合成)
|
|
1108
|
+
const sourceCompositionComponent = findCompositionComponentByItemId(scene, itemId);
|
|
1109
|
+
if (!sourceCompositionComponent) {
|
|
1110
|
+
console.warn(`moveItemBetweenCompositions: source composition not found for item ${itemId}`);
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
// 3. 查找目标合成
|
|
1114
|
+
const targetCompositionComponent = targetCompositionId
|
|
1115
|
+
? findCompositionComponentByCompositionId(scene, targetCompositionId)
|
|
1116
|
+
: getMainCompositionComponent(scene);
|
|
1117
|
+
if (!targetCompositionComponent) {
|
|
1118
|
+
console.warn(`moveItemBetweenCompositions: target composition not found ${targetCompositionId ?? '(main)'}`);
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
// 源合成和目标合成相同,无需移动
|
|
1122
|
+
if (sourceCompositionComponent.id === targetCompositionComponent.id) {
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
// 4. 从源合成中移除元素的绑定关系
|
|
1126
|
+
const removedBindingInfo = removeItemBindingFromComposition(scene, sourceCompositionComponent, itemId);
|
|
1127
|
+
if (!removedBindingInfo) {
|
|
1128
|
+
console.warn(`moveItemBetweenCompositions: failed to remove item ${itemId} from source composition`);
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
// 5. 将元素添加到目标合成
|
|
1132
|
+
addItemBindingToComposition(scene, targetCompositionComponent, itemId, removedBindingInfo.trackIds);
|
|
1133
|
+
// 6. 更新元素的变换属性
|
|
1134
|
+
if (options) {
|
|
1135
|
+
updateItemTransform(item, options);
|
|
1136
|
+
}
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* @description 根据元素ID查找包含该合成的CompositionComponent
|
|
1141
|
+
*/
|
|
1142
|
+
function findCompositionComponentByItemId(scene, itemId) {
|
|
1143
|
+
if ((+scene.version) >= 3.3) {
|
|
1144
|
+
return scene.components.find(comp => comp.dataType === spec.DataType.CompositionComponent &&
|
|
1145
|
+
comp.items.some(item => item.id === itemId));
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
// V3.3 以下版本,遍历 compositions 查找
|
|
1149
|
+
const targetComposition = scene.compositions.find((c) => c.items?.some((item) => item.id === itemId));
|
|
1150
|
+
if (targetComposition) {
|
|
1151
|
+
return scene.components.find(comp => comp.dataType === spec.DataType.CompositionComponent &&
|
|
1152
|
+
comp.item.id === targetComposition.id);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return undefined;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* @description 根据合成ID查找CompositionComponent
|
|
1159
|
+
*/
|
|
1160
|
+
function findCompositionComponentByCompositionId(scene, compositionId) {
|
|
1161
|
+
return scene.components.find(comp => comp.dataType === spec.DataType.CompositionComponent &&
|
|
1162
|
+
comp.item.id === compositionId);
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* @description 获取主合成组件
|
|
1166
|
+
*/
|
|
1167
|
+
function getMainCompositionComponent(scene) {
|
|
1168
|
+
const mainComposition = scene.compositions.find(c => c.id === scene.compositionId) ?? scene.compositions[0];
|
|
1169
|
+
if (!mainComposition) {
|
|
1170
|
+
return undefined;
|
|
1171
|
+
}
|
|
1172
|
+
return scene.components.find(comp => comp.dataType === spec.DataType.CompositionComponent &&
|
|
1173
|
+
comp.item.id === mainComposition.id);
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* @description 从合成中移除元素的绑定关系
|
|
1177
|
+
* @returns 移除的轨道ID列表,用于后续添加到新合成
|
|
1178
|
+
*/
|
|
1179
|
+
function removeItemBindingFromComposition(scene, compositionComponent, itemId) {
|
|
1180
|
+
// 在 V3.3+ 中,从 CompositionComponent 中移除
|
|
1181
|
+
// 在 V3.3- 中,从 CompositionData 中移除
|
|
1182
|
+
const trackIds = [];
|
|
1183
|
+
let objectBindingTrackId = null;
|
|
1184
|
+
if ((+scene.version) >= 3.3) {
|
|
1185
|
+
// 1. 从 items 中移除
|
|
1186
|
+
compositionComponent.items = compositionComponent.items.filter(item => item.id !== itemId);
|
|
1187
|
+
// 2. 从 sceneBindings 中找到并移除绑定关系
|
|
1188
|
+
const bindingIndex = compositionComponent.sceneBindings.findIndex(binding => binding.value.id === itemId);
|
|
1189
|
+
if (bindingIndex >= 0) {
|
|
1190
|
+
const binding = compositionComponent.sceneBindings[bindingIndex];
|
|
1191
|
+
objectBindingTrackId = binding.key.id;
|
|
1192
|
+
compositionComponent.sceneBindings.splice(bindingIndex, 1);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
// V3.3 以下版本
|
|
1197
|
+
const composition = scene.compositions.find((c) => {
|
|
1198
|
+
const compComponent = scene.components.find(comp => comp.dataType === spec.DataType.CompositionComponent &&
|
|
1199
|
+
comp.id === compositionComponent.id);
|
|
1200
|
+
return compComponent && c.id === compComponent.item.id;
|
|
1201
|
+
});
|
|
1202
|
+
if (composition) {
|
|
1203
|
+
composition.items = composition.items.filter((item) => item.id !== itemId);
|
|
1204
|
+
const bindingIndex = composition.sceneBindings?.findIndex((binding) => binding.value.id === itemId);
|
|
1205
|
+
if (bindingIndex >= 0) {
|
|
1206
|
+
const binding = composition.sceneBindings[bindingIndex];
|
|
1207
|
+
objectBindingTrackId = binding.key.id;
|
|
1208
|
+
composition.sceneBindings.splice(bindingIndex, 1);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
if (!objectBindingTrackId) {
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
// 3. 从 TimelineAsset 中移除 ObjectBindingTrack
|
|
1216
|
+
const timelineAssetId = compositionComponent.timelineAsset.id;
|
|
1217
|
+
const timelineAsset = scene.miscs.find(m => m.id === timelineAssetId);
|
|
1218
|
+
if (timelineAsset?.tracks) {
|
|
1219
|
+
timelineAsset.tracks = timelineAsset.tracks.filter((track) => track.id !== objectBindingTrackId);
|
|
1220
|
+
}
|
|
1221
|
+
// 4. 获取 ObjectBindingTrack 的子轨道ID列表
|
|
1222
|
+
const objectBindingTrack = scene.miscs.find(m => m.id === objectBindingTrackId);
|
|
1223
|
+
if (objectBindingTrack?.children) {
|
|
1224
|
+
objectBindingTrack.children.forEach((child) => {
|
|
1225
|
+
trackIds.push(child.id);
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
return { trackIds, objectBindingTrackId };
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* @description 将元素绑定添加到目标合成
|
|
1232
|
+
*/
|
|
1233
|
+
function addItemBindingToComposition(scene, compositionComponent, itemId, trackIds) {
|
|
1234
|
+
// 1. 添加到 items
|
|
1235
|
+
compositionComponent.items.push({ id: itemId });
|
|
1236
|
+
// 2. 创建新的 ObjectBindingTrack
|
|
1237
|
+
const bindingTrackData = {
|
|
1238
|
+
id: generateGUID(),
|
|
1239
|
+
dataType: spec.DataType.ObjectBindingTrack,
|
|
1240
|
+
children: [],
|
|
1241
|
+
clips: [],
|
|
1242
|
+
};
|
|
1243
|
+
// 添加子轨道引用
|
|
1244
|
+
for (const trackId of trackIds) {
|
|
1245
|
+
bindingTrackData.children.push({ id: trackId });
|
|
1246
|
+
}
|
|
1247
|
+
// 3. 添加到 sceneBindings
|
|
1248
|
+
compositionComponent.sceneBindings.push({
|
|
1249
|
+
key: { id: bindingTrackData.id },
|
|
1250
|
+
value: { id: itemId },
|
|
1251
|
+
});
|
|
1252
|
+
// 4. 添加到 TimelineAsset 的 tracks
|
|
1253
|
+
const timelineAssetId = compositionComponent.timelineAsset.id;
|
|
1254
|
+
const timelineAsset = scene.miscs.find(m => m.id === timelineAssetId);
|
|
1255
|
+
if (timelineAsset?.tracks) {
|
|
1256
|
+
timelineAsset.tracks.push({ id: bindingTrackData.id });
|
|
1257
|
+
}
|
|
1258
|
+
// 5. 添加 ObjectBindingTrack 到 miscs
|
|
1259
|
+
scene.miscs.push(bindingTrackData);
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* @description 更新元素的变换属性
|
|
1263
|
+
*/
|
|
1264
|
+
function updateItemTransform(item, options) {
|
|
1265
|
+
item.transform ??= {
|
|
1266
|
+
position: { x: 0, y: 0, z: 0 },
|
|
1267
|
+
eulerHint: { x: 0, y: 0, z: 0 },
|
|
1268
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
1269
|
+
size: { x: 1, y: 1 },
|
|
1270
|
+
};
|
|
1271
|
+
if (options.position) {
|
|
1272
|
+
item.transform.position = { ...options.position };
|
|
1273
|
+
}
|
|
1274
|
+
if (options.size) {
|
|
1275
|
+
item.transform.size = { ...options.size };
|
|
1276
|
+
}
|
|
1277
|
+
if (options.scale) {
|
|
1278
|
+
item.transform.scale = { ...options.scale };
|
|
1279
|
+
}
|
|
1280
|
+
if (options.rotation) {
|
|
1281
|
+
item.transform.eulerHint = { ...options.rotation };
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
export function getCompositionIdByRefCompositionItemId(scene, compositionItemId) {
|
|
1285
|
+
const compositionItem = scene.items.find(item => item.id === compositionItemId);
|
|
1286
|
+
if (!compositionItem) {
|
|
1287
|
+
console.warn(`Target composition item ${compositionItemId} is not existed.`);
|
|
1288
|
+
return undefined;
|
|
1289
|
+
}
|
|
1290
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1291
|
+
return compositionItem.content.options.refId;
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* @description 获取预合成元素所指向的合成数据
|
|
1295
|
+
* @param scene 场景数据
|
|
1296
|
+
* @param compositionItemId 预合成元素ID
|
|
1297
|
+
* @returns 对应的合成数据 (CompositionData),如果未找到则返回 undefined
|
|
1298
|
+
*/
|
|
1299
|
+
export function getCompositionDataByRefCompositionItemId(scene, compositionItemId) {
|
|
1300
|
+
const compositionItem = scene.items.find(item => item.id === compositionItemId);
|
|
1301
|
+
if (!compositionItem) {
|
|
1302
|
+
console.warn(`Target composition item ${compositionItemId} is not existed.`);
|
|
1303
|
+
return undefined;
|
|
1304
|
+
}
|
|
1305
|
+
// 预合成元素通过 content.options.refId 存储引用的合成ID
|
|
1306
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1307
|
+
const refId = compositionItem.content?.options?.refId;
|
|
1308
|
+
if (!refId) {
|
|
1309
|
+
console.warn(`Composition item ${compositionItemId} has no refId.`);
|
|
1310
|
+
return undefined;
|
|
1311
|
+
}
|
|
1312
|
+
// 根据 refId 查找对应的合成数据
|
|
1313
|
+
const compositionData = scene.compositions.find(composition => composition.id === refId);
|
|
1314
|
+
if (!compositionData) {
|
|
1315
|
+
console.warn(`Composition data with id ${refId} not found.`);
|
|
1316
|
+
return undefined;
|
|
1317
|
+
}
|
|
1318
|
+
return compositionData;
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* @description 提取子合成元素及其所有关联资源为独立的 JSONScene
|
|
1322
|
+
* @param scene 主场景数据
|
|
1323
|
+
* @param compositionItemId 预合成元素ID(子合成元素ID)
|
|
1324
|
+
* @returns 独立的子合成场景 JSONScene,如果未找到则返回 undefined
|
|
1325
|
+
*/
|
|
1326
|
+
export function extractSubCompositionToScene(scene, compositionItemId) {
|
|
1327
|
+
// 1. 查找预合成元素
|
|
1328
|
+
const compositionItem = scene.items.find(item => item.id === compositionItemId);
|
|
1329
|
+
if (!compositionItem) {
|
|
1330
|
+
console.warn(`Target composition item ${compositionItemId} is not existed.`);
|
|
1331
|
+
return undefined;
|
|
1332
|
+
}
|
|
1333
|
+
// 验证元素类型是预合成类型
|
|
1334
|
+
if (compositionItem.type !== spec.ItemType.composition) {
|
|
1335
|
+
console.warn(`Item ${compositionItemId} is not a composition item.`);
|
|
1336
|
+
return undefined;
|
|
1337
|
+
}
|
|
1338
|
+
// 2. 获取子合成ID (refId)
|
|
1339
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1340
|
+
const refId = compositionItem.content?.options?.refId;
|
|
1341
|
+
if (!refId) {
|
|
1342
|
+
console.warn(`Composition item ${compositionItemId} has no refId.`);
|
|
1343
|
+
return undefined;
|
|
1344
|
+
}
|
|
1345
|
+
// 3. 提取合成数据
|
|
1346
|
+
const compositionData = scene.compositions.find(comp => comp.id === refId);
|
|
1347
|
+
if (!compositionData) {
|
|
1348
|
+
console.warn(`Composition data with id ${refId} not found.`);
|
|
1349
|
+
return undefined;
|
|
1350
|
+
}
|
|
1351
|
+
// 4. 查找对应的 CompositionComponent
|
|
1352
|
+
const compositionComponent = scene.components.find(comp => comp.dataType === spec.DataType.CompositionComponent && comp.item.id === refId);
|
|
1353
|
+
if (!compositionComponent) {
|
|
1354
|
+
console.warn(`CompositionComponent for composition ${refId} not found.`);
|
|
1355
|
+
return undefined;
|
|
1356
|
+
}
|
|
1357
|
+
// 收集所有需要的数据集合
|
|
1358
|
+
const itemIds = new Set();
|
|
1359
|
+
const componentIds = new Set();
|
|
1360
|
+
const miscIds = new Set();
|
|
1361
|
+
const textureIds = new Set();
|
|
1362
|
+
const imageIds = new Set();
|
|
1363
|
+
const videoIds = new Set();
|
|
1364
|
+
const materialIds = new Set();
|
|
1365
|
+
const shaderIds = new Set();
|
|
1366
|
+
const geometryIds = new Set();
|
|
1367
|
+
const animationIds = new Set();
|
|
1368
|
+
// 收集元素ID
|
|
1369
|
+
compositionComponent.items.forEach(itemRef => itemIds.add(itemRef.id));
|
|
1370
|
+
// 收集 CompositionComponent ID
|
|
1371
|
+
componentIds.add(compositionComponent.id);
|
|
1372
|
+
// 收集 TimelineAsset ID
|
|
1373
|
+
miscIds.add(compositionComponent.timelineAsset.id);
|
|
1374
|
+
// 5. 提取元素数据并收集关联资源
|
|
1375
|
+
const extractedItems = scene.items.filter(item => itemIds.has(item.id));
|
|
1376
|
+
// 6. 提取组件数据并收集资源引用
|
|
1377
|
+
const extractedComponents = scene.components.filter(comp => {
|
|
1378
|
+
if (comp.item.id === refId) {
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
if (itemIds.has(comp.item.id)) {
|
|
1382
|
+
return true;
|
|
1383
|
+
}
|
|
1384
|
+
return false;
|
|
1385
|
+
});
|
|
1386
|
+
// 收集 resources 引用
|
|
1387
|
+
extractedComponents.forEach(comp => {
|
|
1388
|
+
componentIds.add(comp.id);
|
|
1389
|
+
// 收集 SpriteComponent 的 texture 引用
|
|
1390
|
+
if (comp.dataType === spec.DataType.SpriteComponent) {
|
|
1391
|
+
const spriteComp = comp;
|
|
1392
|
+
if (spriteComp.renderer?.texture?.id) {
|
|
1393
|
+
textureIds.add(spriteComp.renderer.texture.id);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
// 收集 VideoComponent 的 texture 引用
|
|
1397
|
+
if (comp.dataType === spec.DataType.VideoComponent) {
|
|
1398
|
+
const videoComp = comp;
|
|
1399
|
+
if (videoComp.renderer?.texture?.id) {
|
|
1400
|
+
textureIds.add(videoComp.renderer.texture.id);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
// 7. 提取贴图和图片/视频资源
|
|
1405
|
+
const extractedTextures = [];
|
|
1406
|
+
if (scene.textures) {
|
|
1407
|
+
scene.textures.forEach(texture => {
|
|
1408
|
+
if (texture.id && textureIds.has(texture.id)) {
|
|
1409
|
+
extractedTextures.push(texture);
|
|
1410
|
+
// 收集图片或视频资源ID
|
|
1411
|
+
const sourceId = texture.source?.id;
|
|
1412
|
+
if (sourceId) {
|
|
1413
|
+
// 根据 texture 类型判断是图片还是视频
|
|
1414
|
+
const textureType = texture.sourceType;
|
|
1415
|
+
if (textureType === 1) { // Video source type
|
|
1416
|
+
videoIds.add(sourceId);
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
imageIds.add(sourceId);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
const extractedImages = scene.images.filter(img => imageIds.has(img.id));
|
|
1426
|
+
const extractedVideos = scene.videos?.filter(video => videoIds.has(video.id)) ?? [];
|
|
1427
|
+
// 8. 提取 miscs 数据 (TimelineAsset, tracks, playableAssets)
|
|
1428
|
+
const extractedMiscs = [];
|
|
1429
|
+
// 提取 TimelineAsset
|
|
1430
|
+
const timelineAsset = scene.miscs.find(m => m.id === compositionComponent.timelineAsset.id);
|
|
1431
|
+
if (timelineAsset) {
|
|
1432
|
+
extractedMiscs.push({ ...timelineAsset, tracks: [] }); // 先添加空的tracks,后面再填充
|
|
1433
|
+
}
|
|
1434
|
+
// 收集 sceneBindings 相关的 track IDs
|
|
1435
|
+
const bindingTrackIds = compositionComponent.sceneBindings.map(binding => binding.key.id);
|
|
1436
|
+
bindingTrackIds.forEach(id => miscIds.add(id));
|
|
1437
|
+
// 收集 item 引用的所有 tracks
|
|
1438
|
+
compositionComponent.sceneBindings.forEach(binding => {
|
|
1439
|
+
const objectTrackId = binding.key.id;
|
|
1440
|
+
const objectTrack = scene.miscs.find(m => m.id === objectTrackId);
|
|
1441
|
+
if (objectTrack) {
|
|
1442
|
+
// 提取 ObjectBindingTrack 本身
|
|
1443
|
+
extractedMiscs.push({ ...objectTrack });
|
|
1444
|
+
// 将 track 添加到 TimelineAsset
|
|
1445
|
+
if (timelineAsset) {
|
|
1446
|
+
const timelineIndex = extractedMiscs.findIndex(m => m.id === timelineAsset.id);
|
|
1447
|
+
if (timelineIndex >= 0) {
|
|
1448
|
+
extractedMiscs[timelineIndex].tracks = extractedMiscs[timelineIndex].tracks || [];
|
|
1449
|
+
extractedMiscs[timelineIndex].tracks.push({ id: objectTrackId });
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
// 提取子 tracks
|
|
1453
|
+
const childTrackIds = (objectTrack.children || []).map((child) => child.id);
|
|
1454
|
+
childTrackIds.forEach(trackId => {
|
|
1455
|
+
miscIds.add(trackId);
|
|
1456
|
+
const track = scene.miscs.find(m => m.id === trackId);
|
|
1457
|
+
if (track) {
|
|
1458
|
+
extractedMiscs.push({ ...track });
|
|
1459
|
+
// 从子 track 中提取 playableAssets
|
|
1460
|
+
const trackClips = track.clips;
|
|
1461
|
+
if (trackClips) {
|
|
1462
|
+
trackClips.forEach(clip => {
|
|
1463
|
+
if (clip.asset?.id) {
|
|
1464
|
+
miscIds.add(clip.asset.id);
|
|
1465
|
+
const asset = scene.miscs.find(m => m.id === clip.asset.id);
|
|
1466
|
+
if (asset) {
|
|
1467
|
+
extractedMiscs.push({ ...asset });
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
// 从 ObjectBindingTrack 中提取 playableAssets
|
|
1475
|
+
const objectTrackClips = objectTrack.clips;
|
|
1476
|
+
if (objectTrackClips) {
|
|
1477
|
+
objectTrackClips.forEach(clip => {
|
|
1478
|
+
if (clip.asset?.id) {
|
|
1479
|
+
miscIds.add(clip.asset.id);
|
|
1480
|
+
const asset = scene.miscs.find(m => m.id === clip.asset.id);
|
|
1481
|
+
if (asset) {
|
|
1482
|
+
extractedMiscs.push({ ...asset });
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
// 9. 按需提取其他资源 (shaders, materials, geometries, animations)
|
|
1490
|
+
// 这些需要根据实际使用情况进行收集,这里先保留空数组
|
|
1491
|
+
const extractedShaders = scene.shaders?.filter(s => shaderIds.has(s.id)) || [];
|
|
1492
|
+
const extractedMaterials = scene.materials?.filter(m => materialIds.has(m.id)) || [];
|
|
1493
|
+
const extractedGeometries = scene.geometries?.filter(g => geometryIds.has(g.id)) || [];
|
|
1494
|
+
const extractedAnimations = scene.animations?.filter(a => animationIds.has(a.id)) || [];
|
|
1495
|
+
// 10. 构建新的 JSONScene
|
|
1496
|
+
const newScene = {
|
|
1497
|
+
version: spec.JSONSceneVersion['3_6'],
|
|
1498
|
+
playerVersion: { ...scene.playerVersion },
|
|
1499
|
+
type: scene.type,
|
|
1500
|
+
compositionId: refId,
|
|
1501
|
+
compositions: [{ ...compositionData }],
|
|
1502
|
+
items: extractedItems.map(item => ({ ...item })),
|
|
1503
|
+
components: extractedComponents.map(comp => ({ ...comp })),
|
|
1504
|
+
miscs: extractedMiscs,
|
|
1505
|
+
images: extractedImages.map(img => ({ ...img })),
|
|
1506
|
+
textures: extractedTextures,
|
|
1507
|
+
videos: extractedVideos,
|
|
1508
|
+
shaders: extractedShaders,
|
|
1509
|
+
materials: extractedMaterials,
|
|
1510
|
+
geometries: extractedGeometries,
|
|
1511
|
+
animations: extractedAnimations,
|
|
1512
|
+
plugins: scene.plugins ? [...scene.plugins] : [],
|
|
1513
|
+
bins: scene.bins ? [...scene.bins] : [],
|
|
1514
|
+
};
|
|
1515
|
+
return newScene;
|
|
1516
|
+
}
|
|
1517
|
+
export function fixStandardJSON(scene) {
|
|
1518
|
+
const compositionComponents = scene.components.filter(component => component.dataType === spec.DataType.CompositionComponent);
|
|
1519
|
+
compositionComponents.forEach(compositionComponent => {
|
|
1520
|
+
if (!compositionComponent.items) {
|
|
1521
|
+
compositionComponent.items ??= [];
|
|
1522
|
+
const composition = scene.compositions.find(comp => comp.id === compositionComponent.item.id);
|
|
1523
|
+
if (composition && 'items' in composition && composition?.items) {
|
|
1524
|
+
compositionComponent.items.push(...composition.items);
|
|
1525
|
+
delete composition.items;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
}
|