Needle Engine

Changes between version 3.19.1 and 3.19.2
Files changed (3) hide show
  1. src/engine/engine_physics_rapier.ts +6 -0
  2. src/engine/engine_types.ts +1 -0
  3. src/engine-components/OrbitControls.ts +39 -21
src/engine/engine_physics_rapier.ts CHANGED
@@ -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;
src/engine/engine_types.ts CHANGED
@@ -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);
src/engine-components/OrbitControls.ts CHANGED
@@ -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
- this.setTarget(center, true);
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
- setWorldPosition(camera, controls.target.clone().sub(direction));
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);