@@ -593,6 +593,12 @@
|
|
593
593
|
}
|
594
594
|
}
|
595
595
|
|
596
|
+
getBody(obj: ICollider | IRigidbody): null | any {
|
597
|
+
if (!obj) return null;
|
598
|
+
const body = obj[$bodyKey];
|
599
|
+
return body;
|
600
|
+
}
|
601
|
+
|
596
602
|
private createCollider(collider: ICollider, desc: ColliderDesc, center?: Vector3) {
|
597
603
|
if (!this.world) throw new Error("Physics world not initialized");
|
598
604
|
const matrix = this._tempMatrix;
|
@@ -431,6 +431,7 @@
|
|
431
431
|
|
432
432
|
updateBody(comp: ICollider | IRigidbody, translation: boolean, rotation: boolean);
|
433
433
|
removeBody(body: IComponent);
|
434
|
+
getBody(obj: ICollider | IRigidbody): null | any;
|
434
435
|
|
435
436
|
// Joints
|
436
437
|
addFixedJoint(body1: IRigidbody, body2: IRigidbody);
|
@@ -13,8 +13,6 @@
|
|
13
13
|
import { setCameraController } from "../engine/engine_camera.js";
|
14
14
|
import { SyncedTransform } from "./SyncedTransform.js";
|
15
15
|
import { tryGetUIComponent } from "./ui/Utils.js";
|
16
|
-
import { GroundProjectedEnv } from "./GroundProjection.js";
|
17
|
-
import { ShadowCatcher } from "./ShadowCatcher.js";
|
18
16
|
import { GroundProjectedSkybox } from "three/examples/jsm/objects/GroundProjectedSkybox.js";
|
19
17
|
|
20
18
|
const freeCam = getParam("freecam");
|
@@ -43,6 +41,9 @@
|
|
43
41
|
|
44
42
|
autoRotate: boolean = false;
|
45
43
|
autoRotateSpeed: number = 1.0;
|
44
|
+
/** When enabled the scene will be automatically fitted into the camera view in onEnable */
|
45
|
+
@serializable()
|
46
|
+
autoFit: boolean = false;
|
46
47
|
enableKeys: boolean = true;
|
47
48
|
enableDamping: boolean = true;
|
48
49
|
dampingFactor: number = 0.1;
|
@@ -81,15 +82,18 @@
|
|
81
82
|
private _afterHandleInputFn?: any;
|
82
83
|
private _camera: Camera | null = null;
|
83
84
|
private _syncedTransform?: SyncedTransform;
|
85
|
+
private _didStart = false;
|
84
86
|
|
85
87
|
targetElement: HTMLElement | null = null;
|
86
88
|
|
87
89
|
awake(): void {
|
90
|
+
this._didStart = false;
|
88
91
|
this._lookTargetPosition = new Vector3();
|
89
92
|
this._startedListeningToKeyEvents = false;
|
90
93
|
}
|
91
94
|
|
92
95
|
start() {
|
96
|
+
this._didStart = true;
|
93
97
|
if (this.autoTarget) {
|
94
98
|
if (this._controls) {
|
95
99
|
const camGo = GameObject.getComponent(this.gameObject, Camera);
|
@@ -99,10 +103,19 @@
|
|
99
103
|
const worldPosition = getWorldPosition(camGo.cam);
|
100
104
|
const distanceToCenter = worldPosition.length();
|
101
105
|
const forward = new Vector3(0, 0, -distanceToCenter).applyMatrix4(camGo.cam.matrixWorld);
|
102
|
-
this.setTarget(forward, true);
|
103
106
|
}
|
107
|
+
if (this.autoTarget && !this.setFromTargetPosition()) {
|
108
|
+
const opts = new RaycastOptions();
|
109
|
+
// center of the screen:
|
110
|
+
opts.screenPoint = new Vector2(0, 0);
|
111
|
+
opts.lineThreshold = 0.1;
|
112
|
+
const hits = this.context.physics.raycast(opts);
|
113
|
+
if (hits.length > 0) {
|
114
|
+
this.setTarget(hits[0].point, true);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
if (this.autoFit) this.fitCamera()
|
104
118
|
}
|
105
|
-
this.startCoroutine(this.startRaycastDelayed());
|
106
119
|
}
|
107
120
|
|
108
121
|
this._eventSystem = EventSystem.get(this.context) ?? undefined;
|
@@ -173,6 +186,9 @@
|
|
173
186
|
}
|
174
187
|
}
|
175
188
|
this._syncedTransform = GameObject.getComponent(this.gameObject, SyncedTransform) ?? undefined;
|
189
|
+
if (this._didStart) {
|
190
|
+
if (this.autoFit) this.fitCamera()
|
191
|
+
}
|
176
192
|
}
|
177
193
|
|
178
194
|
onDisable() {
|
@@ -207,20 +223,6 @@
|
|
207
223
|
}
|
208
224
|
}
|
209
225
|
|
210
|
-
// we need to wait one frame (when starting the scene for the very first time)
|
211
|
-
private * startRaycastDelayed() {
|
212
|
-
yield;
|
213
|
-
if (this.autoTarget && !this.setFromTargetPosition()) {
|
214
|
-
const opts = new RaycastOptions();
|
215
|
-
// center of the screen:
|
216
|
-
opts.screenPoint = new Vector2(0, 0);
|
217
|
-
opts.lineThreshold = 0.1;
|
218
|
-
const hits = this.context.physics.raycast(opts);
|
219
|
-
if (hits.length > 0) {
|
220
|
-
this.setTarget(hits[0].point, true);
|
221
|
-
}
|
222
|
-
}
|
223
|
-
}
|
224
226
|
|
225
227
|
onBeforeRender() {
|
226
228
|
if (!this._controls) return;
|
@@ -374,7 +376,7 @@
|
|
374
376
|
// Adapted from https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/24
|
375
377
|
// Slower but better implementation that takes bones and exact vertex positions into account: https://github.com/google/model-viewer/blob/04e900c5027de8c5306fe1fe9627707f42811b05/packages/model-viewer/src/three-components/ModelScene.ts#L321
|
376
378
|
/** Fits the camera to show the objects provided (defaults to the scene if no objects are passed in) */
|
377
|
-
fitCamera(objects?: Array<Object3D>, fitOffset: number = 1.1) {
|
379
|
+
fitCamera(objects?: Array<Object3D>, fitOffset: number = 1.1, immediate: boolean = true) {
|
378
380
|
const camera = this._cameraObject as PerspectiveCamera;
|
379
381
|
const controls = this._controls as ThreeOrbitControls | null;
|
380
382
|
if (!objects?.length) objects = this.context.scene.children;
|
@@ -395,6 +397,8 @@
|
|
395
397
|
const emptyChildren = [];
|
396
398
|
function expandByObjectRecursive(obj: Object3D) {
|
397
399
|
let allowExpanding = true;
|
400
|
+
// we dont want to check invisible objects
|
401
|
+
if (!obj.visible) return;
|
398
402
|
// ignore Box3Helpers
|
399
403
|
if (obj instanceof Box3Helper) allowExpanding = false;
|
400
404
|
if (obj instanceof GridHelper) allowExpanding = false;
|
@@ -402,9 +406,16 @@
|
|
402
406
|
if (obj instanceof GroundProjectedSkybox) allowExpanding = false;
|
403
407
|
// // Ignore shadow catcher geometry
|
404
408
|
if ((obj as Mesh).material instanceof ShadowMaterial) allowExpanding = false;
|
409
|
+
// ONLY fit meshes
|
410
|
+
if (!(obj instanceof Mesh)) allowExpanding = false;
|
411
|
+
// Ignore things parented to the camera + ignore the camera
|
412
|
+
if (obj === camera) return;
|
413
|
+
// We don't want to fit UI objects
|
414
|
+
if (obj["isUI"] === true) return;
|
405
415
|
// If we encountered some geometry that should be ignored
|
406
416
|
// Then we don't want to use that for expanding the view
|
407
417
|
if (allowExpanding) {
|
418
|
+
console.log(obj.name, obj.type, obj);
|
408
419
|
// Temporary override children
|
409
420
|
const children_length = obj.children;
|
410
421
|
obj.children = emptyChildren;
|
@@ -453,7 +464,8 @@
|
|
453
464
|
|
454
465
|
controls.maxDistance = distance * 10;
|
455
466
|
controls.minDistance = distance * 0.01;
|
456
|
-
|
467
|
+
|
468
|
+
this.setTarget(center, immediate);
|
457
469
|
this.autoTarget = false;
|
458
470
|
|
459
471
|
// TODO: this doesnt take the Camera component nearClipPlane into account
|
@@ -463,8 +475,14 @@
|
|
463
475
|
camera.updateMatrixWorld();
|
464
476
|
camera.updateProjectionMatrix();
|
465
477
|
|
466
|
-
|
478
|
+
if (camera.parent) {
|
479
|
+
const cameraLocalPosition = camera.parent!.worldToLocal(controls.target.clone().sub(direction));
|
480
|
+
this.setCameraTarget(cameraLocalPosition, immediate);
|
481
|
+
}
|
482
|
+
else console.error(`Can not fit camera ${camera.name} because it has no parent`)
|
467
483
|
|
484
|
+
// setWorldPosition(camera, controls.target.clone().sub(direction));
|
485
|
+
|
468
486
|
if (debugCameraFit) {
|
469
487
|
const helper = new Box3Helper(box);
|
470
488
|
this.context.scene.add(helper);
|