@combos-fun/plugin-renderer-3d 0.0.42 → 0.0.44
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/agent-skill.md +66 -6
- package/combos-plugin.json +11 -2
- package/dist/plugin-renderer-3d.cjs.js +959 -9
- package/dist/plugin-renderer-3d.cjs.js.map +1 -1
- package/dist/plugin-renderer-3d.cjs.prod.js +1 -1
- package/dist/plugin-renderer-3d.d.ts +257 -5
- package/dist/plugin-renderer-3d.esm.js +943 -13
- package/dist/plugin-renderer-3d.esm.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var tslib = require('tslib');
|
|
3
4
|
var engine = require('@combos-fun/engine');
|
|
4
5
|
var three = require('three');
|
|
6
|
+
var inspectorDecorator = require('@combos-fun/inspector-decorator');
|
|
5
7
|
|
|
6
8
|
const COMBOS_GAME_OBJECT_ID = 'combosGameObjectId';
|
|
9
|
+
const COMBOS_INSTANCE_GAME_OBJECT_IDS = 'combosInstanceGameObjectIds';
|
|
10
|
+
const COMBOS_TRANSFORM3D = 'combosTransform3D';
|
|
11
|
+
function isTransform3DRoot(object) {
|
|
12
|
+
return !!object?.userData?.[COMBOS_TRANSFORM3D];
|
|
13
|
+
}
|
|
7
14
|
/** Stamp a Three.js object (and its children) so Event3D raycasts can resolve the GameObject. */
|
|
8
15
|
function tagObject3D(object, gameObjectId) {
|
|
9
16
|
object.traverse((child) => {
|
|
@@ -21,15 +28,130 @@ function gameObjectIdFromObject3D(object) {
|
|
|
21
28
|
}
|
|
22
29
|
return undefined;
|
|
23
30
|
}
|
|
31
|
+
/** Resolve a raycast hit, including InstancedMesh `instanceId` rows. */
|
|
32
|
+
function gameObjectIdFromIntersection(intersection) {
|
|
33
|
+
if (!intersection?.object)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (typeof intersection.instanceId === 'number') {
|
|
36
|
+
let current = intersection.object;
|
|
37
|
+
while (current) {
|
|
38
|
+
const ids = current.userData?.[COMBOS_INSTANCE_GAME_OBJECT_IDS];
|
|
39
|
+
if (Array.isArray(ids) && typeof ids[intersection.instanceId] === 'number') {
|
|
40
|
+
return ids[intersection.instanceId];
|
|
41
|
+
}
|
|
42
|
+
current = current.parent;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return gameObjectIdFromObject3D(intersection.object);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** True when this GameObject is a scene root (no 3D parent). */
|
|
49
|
+
function is3DSceneParent(gameObject) {
|
|
50
|
+
if (!gameObject)
|
|
51
|
+
return true;
|
|
52
|
+
const parent = gameObject.parent;
|
|
53
|
+
return !parent || parent === gameObject.scene;
|
|
54
|
+
}
|
|
55
|
+
function has3DChildGameObjects(gameObject) {
|
|
56
|
+
return (gameObject?.transform?.children?.length ?? 0) > 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readPose(pose) {
|
|
60
|
+
return {
|
|
61
|
+
positionX: pose.positionX ?? 0,
|
|
62
|
+
positionY: pose.positionY ?? 0,
|
|
63
|
+
positionZ: pose.positionZ ?? 0,
|
|
64
|
+
rotationX: pose.rotationX ?? 0,
|
|
65
|
+
rotationY: pose.rotationY ?? 0,
|
|
66
|
+
rotationZ: pose.rotationZ ?? 0,
|
|
67
|
+
scaleX: pose.scaleX ?? 1,
|
|
68
|
+
scaleY: pose.scaleY ?? 1,
|
|
69
|
+
scaleZ: pose.scaleZ ?? 1,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function poseKeyOf(pose) {
|
|
73
|
+
const r = readPose(pose);
|
|
74
|
+
return [
|
|
75
|
+
r.positionX, r.positionY, r.positionZ,
|
|
76
|
+
r.rotationX, r.rotationY, r.rotationZ,
|
|
77
|
+
r.scaleX, r.scaleY, r.scaleZ,
|
|
78
|
+
].join(',');
|
|
79
|
+
}
|
|
80
|
+
function copyPose(from, to) {
|
|
81
|
+
const r = readPose(from);
|
|
82
|
+
to.positionX = r.positionX;
|
|
83
|
+
to.positionY = r.positionY;
|
|
84
|
+
to.positionZ = r.positionZ;
|
|
85
|
+
to.rotationX = r.rotationX;
|
|
86
|
+
to.rotationY = r.rotationY;
|
|
87
|
+
to.rotationZ = r.rotationZ;
|
|
88
|
+
to.scaleX = r.scaleX;
|
|
89
|
+
to.scaleY = r.scaleY;
|
|
90
|
+
to.scaleZ = r.scaleZ;
|
|
91
|
+
}
|
|
92
|
+
function isIdentityPose(pose) {
|
|
93
|
+
const r = readPose(pose);
|
|
94
|
+
return r.positionX === 0 && r.positionY === 0 && r.positionZ === 0
|
|
95
|
+
&& r.rotationX === 0 && r.rotationY === 0 && r.rotationZ === 0
|
|
96
|
+
&& r.scaleX === 1 && r.scaleY === 1 && r.scaleZ === 1;
|
|
97
|
+
}
|
|
98
|
+
function seedTransform3D(visual, transform) {
|
|
99
|
+
if (isIdentityPose(transform) && !isIdentityPose(visual)) {
|
|
100
|
+
copyPose(visual, transform);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Keep a visual component's pose fields and a sibling Transform3D in sync.
|
|
105
|
+
* Returns true when the visual Object3D should still apply pose itself
|
|
106
|
+
* (no Transform3D root yet).
|
|
107
|
+
*/
|
|
108
|
+
class VisualPoseBridge {
|
|
109
|
+
constructor() {
|
|
110
|
+
this.lastVisual = '';
|
|
111
|
+
this.lastRoot = '';
|
|
112
|
+
}
|
|
113
|
+
sync(visual, root) {
|
|
114
|
+
if (!root)
|
|
115
|
+
return true;
|
|
116
|
+
if (this.lastVisual === '' && this.lastRoot === '') {
|
|
117
|
+
seedTransform3D(visual, root);
|
|
118
|
+
this.lastVisual = poseKeyOf(visual);
|
|
119
|
+
this.lastRoot = poseKeyOf(root);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const visualKey = poseKeyOf(visual);
|
|
123
|
+
const rootKey = poseKeyOf(root);
|
|
124
|
+
if (visualKey !== this.lastVisual && rootKey === this.lastRoot) {
|
|
125
|
+
copyPose(visual, root);
|
|
126
|
+
}
|
|
127
|
+
this.lastVisual = poseKeyOf(visual);
|
|
128
|
+
this.lastRoot = poseKeyOf(root);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
24
132
|
|
|
133
|
+
const TONE_MAPPING = {
|
|
134
|
+
none: three.NoToneMapping,
|
|
135
|
+
aces: three.ACESFilmicToneMapping,
|
|
136
|
+
linear: three.LinearToneMapping,
|
|
137
|
+
reinhard: three.ReinhardToneMapping,
|
|
138
|
+
cineon: three.CineonToneMapping,
|
|
139
|
+
};
|
|
140
|
+
const _world = new three.Vector3();
|
|
141
|
+
const _euler = new three.Euler();
|
|
25
142
|
class ThreeContext {
|
|
26
143
|
constructor(params) {
|
|
27
144
|
this.mixers = new Map();
|
|
28
145
|
this.models = new Map();
|
|
146
|
+
/** Root Object3D for each GameObject that has a 3D node. */
|
|
147
|
+
this.nodes = new Map();
|
|
148
|
+
this.defaultLights = [];
|
|
29
149
|
const { canvas, container, width, height, antialias = true, backgroundColor = 0x000000, backgroundAlpha = 1, } = params;
|
|
30
150
|
this.scene = new three.Scene();
|
|
151
|
+
this.shadows = !!params.shadows;
|
|
31
152
|
this.camera = new three.PerspectiveCamera(75, width / height, 0.1, 1000);
|
|
32
153
|
this.camera.position.z = 5;
|
|
154
|
+
this.fallbackCamera = this.camera;
|
|
33
155
|
const rendererOpts = { antialias };
|
|
34
156
|
if (canvas) {
|
|
35
157
|
rendererOpts.canvas = canvas;
|
|
@@ -41,15 +163,36 @@ class ThreeContext {
|
|
|
41
163
|
this.renderer.setSize(width, height);
|
|
42
164
|
this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
43
165
|
this.renderer.setClearColor(backgroundColor, backgroundAlpha);
|
|
44
|
-
|
|
166
|
+
this.renderer.toneMapping = TONE_MAPPING[params.toneMapping ?? 'aces'] ?? three.ACESFilmicToneMapping;
|
|
167
|
+
this.renderer.toneMappingExposure = params.toneMappingExposure ?? 1;
|
|
168
|
+
if (this.shadows) {
|
|
169
|
+
this.renderer.shadowMap.enabled = true;
|
|
170
|
+
this.renderer.shadowMap.type = three.PCFSoftShadowMap;
|
|
171
|
+
}
|
|
45
172
|
if (!canvas && container) {
|
|
46
173
|
container.appendChild(this.renderer.domElement);
|
|
47
174
|
}
|
|
48
|
-
const ambientLight = new three.AmbientLight(0xffffff, 0.6);
|
|
175
|
+
const ambientLight = new three.AmbientLight(0xffffff, params.ambientIntensity ?? 0.6);
|
|
49
176
|
this.scene.add(ambientLight);
|
|
50
|
-
|
|
177
|
+
this.defaultLights.push(ambientLight);
|
|
178
|
+
const directionalLight = new three.DirectionalLight(0xffffff, params.directionalIntensity ?? 0.8);
|
|
51
179
|
directionalLight.position.set(5, 10, 7.5);
|
|
180
|
+
if (this.shadows) {
|
|
181
|
+
directionalLight.castShadow = true;
|
|
182
|
+
directionalLight.shadow.mapSize.set(1024, 1024);
|
|
183
|
+
}
|
|
52
184
|
this.scene.add(directionalLight);
|
|
185
|
+
this.defaultLights.push(directionalLight);
|
|
186
|
+
if (params.hemisphereLight !== false) {
|
|
187
|
+
const hemi = typeof params.hemisphereLight === 'object' ? params.hemisphereLight : {};
|
|
188
|
+
const hemisphereLight = new three.HemisphereLight(hemi.skyColor ?? 0xffffff, hemi.groundColor ?? 0x444444, hemi.intensity ?? 0.35);
|
|
189
|
+
this.scene.add(hemisphereLight);
|
|
190
|
+
this.defaultLights.push(hemisphereLight);
|
|
191
|
+
}
|
|
192
|
+
if (params.fog) {
|
|
193
|
+
const fog = typeof params.fog === 'object' ? params.fog : {};
|
|
194
|
+
this.scene.fog = new three.Fog(fog.color ?? backgroundColor, fog.near ?? 8, fog.far ?? 40);
|
|
195
|
+
}
|
|
53
196
|
this.clock = new three.Clock();
|
|
54
197
|
}
|
|
55
198
|
update() {
|
|
@@ -59,10 +202,25 @@ class ThreeContext {
|
|
|
59
202
|
}
|
|
60
203
|
this.renderer.render(this.scene, this.camera);
|
|
61
204
|
}
|
|
62
|
-
|
|
205
|
+
hasTransform3DRoot(id) {
|
|
206
|
+
return isTransform3DRoot(this.nodes.get(id));
|
|
207
|
+
}
|
|
208
|
+
applyModelShadows(object) {
|
|
209
|
+
if (!this.shadows)
|
|
210
|
+
return;
|
|
211
|
+
object.traverse((child) => {
|
|
212
|
+
const mesh = child;
|
|
213
|
+
if (mesh.isMesh) {
|
|
214
|
+
mesh.castShadow = true;
|
|
215
|
+
mesh.receiveShadow = true;
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
attachParsedModel(id, model, animations, gameObject) {
|
|
63
220
|
tagObject3D(model, id);
|
|
64
|
-
this.
|
|
221
|
+
this.applyModelShadows(model);
|
|
65
222
|
this.models.set(id, model);
|
|
223
|
+
this.attachVisual(id, model, gameObject);
|
|
66
224
|
if (animations && animations.length > 0) {
|
|
67
225
|
const mixer = new three.AnimationMixer(model);
|
|
68
226
|
this.mixers.set(id, mixer);
|
|
@@ -70,6 +228,185 @@ class ThreeContext {
|
|
|
70
228
|
}
|
|
71
229
|
return model;
|
|
72
230
|
}
|
|
231
|
+
object3DFor(id) {
|
|
232
|
+
return this.nodes.get(id);
|
|
233
|
+
}
|
|
234
|
+
/** Place a GameObject's root Object3D under the nearest ancestor visual (or the scene). */
|
|
235
|
+
attachObject3D(id, object, gameObject) {
|
|
236
|
+
const prev = this.nodes.get(id);
|
|
237
|
+
if (prev && prev !== object) {
|
|
238
|
+
this.rehomeRegisteredChildren(prev);
|
|
239
|
+
prev.parent?.remove(prev);
|
|
240
|
+
}
|
|
241
|
+
this.nodes.set(id, object);
|
|
242
|
+
const parentNode = this.findParentNode(gameObject);
|
|
243
|
+
if (object.parent !== parentNode) {
|
|
244
|
+
parentNode.add(object);
|
|
245
|
+
}
|
|
246
|
+
if (gameObject) {
|
|
247
|
+
this.adoptChildGameObjects(gameObject, object);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Attach a visual / camera / light under the Transform3D root when one exists.
|
|
252
|
+
* Otherwise the object itself becomes the GameObject root (legacy path).
|
|
253
|
+
*/
|
|
254
|
+
attachVisual(id, object, gameObject) {
|
|
255
|
+
tagObject3D(object, id);
|
|
256
|
+
const root = this.nodes.get(id);
|
|
257
|
+
if (isTransform3DRoot(root) && root !== object) {
|
|
258
|
+
if (object.parent !== root) {
|
|
259
|
+
root.add(object);
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.attachObject3D(id, object, gameObject);
|
|
264
|
+
}
|
|
265
|
+
detachVisual(id, object) {
|
|
266
|
+
const root = this.nodes.get(id);
|
|
267
|
+
if (isTransform3DRoot(root) && root !== object) {
|
|
268
|
+
object.parent?.remove(object);
|
|
269
|
+
return object;
|
|
270
|
+
}
|
|
271
|
+
if (root === object) {
|
|
272
|
+
return this.detachObject3D(id);
|
|
273
|
+
}
|
|
274
|
+
object.parent?.remove(object);
|
|
275
|
+
return object;
|
|
276
|
+
}
|
|
277
|
+
/** Remove a registered node without disposing it; child GameObject nodes stay in the scene. */
|
|
278
|
+
detachObject3D(id) {
|
|
279
|
+
const object = this.nodes.get(id);
|
|
280
|
+
if (!object)
|
|
281
|
+
return undefined;
|
|
282
|
+
this.rehomeRegisteredChildren(object);
|
|
283
|
+
object.parent?.remove(object);
|
|
284
|
+
this.nodes.delete(id);
|
|
285
|
+
return object;
|
|
286
|
+
}
|
|
287
|
+
reparentGameObject(gameObject) {
|
|
288
|
+
const object = this.nodes.get(gameObject.id);
|
|
289
|
+
if (!object)
|
|
290
|
+
return;
|
|
291
|
+
const parentNode = this.findParentNode(gameObject);
|
|
292
|
+
if (object.parent === parentNode)
|
|
293
|
+
return;
|
|
294
|
+
parentNode.add(object);
|
|
295
|
+
}
|
|
296
|
+
findParentNode(gameObject) {
|
|
297
|
+
if (!gameObject || is3DSceneParent(gameObject)) {
|
|
298
|
+
return this.scene;
|
|
299
|
+
}
|
|
300
|
+
let current = gameObject.parent;
|
|
301
|
+
while (current && current !== gameObject.scene) {
|
|
302
|
+
const node = this.nodes.get(current.id);
|
|
303
|
+
if (node)
|
|
304
|
+
return node;
|
|
305
|
+
current = current.parent;
|
|
306
|
+
}
|
|
307
|
+
return this.scene;
|
|
308
|
+
}
|
|
309
|
+
adoptChildGameObjects(gameObject, object) {
|
|
310
|
+
const children = gameObject.transform?.children;
|
|
311
|
+
if (!children)
|
|
312
|
+
return;
|
|
313
|
+
for (const childTransform of children) {
|
|
314
|
+
const childGo = childTransform.gameObject;
|
|
315
|
+
if (!childGo)
|
|
316
|
+
continue;
|
|
317
|
+
const childNode = this.nodes.get(childGo.id);
|
|
318
|
+
if (childNode && childNode.parent !== object) {
|
|
319
|
+
object.add(childNode);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
ensureTransformRoot(gameObject, transform) {
|
|
324
|
+
const existing = this.nodes.get(gameObject.id);
|
|
325
|
+
if (isTransform3DRoot(existing)) {
|
|
326
|
+
this.applyTransformPose(existing, transform);
|
|
327
|
+
return existing;
|
|
328
|
+
}
|
|
329
|
+
const group = new three.Group();
|
|
330
|
+
group.name = gameObject.name;
|
|
331
|
+
group.userData[COMBOS_TRANSFORM3D] = true;
|
|
332
|
+
tagObject3D(group, gameObject.id);
|
|
333
|
+
if (existing && existing !== group) {
|
|
334
|
+
if (isIdentityPoseObject(transform)) {
|
|
335
|
+
transform.positionX = existing.position.x;
|
|
336
|
+
transform.positionY = existing.position.y;
|
|
337
|
+
transform.positionZ = existing.position.z;
|
|
338
|
+
transform.rotationX = existing.rotation.x;
|
|
339
|
+
transform.rotationY = existing.rotation.y;
|
|
340
|
+
transform.rotationZ = existing.rotation.z;
|
|
341
|
+
transform.scaleX = existing.scale.x;
|
|
342
|
+
transform.scaleY = existing.scale.y;
|
|
343
|
+
transform.scaleZ = existing.scale.z;
|
|
344
|
+
}
|
|
345
|
+
const parent = existing.parent || this.scene;
|
|
346
|
+
parent.add(group);
|
|
347
|
+
group.add(existing);
|
|
348
|
+
existing.position.set(0, 0, 0);
|
|
349
|
+
existing.rotation.set(0, 0, 0);
|
|
350
|
+
existing.scale.set(1, 1, 1);
|
|
351
|
+
this.nodes.set(gameObject.id, group);
|
|
352
|
+
this.adoptChildGameObjects(gameObject, group);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
this.attachObject3D(gameObject.id, group, gameObject);
|
|
356
|
+
}
|
|
357
|
+
this.applyTransformPose(group, transform);
|
|
358
|
+
return group;
|
|
359
|
+
}
|
|
360
|
+
applyTransform3D(gameObject, transform) {
|
|
361
|
+
const root = this.ensureTransformRoot(gameObject, transform);
|
|
362
|
+
this.applyTransformPose(root, transform);
|
|
363
|
+
root.updateWorldMatrix(true, false);
|
|
364
|
+
root.getWorldPosition(_world);
|
|
365
|
+
transform.worldPositionX = _world.x;
|
|
366
|
+
transform.worldPositionY = _world.y;
|
|
367
|
+
transform.worldPositionZ = _world.z;
|
|
368
|
+
_euler.setFromRotationMatrix(root.matrixWorld);
|
|
369
|
+
transform.worldRotationX = _euler.x;
|
|
370
|
+
transform.worldRotationY = _euler.y;
|
|
371
|
+
transform.worldRotationZ = _euler.z;
|
|
372
|
+
return root;
|
|
373
|
+
}
|
|
374
|
+
applyTransformPose(root, transform) {
|
|
375
|
+
const pose = readPose(transform);
|
|
376
|
+
root.position.set(pose.positionX, pose.positionY, pose.positionZ);
|
|
377
|
+
root.rotation.set(pose.rotationX, pose.rotationY, pose.rotationZ);
|
|
378
|
+
root.scale.set(pose.scaleX, pose.scaleY, pose.scaleZ);
|
|
379
|
+
}
|
|
380
|
+
removeDefaultLights() {
|
|
381
|
+
if (!this.defaultLights.length)
|
|
382
|
+
return;
|
|
383
|
+
for (const light of this.defaultLights) {
|
|
384
|
+
light.parent?.remove(light);
|
|
385
|
+
light.dispose?.();
|
|
386
|
+
}
|
|
387
|
+
this.defaultLights = [];
|
|
388
|
+
}
|
|
389
|
+
setActiveCamera(camera) {
|
|
390
|
+
const prev = this.camera;
|
|
391
|
+
this.camera = camera;
|
|
392
|
+
if (prev) {
|
|
393
|
+
camera.aspect = prev.aspect;
|
|
394
|
+
}
|
|
395
|
+
camera.updateProjectionMatrix();
|
|
396
|
+
}
|
|
397
|
+
restoreFallbackCamera() {
|
|
398
|
+
if (this.camera !== this.fallbackCamera) {
|
|
399
|
+
this.setActiveCamera(this.fallbackCamera);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
rehomeRegisteredChildren(object) {
|
|
403
|
+
for (const child of [...object.children]) {
|
|
404
|
+
const childId = child.userData?.[COMBOS_GAME_OBJECT_ID];
|
|
405
|
+
if (typeof childId === 'number' && this.nodes.get(childId) === child) {
|
|
406
|
+
this.scene.add(child);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
73
410
|
playAnimation(id, animationIndex, speed) {
|
|
74
411
|
const mixer = this.mixers.get(id);
|
|
75
412
|
const model = this.models.get(id);
|
|
@@ -91,7 +428,7 @@ class ThreeContext {
|
|
|
91
428
|
removeModel(id) {
|
|
92
429
|
const model = this.models.get(id);
|
|
93
430
|
if (model) {
|
|
94
|
-
this.
|
|
431
|
+
this.detachVisual(id, model);
|
|
95
432
|
model.traverse((child) => {
|
|
96
433
|
const mesh = child;
|
|
97
434
|
if (mesh.geometry) {
|
|
@@ -115,20 +452,33 @@ class ThreeContext {
|
|
|
115
452
|
resize(width, height) {
|
|
116
453
|
this.camera.aspect = width / height;
|
|
117
454
|
this.camera.updateProjectionMatrix();
|
|
455
|
+
if (this.fallbackCamera !== this.camera) {
|
|
456
|
+
this.fallbackCamera.aspect = width / height;
|
|
457
|
+
this.fallbackCamera.updateProjectionMatrix();
|
|
458
|
+
}
|
|
118
459
|
this.renderer.setSize(width, height);
|
|
119
460
|
}
|
|
120
461
|
destroy() {
|
|
121
462
|
for (const id of this.models.keys()) {
|
|
122
463
|
this.removeModel(id);
|
|
123
464
|
}
|
|
465
|
+
this.removeDefaultLights();
|
|
124
466
|
this.renderer.dispose();
|
|
125
467
|
this.renderer.domElement.remove();
|
|
468
|
+
this.nodes.clear();
|
|
126
469
|
this.scene = null;
|
|
127
470
|
this.camera = null;
|
|
471
|
+
this.fallbackCamera = null;
|
|
128
472
|
this.renderer = null;
|
|
129
473
|
this.clock = null;
|
|
130
474
|
}
|
|
131
475
|
}
|
|
476
|
+
function isIdentityPoseObject(transform) {
|
|
477
|
+
const pose = readPose(transform);
|
|
478
|
+
return pose.positionX === 0 && pose.positionY === 0 && pose.positionZ === 0
|
|
479
|
+
&& pose.rotationX === 0 && pose.rotationY === 0 && pose.rotationZ === 0
|
|
480
|
+
&& pose.scaleX === 1 && pose.scaleY === 1 && pose.scaleZ === 1;
|
|
481
|
+
}
|
|
132
482
|
|
|
133
483
|
class Renderer3DManager {
|
|
134
484
|
constructor({ game, rendererSystem }) {
|
|
@@ -195,7 +545,60 @@ class Renderer3DManager {
|
|
|
195
545
|
}
|
|
196
546
|
}
|
|
197
547
|
|
|
198
|
-
class
|
|
548
|
+
class Transform3D extends engine.Component {
|
|
549
|
+
constructor() {
|
|
550
|
+
super(...arguments);
|
|
551
|
+
this.positionX = 0;
|
|
552
|
+
this.positionY = 0;
|
|
553
|
+
this.positionZ = 0;
|
|
554
|
+
this.rotationX = 0;
|
|
555
|
+
this.rotationY = 0;
|
|
556
|
+
this.rotationZ = 0;
|
|
557
|
+
this.scaleX = 1;
|
|
558
|
+
this.scaleY = 1;
|
|
559
|
+
this.scaleZ = 1;
|
|
560
|
+
this.worldPositionX = 0;
|
|
561
|
+
this.worldPositionY = 0;
|
|
562
|
+
this.worldPositionZ = 0;
|
|
563
|
+
this.worldRotationX = 0;
|
|
564
|
+
this.worldRotationY = 0;
|
|
565
|
+
this.worldRotationZ = 0;
|
|
566
|
+
}
|
|
567
|
+
static { this.componentName = 'Transform3D'; }
|
|
568
|
+
init(obj) {
|
|
569
|
+
if (obj)
|
|
570
|
+
Object.assign(this, obj);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
tslib.__decorate([
|
|
574
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'positionX', editor: 'number-stepper' })
|
|
575
|
+
], Transform3D.prototype, "positionX", void 0);
|
|
576
|
+
tslib.__decorate([
|
|
577
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'positionY', editor: 'number-stepper' })
|
|
578
|
+
], Transform3D.prototype, "positionY", void 0);
|
|
579
|
+
tslib.__decorate([
|
|
580
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'positionZ', editor: 'number-stepper' })
|
|
581
|
+
], Transform3D.prototype, "positionZ", void 0);
|
|
582
|
+
tslib.__decorate([
|
|
583
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Transform3D', label: 'rotationX', editor: 'number-stepper' })
|
|
584
|
+
], Transform3D.prototype, "rotationX", void 0);
|
|
585
|
+
tslib.__decorate([
|
|
586
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Transform3D', label: 'rotationY', editor: 'number-stepper' })
|
|
587
|
+
], Transform3D.prototype, "rotationY", void 0);
|
|
588
|
+
tslib.__decorate([
|
|
589
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Transform3D', label: 'rotationZ', editor: 'number-stepper' })
|
|
590
|
+
], Transform3D.prototype, "rotationZ", void 0);
|
|
591
|
+
tslib.__decorate([
|
|
592
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'scaleX', editor: 'number-stepper' })
|
|
593
|
+
], Transform3D.prototype, "scaleX", void 0);
|
|
594
|
+
tslib.__decorate([
|
|
595
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'scaleY', editor: 'number-stepper' })
|
|
596
|
+
], Transform3D.prototype, "scaleY", void 0);
|
|
597
|
+
tslib.__decorate([
|
|
598
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Transform3D', label: 'scaleZ', editor: 'number-stepper' })
|
|
599
|
+
], Transform3D.prototype, "scaleZ", void 0);
|
|
600
|
+
|
|
601
|
+
let Renderer3DSystem$1 = class Renderer3DSystem extends engine.System {
|
|
199
602
|
constructor() {
|
|
200
603
|
super(...arguments);
|
|
201
604
|
this.name = 'Renderer3DSystem';
|
|
@@ -213,6 +616,13 @@ class Renderer3DSystem extends engine.System {
|
|
|
213
616
|
antialias: opts.antialias,
|
|
214
617
|
backgroundColor: opts.backgroundColor,
|
|
215
618
|
backgroundAlpha: opts.backgroundAlpha,
|
|
619
|
+
fog: opts.fog,
|
|
620
|
+
hemisphereLight: opts.hemisphereLight,
|
|
621
|
+
shadows: opts.shadows,
|
|
622
|
+
toneMapping: opts.toneMapping,
|
|
623
|
+
toneMappingExposure: opts.toneMappingExposure,
|
|
624
|
+
ambientIntensity: opts.ambientIntensity,
|
|
625
|
+
directionalIntensity: opts.directionalIntensity,
|
|
216
626
|
});
|
|
217
627
|
this.game.canvas = this.threeContext.renderer.domElement;
|
|
218
628
|
this.rendererManager = new Renderer3DManager({
|
|
@@ -221,10 +631,38 @@ class Renderer3DSystem extends engine.System {
|
|
|
221
631
|
});
|
|
222
632
|
}
|
|
223
633
|
update(e) {
|
|
634
|
+
const changes = this.componentObserver.clear();
|
|
635
|
+
for (const changed of changes) {
|
|
636
|
+
this.handleHierarchyChange(changed);
|
|
637
|
+
}
|
|
224
638
|
for (const gameObject of this.game.gameObjects) {
|
|
639
|
+
const transform = gameObject.getComponent(Transform3D);
|
|
640
|
+
if (transform) {
|
|
641
|
+
this.threeContext.applyTransform3D(gameObject, transform);
|
|
642
|
+
}
|
|
225
643
|
this.rendererManager.update(gameObject);
|
|
226
644
|
}
|
|
227
645
|
}
|
|
646
|
+
handleHierarchyChange(changed) {
|
|
647
|
+
if (changed.componentName !== 'Transform')
|
|
648
|
+
return;
|
|
649
|
+
if (changed.type !== engine.OBSERVER_TYPE.CHANGE && changed.type !== engine.OBSERVER_TYPE.ADD)
|
|
650
|
+
return;
|
|
651
|
+
this.ensureTransform3D(changed.gameObject);
|
|
652
|
+
if (changed.gameObject.parent)
|
|
653
|
+
this.ensureTransform3D(changed.gameObject.parent);
|
|
654
|
+
this.threeContext?.reparentGameObject(changed.gameObject);
|
|
655
|
+
}
|
|
656
|
+
ensureTransform3D(gameObject) {
|
|
657
|
+
if (!gameObject || gameObject === gameObject.scene)
|
|
658
|
+
return;
|
|
659
|
+
let transform = gameObject.getComponent(Transform3D);
|
|
660
|
+
if (!transform) {
|
|
661
|
+
transform = new Transform3D();
|
|
662
|
+
gameObject.addComponent(transform);
|
|
663
|
+
}
|
|
664
|
+
this.threeContext?.ensureTransformRoot(gameObject, transform);
|
|
665
|
+
}
|
|
228
666
|
lateUpdate() {
|
|
229
667
|
this.threeContext?.update();
|
|
230
668
|
}
|
|
@@ -235,12 +673,18 @@ class Renderer3DSystem extends engine.System {
|
|
|
235
673
|
this.threeContext?.destroy();
|
|
236
674
|
this.threeContext = null;
|
|
237
675
|
}
|
|
238
|
-
}
|
|
676
|
+
};
|
|
677
|
+
Renderer3DSystem$1 = tslib.__decorate([
|
|
678
|
+
engine.decorators.componentObserver({
|
|
679
|
+
Transform: ['_parent'],
|
|
680
|
+
})
|
|
681
|
+
], Renderer3DSystem$1);
|
|
682
|
+
var Renderer3DSystem = Renderer3DSystem$1;
|
|
239
683
|
|
|
240
684
|
/** Auto-generated by scripts/build-package.mjs — do not edit. */
|
|
241
685
|
Object.assign(Renderer3DSystem, {
|
|
242
686
|
packageName: "@combos-fun/plugin-renderer-3d",
|
|
243
|
-
packageVersion: "0.0.
|
|
687
|
+
packageVersion: "0.0.44",
|
|
244
688
|
});
|
|
245
689
|
|
|
246
690
|
class Renderer3D extends engine.System {
|
|
@@ -267,6 +711,492 @@ class Renderer3D extends engine.System {
|
|
|
267
711
|
}
|
|
268
712
|
}
|
|
269
713
|
|
|
714
|
+
const POSE_PROPS = new Set([
|
|
715
|
+
'positionX', 'positionY', 'positionZ',
|
|
716
|
+
'rotationX', 'rotationY', 'rotationZ',
|
|
717
|
+
'scaleX', 'scaleY', 'scaleZ',
|
|
718
|
+
]);
|
|
719
|
+
let Transform3DSystem = class Transform3DSystem extends Renderer3D {
|
|
720
|
+
constructor() {
|
|
721
|
+
super(...arguments);
|
|
722
|
+
this.name = 'Transform3DSystem';
|
|
723
|
+
}
|
|
724
|
+
static { this.systemName = 'Transform3DSystem'; }
|
|
725
|
+
init() {
|
|
726
|
+
const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
|
|
727
|
+
renderer3DSystem.rendererManager.register(this);
|
|
728
|
+
}
|
|
729
|
+
componentChanged(changed) {
|
|
730
|
+
if (changed.componentName === 'Transform') {
|
|
731
|
+
this.ensureOn(changed.gameObject);
|
|
732
|
+
if (changed.gameObject.parent)
|
|
733
|
+
this.ensureOn(changed.gameObject.parent);
|
|
734
|
+
this.threeContext.reparentGameObject(changed.gameObject);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (changed.componentName !== 'Transform3D')
|
|
738
|
+
return;
|
|
739
|
+
if (changed.type === engine.OBSERVER_TYPE.ADD) {
|
|
740
|
+
this.threeContext.ensureTransformRoot(changed.gameObject, changed.component);
|
|
741
|
+
}
|
|
742
|
+
else if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
|
|
743
|
+
this.threeContext.detachObject3D(changed.gameObject.id);
|
|
744
|
+
}
|
|
745
|
+
else if (POSE_PROPS.has(changed.prop?.prop?.[0])) {
|
|
746
|
+
this.threeContext.applyTransform3D(changed.gameObject, changed.component);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
rendererUpdate(gameObject) {
|
|
750
|
+
const transform = gameObject.getComponent(Transform3D);
|
|
751
|
+
if (!transform)
|
|
752
|
+
return;
|
|
753
|
+
this.threeContext.applyTransform3D(gameObject, transform);
|
|
754
|
+
}
|
|
755
|
+
ensureOn(gameObject) {
|
|
756
|
+
if (!gameObject || gameObject === gameObject.scene)
|
|
757
|
+
return;
|
|
758
|
+
let transform = gameObject.getComponent(Transform3D);
|
|
759
|
+
if (!transform) {
|
|
760
|
+
transform = new Transform3D();
|
|
761
|
+
gameObject.addComponent(transform);
|
|
762
|
+
}
|
|
763
|
+
this.threeContext.ensureTransformRoot(gameObject, transform);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
Transform3DSystem = tslib.__decorate([
|
|
767
|
+
engine.decorators.componentObserver({
|
|
768
|
+
Transform3D: [
|
|
769
|
+
'positionX', 'positionY', 'positionZ',
|
|
770
|
+
'rotationX', 'rotationY', 'rotationZ',
|
|
771
|
+
'scaleX', 'scaleY', 'scaleZ',
|
|
772
|
+
],
|
|
773
|
+
Transform: ['_parent'],
|
|
774
|
+
})
|
|
775
|
+
], Transform3DSystem);
|
|
776
|
+
var Transform3DSystem_default = Transform3DSystem;
|
|
777
|
+
|
|
778
|
+
class Render3D extends engine.Component {
|
|
779
|
+
constructor() {
|
|
780
|
+
super(...arguments);
|
|
781
|
+
this.visible = true;
|
|
782
|
+
this.opacity = 1;
|
|
783
|
+
this.renderOrder = 0;
|
|
784
|
+
this.sortableChildren = false;
|
|
785
|
+
}
|
|
786
|
+
static { this.componentName = 'Render3D'; }
|
|
787
|
+
init(obj) {
|
|
788
|
+
if (obj)
|
|
789
|
+
Object.assign(this, obj);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
tslib.__decorate([
|
|
793
|
+
inspectorDecorator.Field({ type: 'boolean', group: 'Render3D', label: 'visible', editor: 'toggle' })
|
|
794
|
+
], Render3D.prototype, "visible", void 0);
|
|
795
|
+
tslib.__decorate([
|
|
796
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, min: 0, max: 1, group: 'Render3D', label: 'opacity', editor: 'number-slider' })
|
|
797
|
+
], Render3D.prototype, "opacity", void 0);
|
|
798
|
+
tslib.__decorate([
|
|
799
|
+
inspectorDecorator.Field({ type: 'number', step: 1, group: 'Render3D', label: 'renderOrder', editor: 'number-stepper' })
|
|
800
|
+
], Render3D.prototype, "renderOrder", void 0);
|
|
801
|
+
tslib.__decorate([
|
|
802
|
+
inspectorDecorator.Field({ type: 'boolean', group: 'Render3D', label: 'sortableChildren', editor: 'toggle' })
|
|
803
|
+
], Render3D.prototype, "sortableChildren", void 0);
|
|
804
|
+
|
|
805
|
+
let Render3DSystem = class Render3DSystem extends Renderer3D {
|
|
806
|
+
constructor() {
|
|
807
|
+
super(...arguments);
|
|
808
|
+
this.name = 'Render3DSystem';
|
|
809
|
+
}
|
|
810
|
+
static { this.systemName = 'Render3DSystem'; }
|
|
811
|
+
init() {
|
|
812
|
+
const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
|
|
813
|
+
renderer3DSystem.rendererManager.register(this);
|
|
814
|
+
}
|
|
815
|
+
componentChanged(changed) {
|
|
816
|
+
if (changed.componentName !== 'Render3D')
|
|
817
|
+
return;
|
|
818
|
+
if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
|
|
819
|
+
const root = this.threeContext.object3DFor(changed.gameObject.id);
|
|
820
|
+
if (root)
|
|
821
|
+
this.apply(root, { visible: true, opacity: 1, renderOrder: 0, sortableChildren: false });
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
this.applyTo(changed.gameObject, changed.component);
|
|
825
|
+
}
|
|
826
|
+
rendererUpdate(gameObject) {
|
|
827
|
+
const component = gameObject.getComponent(Render3D);
|
|
828
|
+
if (!component)
|
|
829
|
+
return;
|
|
830
|
+
this.applyTo(gameObject, component);
|
|
831
|
+
}
|
|
832
|
+
applyTo(gameObject, component) {
|
|
833
|
+
const root = this.threeContext.object3DFor(gameObject.id);
|
|
834
|
+
if (!root)
|
|
835
|
+
return;
|
|
836
|
+
this.apply(root, component);
|
|
837
|
+
if (component.sortableChildren) {
|
|
838
|
+
this.sortChildren(gameObject, root);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
apply(root, component) {
|
|
842
|
+
root.visible = component.visible;
|
|
843
|
+
root.traverse((child) => {
|
|
844
|
+
child.renderOrder = component.renderOrder;
|
|
845
|
+
const mesh = child;
|
|
846
|
+
if (!mesh.isMesh || !mesh.material)
|
|
847
|
+
return;
|
|
848
|
+
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
849
|
+
for (const material of materials) {
|
|
850
|
+
const mat = material;
|
|
851
|
+
if (typeof mat.opacity === 'number') {
|
|
852
|
+
mat.opacity = component.opacity;
|
|
853
|
+
mat.transparent = component.opacity < 1;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
sortChildren(gameObject, root) {
|
|
859
|
+
const ranked = gameObject.transform?.children
|
|
860
|
+
?.map((childTransform) => {
|
|
861
|
+
const childGo = childTransform.gameObject;
|
|
862
|
+
if (!childGo)
|
|
863
|
+
return null;
|
|
864
|
+
const node = this.threeContext.object3DFor(childGo.id);
|
|
865
|
+
if (!node || node.parent !== root)
|
|
866
|
+
return null;
|
|
867
|
+
const render = childGo.getComponent(Render3D);
|
|
868
|
+
return { node, order: render?.renderOrder ?? 0 };
|
|
869
|
+
})
|
|
870
|
+
.filter((item) => !!item)
|
|
871
|
+
.sort((a, b) => a.order - b.order);
|
|
872
|
+
if (!ranked?.length)
|
|
873
|
+
return;
|
|
874
|
+
for (const item of ranked) {
|
|
875
|
+
root.add(item.node);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
Render3DSystem = tslib.__decorate([
|
|
880
|
+
engine.decorators.componentObserver({
|
|
881
|
+
Render3D: ['visible', 'opacity', 'renderOrder', 'sortableChildren'],
|
|
882
|
+
})
|
|
883
|
+
], Render3DSystem);
|
|
884
|
+
var Render3DSystem_default = Render3DSystem;
|
|
885
|
+
|
|
886
|
+
class Camera3D extends engine.Component {
|
|
887
|
+
constructor() {
|
|
888
|
+
super(...arguments);
|
|
889
|
+
this.fov = 75;
|
|
890
|
+
this.near = 0.1;
|
|
891
|
+
this.far = 1000;
|
|
892
|
+
this.active = true;
|
|
893
|
+
this.lookAt = false;
|
|
894
|
+
this.lookAtX = 0;
|
|
895
|
+
this.lookAtY = 0;
|
|
896
|
+
this.lookAtZ = 0;
|
|
897
|
+
}
|
|
898
|
+
static { this.componentName = 'Camera3D'; }
|
|
899
|
+
init(obj) {
|
|
900
|
+
if (obj)
|
|
901
|
+
Object.assign(this, obj);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
tslib.__decorate([
|
|
905
|
+
inspectorDecorator.Field({ type: 'number', step: 1, group: 'Camera3D', label: 'fov', editor: 'number-stepper' })
|
|
906
|
+
], Camera3D.prototype, "fov", void 0);
|
|
907
|
+
tslib.__decorate([
|
|
908
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Camera3D', label: 'near', editor: 'number-stepper' })
|
|
909
|
+
], Camera3D.prototype, "near", void 0);
|
|
910
|
+
tslib.__decorate([
|
|
911
|
+
inspectorDecorator.Field({ type: 'number', step: 1, group: 'Camera3D', label: 'far', editor: 'number-stepper' })
|
|
912
|
+
], Camera3D.prototype, "far", void 0);
|
|
913
|
+
tslib.__decorate([
|
|
914
|
+
inspectorDecorator.Field({ type: 'boolean', group: 'Camera3D', label: 'active', editor: 'toggle' })
|
|
915
|
+
], Camera3D.prototype, "active", void 0);
|
|
916
|
+
tslib.__decorate([
|
|
917
|
+
inspectorDecorator.Field({ type: 'boolean', group: 'Camera3D', label: 'lookAt', editor: 'toggle' })
|
|
918
|
+
], Camera3D.prototype, "lookAt", void 0);
|
|
919
|
+
tslib.__decorate([
|
|
920
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Camera3D', label: 'lookAtX', editor: 'number-stepper' })
|
|
921
|
+
], Camera3D.prototype, "lookAtX", void 0);
|
|
922
|
+
tslib.__decorate([
|
|
923
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Camera3D', label: 'lookAtY', editor: 'number-stepper' })
|
|
924
|
+
], Camera3D.prototype, "lookAtY", void 0);
|
|
925
|
+
tslib.__decorate([
|
|
926
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Camera3D', label: 'lookAtZ', editor: 'number-stepper' })
|
|
927
|
+
], Camera3D.prototype, "lookAtZ", void 0);
|
|
928
|
+
|
|
929
|
+
let Camera3DSystem = class Camera3DSystem extends Renderer3D {
|
|
930
|
+
constructor() {
|
|
931
|
+
super(...arguments);
|
|
932
|
+
this.name = 'Camera3DSystem';
|
|
933
|
+
this.cameras = new Map();
|
|
934
|
+
}
|
|
935
|
+
static { this.systemName = 'Camera3DSystem'; }
|
|
936
|
+
init() {
|
|
937
|
+
const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
|
|
938
|
+
renderer3DSystem.rendererManager.register(this);
|
|
939
|
+
}
|
|
940
|
+
componentChanged(changed) {
|
|
941
|
+
if (changed.componentName !== 'Camera3D')
|
|
942
|
+
return;
|
|
943
|
+
const component = changed.component;
|
|
944
|
+
if (changed.type === engine.OBSERVER_TYPE.ADD) {
|
|
945
|
+
this.mount(changed.gameObject, component);
|
|
946
|
+
}
|
|
947
|
+
else if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
|
|
948
|
+
this.unmount(changed.gameObject.id);
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
this.sync(changed.gameObject, component);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
rendererUpdate(gameObject) {
|
|
955
|
+
const component = gameObject.getComponent(Camera3D);
|
|
956
|
+
const camera = this.cameras.get(gameObject.id);
|
|
957
|
+
if (!component || !camera)
|
|
958
|
+
return;
|
|
959
|
+
if (component.lookAt) {
|
|
960
|
+
camera.lookAt(component.lookAtX, component.lookAtY, component.lookAtZ);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
mount(gameObject, component) {
|
|
964
|
+
this.ensureTransform(gameObject);
|
|
965
|
+
const camera = new three.PerspectiveCamera(component.fov, this.threeContext.camera.aspect, component.near, component.far);
|
|
966
|
+
this.threeContext.attachVisual(gameObject.id, camera, gameObject);
|
|
967
|
+
this.cameras.set(gameObject.id, camera);
|
|
968
|
+
this.sync(gameObject, component);
|
|
969
|
+
}
|
|
970
|
+
sync(gameObject, component) {
|
|
971
|
+
const camera = this.cameras.get(gameObject.id);
|
|
972
|
+
if (!camera)
|
|
973
|
+
return;
|
|
974
|
+
camera.fov = component.fov;
|
|
975
|
+
camera.near = component.near;
|
|
976
|
+
camera.far = component.far;
|
|
977
|
+
camera.updateProjectionMatrix();
|
|
978
|
+
if (component.active) {
|
|
979
|
+
this.threeContext.setActiveCamera(camera);
|
|
980
|
+
}
|
|
981
|
+
else if (this.threeContext.camera === camera) {
|
|
982
|
+
this.activateAnother(gameObject.id);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
unmount(id) {
|
|
986
|
+
const camera = this.cameras.get(id);
|
|
987
|
+
if (!camera)
|
|
988
|
+
return;
|
|
989
|
+
this.threeContext.detachVisual(id, camera);
|
|
990
|
+
this.cameras.delete(id);
|
|
991
|
+
if (this.threeContext.camera === camera) {
|
|
992
|
+
this.activateAnother(id);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
activateAnother(exceptId) {
|
|
996
|
+
for (const [id, camera] of this.cameras) {
|
|
997
|
+
if (id === exceptId)
|
|
998
|
+
continue;
|
|
999
|
+
const go = this.game.gameObjects.find((item) => item.id === id);
|
|
1000
|
+
const component = go?.getComponent(Camera3D);
|
|
1001
|
+
if (component?.active) {
|
|
1002
|
+
this.threeContext.setActiveCamera(camera);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
this.threeContext.restoreFallbackCamera();
|
|
1007
|
+
}
|
|
1008
|
+
ensureTransform(gameObject) {
|
|
1009
|
+
let transform = gameObject.getComponent(Transform3D);
|
|
1010
|
+
if (!transform) {
|
|
1011
|
+
transform = new Transform3D();
|
|
1012
|
+
transform.positionZ = 5;
|
|
1013
|
+
gameObject.addComponent(transform);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
if (isIdentityPose(transform)) {
|
|
1017
|
+
transform.positionZ = 5;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
onDestroy() {
|
|
1021
|
+
for (const [id] of this.cameras) {
|
|
1022
|
+
this.unmount(id);
|
|
1023
|
+
}
|
|
1024
|
+
this.cameras.clear();
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
Camera3DSystem = tslib.__decorate([
|
|
1028
|
+
engine.decorators.componentObserver({
|
|
1029
|
+
Camera3D: ['fov', 'near', 'far', 'active', 'lookAt', 'lookAtX', 'lookAtY', 'lookAtZ'],
|
|
1030
|
+
})
|
|
1031
|
+
], Camera3DSystem);
|
|
1032
|
+
var Camera3DSystem_default = Camera3DSystem;
|
|
1033
|
+
|
|
1034
|
+
class Light3D extends engine.Component {
|
|
1035
|
+
constructor() {
|
|
1036
|
+
super(...arguments);
|
|
1037
|
+
this.type = 'directional';
|
|
1038
|
+
this.color = 0xffffff;
|
|
1039
|
+
this.intensity = 1;
|
|
1040
|
+
this.castShadow = false;
|
|
1041
|
+
this.skyColor = 0xffffff;
|
|
1042
|
+
this.groundColor = 0x444444;
|
|
1043
|
+
this.distance = 0;
|
|
1044
|
+
this.decay = 2;
|
|
1045
|
+
this.angle = Math.PI / 3;
|
|
1046
|
+
this.penumbra = 0;
|
|
1047
|
+
}
|
|
1048
|
+
static { this.componentName = 'Light3D'; }
|
|
1049
|
+
init(obj) {
|
|
1050
|
+
if (obj)
|
|
1051
|
+
Object.assign(this, obj);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
tslib.__decorate([
|
|
1055
|
+
inspectorDecorator.Field({
|
|
1056
|
+
type: 'string',
|
|
1057
|
+
group: 'Light3D',
|
|
1058
|
+
label: 'type',
|
|
1059
|
+
editor: 'enum',
|
|
1060
|
+
enumOptions: ['ambient', 'directional', 'hemisphere', 'point', 'spot'],
|
|
1061
|
+
})
|
|
1062
|
+
], Light3D.prototype, "type", void 0);
|
|
1063
|
+
tslib.__decorate([
|
|
1064
|
+
inspectorDecorator.Field({ type: 'number', group: 'Light3D', label: 'color', editor: 'number-stepper' })
|
|
1065
|
+
], Light3D.prototype, "color", void 0);
|
|
1066
|
+
tslib.__decorate([
|
|
1067
|
+
inspectorDecorator.Field({ type: 'number', step: 0.05, group: 'Light3D', label: 'intensity', editor: 'number-stepper' })
|
|
1068
|
+
], Light3D.prototype, "intensity", void 0);
|
|
1069
|
+
tslib.__decorate([
|
|
1070
|
+
inspectorDecorator.Field({ type: 'boolean', group: 'Light3D', label: 'castShadow', editor: 'toggle' })
|
|
1071
|
+
], Light3D.prototype, "castShadow", void 0);
|
|
1072
|
+
tslib.__decorate([
|
|
1073
|
+
inspectorDecorator.Field({ type: 'number', group: 'Light3D', label: 'skyColor', editor: 'number-stepper' })
|
|
1074
|
+
], Light3D.prototype, "skyColor", void 0);
|
|
1075
|
+
tslib.__decorate([
|
|
1076
|
+
inspectorDecorator.Field({ type: 'number', group: 'Light3D', label: 'groundColor', editor: 'number-stepper' })
|
|
1077
|
+
], Light3D.prototype, "groundColor", void 0);
|
|
1078
|
+
tslib.__decorate([
|
|
1079
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Light3D', label: 'distance', editor: 'number-stepper' })
|
|
1080
|
+
], Light3D.prototype, "distance", void 0);
|
|
1081
|
+
tslib.__decorate([
|
|
1082
|
+
inspectorDecorator.Field({ type: 'number', step: 0.1, group: 'Light3D', label: 'decay', editor: 'number-stepper' })
|
|
1083
|
+
], Light3D.prototype, "decay", void 0);
|
|
1084
|
+
tslib.__decorate([
|
|
1085
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, group: 'Light3D', label: 'angle', editor: 'number-stepper' })
|
|
1086
|
+
], Light3D.prototype, "angle", void 0);
|
|
1087
|
+
tslib.__decorate([
|
|
1088
|
+
inspectorDecorator.Field({ type: 'number', step: 0.01, min: 0, max: 1, group: 'Light3D', label: 'penumbra', editor: 'number-slider' })
|
|
1089
|
+
], Light3D.prototype, "penumbra", void 0);
|
|
1090
|
+
|
|
1091
|
+
let Light3DSystem = class Light3DSystem extends Renderer3D {
|
|
1092
|
+
constructor() {
|
|
1093
|
+
super(...arguments);
|
|
1094
|
+
this.name = 'Light3DSystem';
|
|
1095
|
+
this.lights = new Map();
|
|
1096
|
+
}
|
|
1097
|
+
static { this.systemName = 'Light3DSystem'; }
|
|
1098
|
+
init() {
|
|
1099
|
+
const renderer3DSystem = this.game.getSystem('Renderer3DSystem');
|
|
1100
|
+
renderer3DSystem.rendererManager.register(this);
|
|
1101
|
+
}
|
|
1102
|
+
componentChanged(changed) {
|
|
1103
|
+
if (changed.componentName !== 'Light3D')
|
|
1104
|
+
return;
|
|
1105
|
+
const component = changed.component;
|
|
1106
|
+
if (changed.type === engine.OBSERVER_TYPE.ADD) {
|
|
1107
|
+
this.mount(changed.gameObject, component);
|
|
1108
|
+
}
|
|
1109
|
+
else if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
|
|
1110
|
+
this.unmount(changed.gameObject.id);
|
|
1111
|
+
}
|
|
1112
|
+
else if (changed.prop?.prop?.[0] === 'type') {
|
|
1113
|
+
this.unmount(changed.gameObject.id);
|
|
1114
|
+
this.mount(changed.gameObject, component);
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
this.sync(changed.gameObject.id, component);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
rendererUpdate(_gameObject) { }
|
|
1121
|
+
mount(gameObject, component) {
|
|
1122
|
+
this.ensureTransform(gameObject);
|
|
1123
|
+
if (this.lights.size === 0) {
|
|
1124
|
+
this.threeContext.removeDefaultLights();
|
|
1125
|
+
}
|
|
1126
|
+
const light = this.createLight(component);
|
|
1127
|
+
this.threeContext.attachVisual(gameObject.id, light, gameObject);
|
|
1128
|
+
this.lights.set(gameObject.id, light);
|
|
1129
|
+
this.sync(gameObject.id, component);
|
|
1130
|
+
}
|
|
1131
|
+
createLight(component) {
|
|
1132
|
+
switch (component.type) {
|
|
1133
|
+
case 'ambient':
|
|
1134
|
+
return new three.AmbientLight(component.color, component.intensity);
|
|
1135
|
+
case 'hemisphere':
|
|
1136
|
+
return new three.HemisphereLight(component.skyColor, component.groundColor, component.intensity);
|
|
1137
|
+
case 'point':
|
|
1138
|
+
return new three.PointLight(component.color, component.intensity, component.distance, component.decay);
|
|
1139
|
+
case 'spot':
|
|
1140
|
+
return new three.SpotLight(component.color, component.intensity, component.distance, component.angle, component.penumbra, component.decay);
|
|
1141
|
+
case 'directional':
|
|
1142
|
+
default:
|
|
1143
|
+
return new three.DirectionalLight(component.color, component.intensity);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
sync(id, component) {
|
|
1147
|
+
const light = this.lights.get(id);
|
|
1148
|
+
if (!light)
|
|
1149
|
+
return;
|
|
1150
|
+
if ('color' in light && light.color) {
|
|
1151
|
+
light.color.set(component.color);
|
|
1152
|
+
}
|
|
1153
|
+
light.intensity = component.intensity;
|
|
1154
|
+
if (light instanceof three.HemisphereLight) {
|
|
1155
|
+
light.color.set(component.skyColor);
|
|
1156
|
+
light.groundColor.set(component.groundColor);
|
|
1157
|
+
}
|
|
1158
|
+
if (light instanceof three.PointLight || light instanceof three.SpotLight) {
|
|
1159
|
+
light.distance = component.distance;
|
|
1160
|
+
light.decay = component.decay;
|
|
1161
|
+
}
|
|
1162
|
+
if (light instanceof three.SpotLight) {
|
|
1163
|
+
light.angle = component.angle;
|
|
1164
|
+
light.penumbra = component.penumbra;
|
|
1165
|
+
}
|
|
1166
|
+
if ('castShadow' in light) {
|
|
1167
|
+
light.castShadow = !!(component.castShadow && this.threeContext.shadows);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
unmount(id) {
|
|
1171
|
+
const light = this.lights.get(id);
|
|
1172
|
+
if (!light)
|
|
1173
|
+
return;
|
|
1174
|
+
this.threeContext.detachVisual(id, light);
|
|
1175
|
+
light.dispose?.();
|
|
1176
|
+
this.lights.delete(id);
|
|
1177
|
+
}
|
|
1178
|
+
ensureTransform(gameObject) {
|
|
1179
|
+
if (gameObject.getComponent(Transform3D))
|
|
1180
|
+
return;
|
|
1181
|
+
gameObject.addComponent(new Transform3D());
|
|
1182
|
+
}
|
|
1183
|
+
onDestroy() {
|
|
1184
|
+
for (const [id] of this.lights) {
|
|
1185
|
+
this.unmount(id);
|
|
1186
|
+
}
|
|
1187
|
+
this.lights.clear();
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
Light3DSystem = tslib.__decorate([
|
|
1191
|
+
engine.decorators.componentObserver({
|
|
1192
|
+
Light3D: [
|
|
1193
|
+
'type', 'color', 'intensity', 'castShadow',
|
|
1194
|
+
'skyColor', 'groundColor', 'distance', 'decay', 'angle', 'penumbra',
|
|
1195
|
+
],
|
|
1196
|
+
})
|
|
1197
|
+
], Light3DSystem);
|
|
1198
|
+
var Light3DSystem_default = Light3DSystem;
|
|
1199
|
+
|
|
270
1200
|
async function requireNamedResource(name) {
|
|
271
1201
|
const trimmed = String(name ?? '').trim();
|
|
272
1202
|
if (!trimmed) {
|
|
@@ -295,12 +1225,32 @@ async function textureFromNamedImage(name) {
|
|
|
295
1225
|
}
|
|
296
1226
|
|
|
297
1227
|
exports.COMBOS_GAME_OBJECT_ID = COMBOS_GAME_OBJECT_ID;
|
|
1228
|
+
exports.COMBOS_INSTANCE_GAME_OBJECT_IDS = COMBOS_INSTANCE_GAME_OBJECT_IDS;
|
|
1229
|
+
exports.COMBOS_TRANSFORM3D = COMBOS_TRANSFORM3D;
|
|
1230
|
+
exports.Camera3D = Camera3D;
|
|
1231
|
+
exports.Camera3DSystem = Camera3DSystem_default;
|
|
1232
|
+
exports.Light3D = Light3D;
|
|
1233
|
+
exports.Light3DSystem = Light3DSystem_default;
|
|
1234
|
+
exports.Render3D = Render3D;
|
|
1235
|
+
exports.Render3DSystem = Render3DSystem_default;
|
|
298
1236
|
exports.Renderer3D = Renderer3D;
|
|
299
1237
|
exports.Renderer3DManager = Renderer3DManager;
|
|
300
1238
|
exports.Renderer3DSystem = Renderer3DSystem;
|
|
301
1239
|
exports.ThreeContext = ThreeContext;
|
|
1240
|
+
exports.Transform3D = Transform3D;
|
|
1241
|
+
exports.Transform3DSystem = Transform3DSystem_default;
|
|
1242
|
+
exports.VisualPoseBridge = VisualPoseBridge;
|
|
1243
|
+
exports.copyPose = copyPose;
|
|
1244
|
+
exports.gameObjectIdFromIntersection = gameObjectIdFromIntersection;
|
|
302
1245
|
exports.gameObjectIdFromObject3D = gameObjectIdFromObject3D;
|
|
1246
|
+
exports.has3DChildGameObjects = has3DChildGameObjects;
|
|
1247
|
+
exports.is3DSceneParent = is3DSceneParent;
|
|
1248
|
+
exports.isIdentityPose = isIdentityPose;
|
|
1249
|
+
exports.isTransform3DRoot = isTransform3DRoot;
|
|
1250
|
+
exports.poseKeyOf = poseKeyOf;
|
|
1251
|
+
exports.readPose = readPose;
|
|
303
1252
|
exports.requireNamedResource = requireNamedResource;
|
|
1253
|
+
exports.seedTransform3D = seedTransform3D;
|
|
304
1254
|
exports.tagObject3D = tagObject3D;
|
|
305
1255
|
exports.textureFromNamedImage = textureFromNamedImage;
|
|
306
1256
|
exports.textureFromResourceImage = textureFromResourceImage;
|