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