MikaFil commited on
Commit
d600987
·
verified ·
1 Parent(s): 6b3454d

Update orbit-camera.js

Browse files
Files changed (1) hide show
  1. orbit-camera.js +47 -269
orbit-camera.js CHANGED
@@ -1,3 +1,4 @@
 
1
  ///////////////////////////////////////////////////////////////////////////////
2
  // Orbit Camera Script //
3
  ////////////////////////////////////////////////////////////////////////////////
@@ -34,7 +35,6 @@ OrbitCamera.attributes.add('frameOnStart', {
34
  description: 'Frames the entity or scene at the start of the application."'
35
  });
36
 
37
-
38
  // Property to get and set the distance between the pivot point and camera
39
  // Clamped between this.distanceMin and this.distanceMax
40
  Object.defineProperty(OrbitCamera.prototype, 'distance', {
@@ -56,11 +56,8 @@ Object.defineProperty(OrbitCamera.prototype, 'orthoHeight', {
56
  }
57
  });
58
 
59
-
60
  // Property to get and set the pitch (in degrees) of the camera around the pivot.
61
  // The pitch value is clamped between pitchAngleMin and pitchAngleMax.
62
- // With your JSON (minAngle: -90, maxAngle: 0), the allowed pitch will be from -90 (overhead)
63
- // to 0 (horizontal).
64
  Object.defineProperty(OrbitCamera.prototype, 'pitch', {
65
  get: function () {
66
  return this._targetPitch;
@@ -70,7 +67,6 @@ Object.defineProperty(OrbitCamera.prototype, 'pitch', {
70
  }
71
  });
72
 
73
-
74
  // Property to get and set the yaw (in degrees) of the camera around the pivot.
75
  Object.defineProperty(OrbitCamera.prototype, 'yaw', {
76
  get: function () {
@@ -81,7 +77,6 @@ Object.defineProperty(OrbitCamera.prototype, 'yaw', {
81
  }
82
  });
83
 
84
-
85
  // Property to get and set the world position of the pivot point that the camera orbits around.
86
  Object.defineProperty(OrbitCamera.prototype, 'pivotPoint', {
87
  get: function () {
@@ -92,10 +87,8 @@ Object.defineProperty(OrbitCamera.prototype, 'pivotPoint', {
92
  }
93
  });
94
 
95
-
96
  // Moves the camera to look at an entity and all its children so they are all in view.
97
  OrbitCamera.prototype.focus = function (focusEntity) {
98
- // Calculate a bounding box that encompasses all models to frame in the camera view.
99
  this._buildAabb(focusEntity);
100
  var halfExtents = this._modelsAabb.halfExtents;
101
  var radius = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
@@ -148,7 +141,6 @@ OrbitCamera.prototype.resetToPosition = function (position, lookAtPoint) {
148
  this._updatePosition();
149
  };
150
 
151
-
152
  ////////////////////////////////////////////////////////////////////////////////
153
  // Private Methods //
154
  ////////////////////////////////////////////////////////////////////////////////
@@ -168,7 +160,6 @@ OrbitCamera.prototype.initialize = function () {
168
  this._pivotPoint.copy(this._modelsAabb.center);
169
  var cameraQuat = this.entity.getRotation();
170
  this._yaw = this._calcYaw(cameraQuat);
171
- // Compute pitch and clamp it immediately.
172
  this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
173
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
174
  this._distance = 0;
@@ -185,26 +176,18 @@ OrbitCamera.prototype.initialize = function () {
185
 
186
  this._targetDistance = this._distance;
187
 
188
- this.on('attr:distanceMin', function (value, prev) {
189
- this._distance = this._clampDistance(this._distance);
190
- });
191
- this.on('attr:distanceMax', function (value, prev) {
192
- this._distance = this._clampDistance(this._distance);
193
- });
194
- this.on('attr:pitchAngleMin', function (value, prev) {
195
- this._pitch = this._clampPitchAngle(this._pitch);
196
- });
197
- this.on('attr:pitchAngleMax', function (value, prev) {
198
- this._pitch = this._clampPitchAngle(this._pitch);
199
- });
200
- this.on('attr:focusEntity', function (value, prev) {
201
  if (this.frameOnStart) {
202
  this.focus(value || this.app.root);
203
  } else {
204
  this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root);
205
  }
206
  });
207
- this.on('attr:frameOnStart', function (value, prev) {
208
  if (value) {
209
  this.focus(this.focusEntity || this.app.root);
210
  }
@@ -215,10 +198,23 @@ OrbitCamera.prototype.initialize = function () {
215
  };
216
 
217
  OrbitCamera.prototype.update = function (dt) {
 
218
  var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
219
  this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
220
- this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
221
- this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);
 
 
 
 
 
 
 
 
 
 
 
 
222
  this._updatePosition();
223
  };
224
 
@@ -229,60 +225,52 @@ OrbitCamera.prototype._updatePosition = function () {
229
  position.copy(this.entity.forward);
230
  position.mulScalar(-this._distance);
231
  position.add(this.pivotPoint);
232
- // NEW: Clamp the camera's Y position so it never goes below the specified minY value.
233
  position.y = Math.max(position.y, this.minY);
234
  this.entity.setPosition(position);
235
  };
236
 
237
  OrbitCamera.prototype._removeInertia = function () {
238
- this._yaw = this._targetYaw;
239
- this._pitch = this._targetPitch;
240
  this._distance = this._targetDistance;
241
  };
242
 
243
  OrbitCamera.prototype._checkAspectRatio = function () {
244
  var height = this.app.graphicsDevice.height;
245
- var width = this.app.graphicsDevice.width;
246
  this.entity.camera.horizontalFov = (height > width);
247
  };
248
 
249
  OrbitCamera.prototype._buildAabb = function (entity) {
250
- var i, m, meshInstances = [];
251
  var renders = entity.findComponents('render');
252
- for (i = 0; i < renders.length; i++) {
253
- var render = renders[i];
254
- for (m = 0; m < render.meshInstances.length; m++) {
255
- meshInstances.push(render.meshInstances[m]);
256
  }
257
  }
258
  var models = entity.findComponents('model');
259
  for (i = 0; i < models.length; i++) {
260
- var model = models[i];
261
- for (m = 0; m < model.meshInstances.length; m++) {
262
- meshInstances.push(model.meshInstances[m]);
263
  }
264
  }
265
  var gsplats = entity.findComponents('gsplat');
266
  for (i = 0; i < gsplats.length; i++) {
267
- var gsplat = gsplats[i];
268
- var instance = gsplat.instance;
269
- if (instance?.meshInstance) {
270
- meshInstances.push(instance.meshInstance);
271
- }
272
  }
273
  for (i = 0; i < meshInstances.length; i++) {
274
- if (i === 0) {
275
- this._modelsAabb.copy(meshInstances[i].aabb);
276
- } else {
277
- this._modelsAabb.add(meshInstances[i].aabb);
278
- }
279
  }
280
  };
281
 
282
  OrbitCamera.prototype._calcYaw = function (quat) {
283
- var transformedForward = new pc.Vec3();
284
- quat.transformVector(pc.Vec3.FORWARD, transformedForward);
285
- return Math.atan2(-transformedForward.x, -transformedForward.z) * pc.math.RAD_TO_DEG;
286
  };
287
 
288
  OrbitCamera.prototype._clampDistance = function (distance) {
@@ -292,10 +280,6 @@ OrbitCamera.prototype._clampDistance = function (distance) {
292
  return Math.max(distance, this.distanceMin);
293
  };
294
 
295
-
296
- // ----- FIXED PITCH CLAMPING -----
297
- // Clamp the pitch between pitchAngleMin and pitchAngleMax so that with your JSON values
298
- // (minAngle: -90, maxAngle: 0) the allowed pitch is between -90 (overhead) and 0 (horizontal).
299
  OrbitCamera.prototype._clampPitchAngle = function (pitch) {
300
  return pc.math.clamp(pitch, this.pitchAngleMin, this.pitchAngleMax);
301
  };
@@ -305,220 +289,14 @@ OrbitCamera.prototype._clampYawAngle = function (yaw) {
305
  };
306
 
307
  OrbitCamera.quatWithoutYaw = new pc.Quat();
308
- OrbitCamera.yawOffset = new pc.Quat();
309
 
310
- // ----- REVISED PITCH CALCULATION -----
311
- // Modify _calcPitch so that horizontal (looking straight ahead) returns 0,
312
- // and looking overhead returns -90.
313
- // This change ensures that with JSON (minAngle: -90, maxAngle: 0) the allowed pitch is strictly between -90 and 0.
314
  OrbitCamera.prototype._calcPitch = function (quat, yaw) {
315
- var quatWithoutYaw = OrbitCamera.quatWithoutYaw;
316
- var yawOffset = OrbitCamera.yawOffset;
317
- yawOffset.setFromEulerAngles(0, -yaw, 0);
318
- quatWithoutYaw.mul2(yawOffset, quat);
319
- var transformedForward = new pc.Vec3();
320
- quatWithoutYaw.transformVector(pc.Vec3.FORWARD, transformedForward);
321
- // Here we swap the sign of the computed angle so that:
322
- // - When the camera is horizontal, transformedForward.y is 0 and the result is 0.
323
- // - When the camera is overhead, transformedForward.y is negative and the result is -90.
324
- return Math.atan2(-transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
325
- };
326
-
327
-
328
- ////////////////////////////////////////////////////////////////////////////////
329
- // Orbit Camera Mouse Input Script //
330
- ////////////////////////////////////////////////////////////////////////////////
331
- var OrbitCameraInputMouse = pc.createScript('orbitCameraInputMouse');
332
-
333
- OrbitCameraInputMouse.attributes.add('orbitSensitivity', {
334
- type: 'number',
335
- default: 0.3,
336
- title: 'Orbit Sensitivity',
337
- description: 'How fast the camera moves around the orbit. Higher is faster'
338
- });
339
- OrbitCameraInputMouse.attributes.add('distanceSensitivity', {
340
- type: 'number',
341
- default: 0.4,
342
- title: 'Distance Sensitivity',
343
- description: 'How fast the camera moves in and out. Higher is faster'
344
- });
345
- OrbitCameraInputMouse.prototype.initialize = function () {
346
- this.orbitCamera = this.entity.script.orbitCamera;
347
- if (this.orbitCamera) {
348
- var self = this;
349
- var onMouseOut = function (e) { self.onMouseOut(e); };
350
- this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
351
- this.app.mouse.on(pc.EVENT_MOUSEUP, this.onMouseUp, this);
352
- this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
353
- this.app.mouse.on(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
354
- window.addEventListener('mouseout', onMouseOut, false);
355
- this.on('destroy', function () {
356
- this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
357
- this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
358
- this.app.mouse.off(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
359
- this.app.mouse.off(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
360
- window.removeEventListener('mouseout', onMouseOut, false);
361
- });
362
- }
363
- this.app.mouse.disableContextMenu();
364
- this.lookButtonDown = false;
365
- this.panButtonDown = false;
366
- this.lastPoint = new pc.Vec2();
367
- };
368
- OrbitCameraInputMouse.fromWorldPoint = new pc.Vec3();
369
- OrbitCameraInputMouse.toWorldPoint = new pc.Vec3();
370
- OrbitCameraInputMouse.worldDiff = new pc.Vec3();
371
- OrbitCameraInputMouse.prototype.pan = function (screenPoint) {
372
- var fromWorldPoint = OrbitCameraInputMouse.fromWorldPoint;
373
- var toWorldPoint = OrbitCameraInputMouse.toWorldPoint;
374
- var worldDiff = OrbitCameraInputMouse.worldDiff;
375
- var camera = this.entity.camera;
376
- var distance = this.orbitCamera.distance;
377
- camera.screenToWorld(screenPoint.x, screenPoint.y, distance, fromWorldPoint);
378
- camera.screenToWorld(this.lastPoint.x, this.lastPoint.y, distance, toWorldPoint);
379
- worldDiff.sub2(toWorldPoint, fromWorldPoint);
380
- this.orbitCamera.pivotPoint.add(worldDiff);
381
- var pitchRadians = this.orbitCamera.pitch * pc.math.DEG_TO_RAD;
382
- var minPivotY = this.orbitCamera.distance * Math.sin(pitchRadians);
383
- if (this.orbitCamera.pivotPoint.y < minPivotY) {
384
- this.orbitCamera.pivotPoint.y = minPivotY;
385
- }
386
  };
387
- OrbitCameraInputMouse.prototype.onMouseDown = function (event) {
388
- switch (event.button) {
389
- case pc.MOUSEBUTTON_LEFT:
390
- this.panButtonDown = true;
391
- break;
392
- case pc.MOUSEBUTTON_MIDDLE:
393
- case pc.MOUSEBUTTON_RIGHT:
394
- this.lookButtonDown = true;
395
- break;
396
- }
397
- };
398
- OrbitCameraInputMouse.prototype.onMouseUp = function (event) {
399
- switch (event.button) {
400
- case pc.MOUSEBUTTON_LEFT:
401
- this.panButtonDown = false;
402
- break;
403
- case pc.MOUSEBUTTON_MIDDLE:
404
- case pc.MOUSEBUTTON_RIGHT:
405
- this.lookButtonDown = false;
406
- break;
407
- }
408
- };
409
- OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
410
- if (this.lookButtonDown) {
411
- this.orbitCamera.pitch -= event.dy * this.orbitSensitivity;
412
- this.orbitCamera.yaw -= event.dx * this.orbitSensitivity;
413
- } else if (this.panButtonDown) {
414
- this.pan(new pc.Vec2(event.x, event.y));
415
- }
416
- this.lastPoint.set(event.x, event.y);
417
- };
418
- OrbitCameraInputMouse.prototype.onMouseWheel = function (event) {
419
- if (this.entity.camera.projection === pc.PROJECTION_PERSPECTIVE) {
420
- this.orbitCamera.distance -= event.wheelDelta * this.distanceSensitivity * (this.orbitCamera.distance * 0.1);
421
- } else {
422
- this.orbitCamera.orthoHeight -= event.wheelDelta * this.distanceSensitivity * (this.orbitCamera.orthoHeight * 0.1);
423
- }
424
- event.event.preventDefault();
425
- };
426
- OrbitCameraInputMouse.prototype.onMouseOut = function (event) {
427
- this.lookButtonDown = false;
428
- this.panButtonDown = false;
429
- };
430
-
431
-
432
- ////////////////////////////////////////////////////////////////////////////////
433
- // Orbit Camera Touch Input Script //
434
- ////////////////////////////////////////////////////////////////////////////////
435
- var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
436
-
437
- OrbitCameraInputTouch.attributes.add('orbitSensitivity', {
438
- type: 'number',
439
- default: 0.6,
440
- title: 'Orbit Sensitivity',
441
- description: 'How fast the camera moves around the orbit. Higher is faster'
442
- });
443
- OrbitCameraInputTouch.attributes.add('distanceSensitivity', {
444
- type: 'number',
445
- default: 0.5,
446
- title: 'Distance Sensitivity',
447
- description: 'How fast the camera moves in and out. Higher is faster'
448
- });
449
- OrbitCameraInputTouch.prototype.initialize = function () {
450
- this.orbitCamera = this.entity.script.orbitCamera;
451
- this.lastTouchPoint = new pc.Vec2();
452
- this.lastPinchMidPoint = new pc.Vec2();
453
- this.lastPinchDistance = 0;
454
- if (this.orbitCamera && this.app.touch) {
455
- this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
456
- this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
457
- this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
458
- this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
459
- this.on('destroy', function () {
460
- this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
461
- this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
462
- this.app.touch.off(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
463
- this.app.touch.off(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
464
- });
465
- }
466
- };
467
- OrbitCameraInputTouch.prototype.getPinchDistance = function (pointA, pointB) {
468
- var dx = pointA.x - pointB.x;
469
- var dy = pointA.y - pointB.y;
470
- return Math.sqrt((dx * dx) + (dy * dy));
471
- };
472
- OrbitCameraInputTouch.prototype.calcMidPoint = function (pointA, pointB, result) {
473
- result.set(pointB.x - pointA.x, pointB.y - pointA.y);
474
- result.mulScalar(0.5);
475
- result.x += pointA.x;
476
- result.y += pointA.y;
477
- };
478
- OrbitCameraInputTouch.prototype.onTouchStartEndCancel = function (event) {
479
- var touches = event.touches;
480
- if (touches.length === 1) {
481
- this.lastTouchPoint.set(touches[0].x, touches[0].y);
482
- } else if (touches.length === 2) {
483
- this.lastPinchDistance = this.getPinchDistance(touches[0], touches[1]);
484
- this.calcMidPoint(touches[0], touches[1], this.lastPinchMidPoint);
485
- }
486
- };
487
- OrbitCameraInputTouch.fromWorldPoint = new pc.Vec3();
488
- OrbitCameraInputTouch.toWorldPoint = new pc.Vec3();
489
- OrbitCameraInputTouch.worldDiff = new pc.Vec3();
490
- OrbitCameraInputTouch.prototype.pan = function (midPoint) {
491
- var fromWorldPoint = OrbitCameraInputTouch.fromWorldPoint;
492
- var toWorldPoint = OrbitCameraInputTouch.toWorldPoint;
493
- var worldDiff = OrbitCameraInputTouch.worldDiff;
494
- var camera = this.entity.camera;
495
- var distance = this.orbitCamera.distance;
496
- camera.screenToWorld(midPoint.x, midPoint.y, distance, fromWorldPoint);
497
- camera.screenToWorld(this.lastPinchMidPoint.x, this.lastPinchMidPoint.y, distance, toWorldPoint);
498
- worldDiff.sub2(toWorldPoint, fromWorldPoint);
499
- this.orbitCamera.pivotPoint.add(worldDiff);
500
- var pitchRadians = this.orbitCamera.pitch * pc.math.DEG_TO_RAD;
501
- var minPivotY = this.orbitCamera.distance * Math.sin(pitchRadians);
502
- if (this.orbitCamera.pivotPoint.y < minPivotY) {
503
- this.orbitCamera.pivotPoint.y = minPivotY;
504
- }
505
- };
506
- OrbitCameraInputTouch.pinchMidPoint = new pc.Vec2();
507
- OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
508
- var pinchMidPoint = OrbitCameraInputTouch.pinchMidPoint;
509
- var touches = event.touches;
510
- if (touches.length === 1) {
511
- var touch = touches[0];
512
- this.orbitCamera.pitch -= (touch.y - this.lastTouchPoint.y) * this.orbitSensitivity;
513
- this.orbitCamera.yaw -= (touch.x - this.lastTouchPoint.x) * this.orbitSensitivity;
514
- this.lastTouchPoint.set(touch.x, touch.y);
515
- } else if (touches.length === 2) {
516
- var currentPinchDistance = this.getPinchDistance(touches[0], touches[1]);
517
- var diffInPinchDistance = currentPinchDistance - this.lastPinchDistance;
518
- this.lastPinchDistance = currentPinchDistance;
519
- this.orbitCamera.distance -= (diffInPinchDistance * this.distanceSensitivity * 0.1) * (this.orbitCamera.distance * 0.1);
520
- this.calcMidPoint(touches[0], touches[1], pinchMidPoint);
521
- this.pan(pinchMidPoint);
522
- this.lastPinchMidPoint.copy(pinchMidPoint);
523
- }
524
- };
 
1
+ // orbit-camera.js
2
  ///////////////////////////////////////////////////////////////////////////////
3
  // Orbit Camera Script //
4
  ////////////////////////////////////////////////////////////////////////////////
 
35
  description: 'Frames the entity or scene at the start of the application."'
36
  });
37
 
 
38
  // Property to get and set the distance between the pivot point and camera
39
  // Clamped between this.distanceMin and this.distanceMax
40
  Object.defineProperty(OrbitCamera.prototype, 'distance', {
 
56
  }
57
  });
58
 
 
59
  // Property to get and set the pitch (in degrees) of the camera around the pivot.
60
  // The pitch value is clamped between pitchAngleMin and pitchAngleMax.
 
 
61
  Object.defineProperty(OrbitCamera.prototype, 'pitch', {
62
  get: function () {
63
  return this._targetPitch;
 
67
  }
68
  });
69
 
 
70
  // Property to get and set the yaw (in degrees) of the camera around the pivot.
71
  Object.defineProperty(OrbitCamera.prototype, 'yaw', {
72
  get: function () {
 
77
  }
78
  });
79
 
 
80
  // Property to get and set the world position of the pivot point that the camera orbits around.
81
  Object.defineProperty(OrbitCamera.prototype, 'pivotPoint', {
82
  get: function () {
 
87
  }
88
  });
89
 
 
90
  // Moves the camera to look at an entity and all its children so they are all in view.
91
  OrbitCamera.prototype.focus = function (focusEntity) {
 
92
  this._buildAabb(focusEntity);
93
  var halfExtents = this._modelsAabb.halfExtents;
94
  var radius = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
 
141
  this._updatePosition();
142
  };
143
 
 
144
  ////////////////////////////////////////////////////////////////////////////////
145
  // Private Methods //
146
  ////////////////////////////////////////////////////////////////////////////////
 
160
  this._pivotPoint.copy(this._modelsAabb.center);
161
  var cameraQuat = this.entity.getRotation();
162
  this._yaw = this._calcYaw(cameraQuat);
 
163
  this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
164
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
165
  this._distance = 0;
 
176
 
177
  this._targetDistance = this._distance;
178
 
179
+ this.on('attr:distanceMin', function () { this._distance = this._clampDistance(this._distance); });
180
+ this.on('attr:distanceMax', function () { this._distance = this._clampDistance(this._distance); });
181
+ this.on('attr:pitchAngleMin', function () { this._pitch = this._clampPitchAngle(this._pitch); });
182
+ this.on('attr:pitchAngleMax', function () { this._pitch = this._clampPitchAngle(this._pitch); });
183
+ this.on('attr:focusEntity', function (value) {
 
 
 
 
 
 
 
 
184
  if (this.frameOnStart) {
185
  this.focus(value || this.app.root);
186
  } else {
187
  this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root);
188
  }
189
  });
190
+ this.on('attr:frameOnStart', function (value) {
191
  if (value) {
192
  this.focus(this.focusEntity || this.app.root);
193
  }
 
198
  };
199
 
200
  OrbitCamera.prototype.update = function (dt) {
201
+ // inertia/lerp step
202
  var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
203
  this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
204
+ this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
205
+ this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);
206
+
207
+ // new: clamp so world Y never < minY when orbiting downward
208
+ var rad = this._pitch * pc.math.DEG_TO_RAD;
209
+ var candY = this._pivotPoint.y + Math.sin(rad) * this._distance;
210
+ if (candY < this.minY) {
211
+ // compute the safe pitch angle so that world Y == minY
212
+ var safeRad = Math.asin((this.minY - this._pivotPoint.y) / this._distance);
213
+ var safeDeg = safeRad * pc.math.RAD_TO_DEG;
214
+ this._pitch = safeDeg;
215
+ this._targetPitch = safeDeg;
216
+ }
217
+
218
  this._updatePosition();
219
  };
220
 
 
225
  position.copy(this.entity.forward);
226
  position.mulScalar(-this._distance);
227
  position.add(this.pivotPoint);
228
+ // ensure no under-floor
229
  position.y = Math.max(position.y, this.minY);
230
  this.entity.setPosition(position);
231
  };
232
 
233
  OrbitCamera.prototype._removeInertia = function () {
234
+ this._yaw = this._targetYaw;
235
+ this._pitch = this._targetPitch;
236
  this._distance = this._targetDistance;
237
  };
238
 
239
  OrbitCamera.prototype._checkAspectRatio = function () {
240
  var height = this.app.graphicsDevice.height;
241
+ var width = this.app.graphicsDevice.width;
242
  this.entity.camera.horizontalFov = (height > width);
243
  };
244
 
245
  OrbitCamera.prototype._buildAabb = function (entity) {
246
+ var meshInstances = [];
247
  var renders = entity.findComponents('render');
248
+ for (var i = 0; i < renders.length; i++) {
249
+ for (var m = 0; m < renders[i].meshInstances.length; m++) {
250
+ meshInstances.push(renders[i].meshInstances[m]);
 
251
  }
252
  }
253
  var models = entity.findComponents('model');
254
  for (i = 0; i < models.length; i++) {
255
+ for (m = 0; m < models[i].meshInstances.length; m++) {
256
+ meshInstances.push(models[i].meshInstances[m]);
 
257
  }
258
  }
259
  var gsplats = entity.findComponents('gsplat');
260
  for (i = 0; i < gsplats.length; i++) {
261
+ var inst = gsplats[i].instance;
262
+ if (inst && inst.meshInstance) meshInstances.push(inst.meshInstance);
 
 
 
263
  }
264
  for (i = 0; i < meshInstances.length; i++) {
265
+ if (i === 0) this._modelsAabb.copy(meshInstances[i].aabb);
266
+ else this._modelsAabb.add(meshInstances[i].aabb);
 
 
 
267
  }
268
  };
269
 
270
  OrbitCamera.prototype._calcYaw = function (quat) {
271
+ var forward = new pc.Vec3();
272
+ quat.transformVector(pc.Vec3.FORWARD, forward);
273
+ return Math.atan2(-forward.x, -forward.z) * pc.math.RAD_TO_DEG;
274
  };
275
 
276
  OrbitCamera.prototype._clampDistance = function (distance) {
 
280
  return Math.max(distance, this.distanceMin);
281
  };
282
 
 
 
 
 
283
  OrbitCamera.prototype._clampPitchAngle = function (pitch) {
284
  return pc.math.clamp(pitch, this.pitchAngleMin, this.pitchAngleMax);
285
  };
 
289
  };
290
 
291
  OrbitCamera.quatWithoutYaw = new pc.Quat();
292
+ OrbitCamera.yawOffset = new pc.Quat();
293
 
 
 
 
 
294
  OrbitCamera.prototype._calcPitch = function (quat, yaw) {
295
+ var quatNoYaw = OrbitCamera.quatWithoutYaw;
296
+ var yawOff = OrbitCamera.yawOffset;
297
+ yawOff.setFromEulerAngles(0, -yaw, 0);
298
+ quatNoYaw.mul2(yawOff, quat);
299
+ var forward = new pc.Vec3();
300
+ quatNoYaw.transformVector(pc.Vec3.FORWARD, forward);
301
+ return Math.atan2(-forward.y, -forward.z) * pc.math.RAD_TO_DEG;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  };