MikaFil commited on
Commit
40eec9e
·
verified ·
1 Parent(s): dff3670

Update orbit-camera.js

Browse files
Files changed (1) hide show
  1. orbit-camera.js +49 -35
orbit-camera.js CHANGED
@@ -4,7 +4,7 @@
4
  /*
5
  * PlayCanvas Orbit Camera for GSplat/SOGS/GLB viewers.
6
  * - Handles min/max zoom, pitch, yaw, minY (don't go below ground)
7
- * - Mouse & touch controls (orbit, pan, zoom)
8
  * - Attributes are auto-injected by viewer.js
9
  */
10
 
@@ -60,9 +60,11 @@ OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
60
  distance.sub2(lookAtPoint, resetPoint);
61
  this.distance = distance.length();
62
  this.pivotPoint.copy(lookAtPoint);
 
63
  var cameraQuat = this.entity.getRotation();
64
  this.yaw = this._calcYaw(cameraQuat);
65
  this.pitch = this._calcPitch(cameraQuat, this.yaw);
 
66
  this._removeInertia();
67
  this._updatePosition();
68
  };
@@ -83,12 +85,15 @@ OrbitCamera.prototype.resetToPosition = function (position, lookAtPoint) {
83
  this.entity.setPosition(position);
84
  this.entity.lookAt(lookAtPoint);
85
  this._pivotPoint.copy(lookAtPoint);
 
86
  var distanceVec = new pc.Vec3();
87
  distanceVec.sub2(position, lookAtPoint);
88
  this._targetDistance = this._distance = distanceVec.length();
 
89
  var cameraQuat = this.entity.getRotation();
90
  this._targetYaw = this._yaw = this._calcYaw(cameraQuat);
91
  this._targetPitch = this._pitch = this._calcPitch(cameraQuat, this._yaw);
 
92
  this._removeInertia();
93
  this._updatePosition();
94
  };
@@ -105,6 +110,7 @@ OrbitCamera.prototype.worldCameraYForPivot = function(pivot) {
105
 
106
  OrbitCamera.prototype.initialize = function () {
107
  var self = this;
 
108
  var onWindowResize = function () { self._checkAspectRatio(); };
109
  window.addEventListener('resize', onWindowResize, false);
110
  this._checkAspectRatio();
@@ -118,7 +124,7 @@ OrbitCamera.prototype.initialize = function () {
118
  this._pivotPoint.copy(this._modelsAabb.center);
119
 
120
  var cameraQuat = this.entity.getRotation();
121
- this._yaw = this._calcYaw(cameraQuat);
122
  this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
123
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
124
 
@@ -135,15 +141,15 @@ OrbitCamera.prototype.initialize = function () {
135
  }
136
  this._targetDistance = this._distance;
137
 
138
- this.on('attr:distanceMin', function (value, prev) { this._distance = this._clampDistance(this._distance); });
139
- this.on('attr:distanceMax', function (value, prev) { this._distance = this._clampDistance(this._distance); });
140
- this.on('attr:pitchAngleMin', function (value, prev) { this._pitch = this._clampPitchAngle(this._pitch); });
141
- this.on('attr:pitchAngleMax', function (value, prev) { this._pitch = this._clampPitchAngle(this._pitch); });
142
- this.on('attr:focusEntity', function (value, prev) {
143
  if (this.frameOnStart) { this.focus(value || this.app.root); }
144
  else { this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root); }
145
  });
146
- this.on('attr:frameOnStart', function (value, prev) {
147
  if (value) { this.focus(this.focusEntity || this.app.root); }
148
  });
149
 
@@ -163,6 +169,7 @@ OrbitCamera.prototype.update = function (dt) {
163
  OrbitCamera.prototype._updatePosition = function () {
164
  this.entity.setLocalPosition(0, 0, 0);
165
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
 
166
  var position = this.entity.getPosition();
167
  position.copy(this.entity.forward);
168
  position.mulScalar(-this._distance);
@@ -209,7 +216,7 @@ OrbitCamera.prototype._buildAabb = function (entity) {
209
  for (i = 0; i < gsplats.length; i++) {
210
  var gsplat = gsplats[i];
211
  var instance = gsplat.instance;
212
- if (instance?.meshInstance) {
213
  meshInstances.push(instance.meshInstance);
214
  }
215
  }
@@ -224,9 +231,10 @@ OrbitCamera.prototype._buildAabb = function (entity) {
224
  };
225
 
226
  OrbitCamera.prototype._calcYaw = function (quat) {
227
- var transformedForward = new pc.Vec3();
228
- quat.transformVector(pc.Vec3.FORWARD, transformedForward);
229
- return Math.atan2(-transformedForward.x, -transformedForward.z) * pc.math.DEG_TO_RAD * 180 / Math.PI;
 
230
  };
231
 
232
  OrbitCamera.prototype._clampDistance = function (distance) {
@@ -394,7 +402,7 @@ OrbitCameraInputMouse.prototype.onMouseWheel = function (event) {
394
  }
395
  event.event.preventDefault();
396
  };
397
- OrbitCameraInputMouse.prototype.onMouseOut = function (event) {
398
  this.lookButtonDown = false;
399
  this.panButtonDown = false;
400
  };
@@ -537,25 +545,30 @@ OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
537
  var OrbitCameraInputKeyboard = pc.createScript('orbitCameraInputKeyboard');
538
 
539
  /**
540
- * Réglages :
541
  * - orbitDegreesPerSecond : vitesse d’orbite (deg/s) pour yaw/pitch
542
- * - zoomFactorPerSecond : agressivité du zoom (facteur/s, proportionnel à la distance/orthoHeight)
543
- * - panUnitsPerSecond : vitesse de pan (en fraction de la distance/s)
544
  */
545
- OrbitCameraInputKeyboard.attributes.add('orbitDegreesPerSecond', { type: 'number', default: 90, title: 'Orbit Degrees / s' });
546
- OrbitCameraInputKeyboard.attributes.add('zoomFactorPerSecond', { type: 'number', default: 1.6, title: 'Zoom Factor / s' });
547
- OrbitCameraInputKeyboard.attributes.add('panUnitsPerSecond', { type: 'number', default: 0.8, title: 'Pan (distance frac) / s' });
548
 
549
  OrbitCameraInputKeyboard.prototype.initialize = function () {
550
  this.orbitCamera = this.entity.script.orbitCamera;
551
 
 
 
 
 
 
 
552
  var self = this;
553
  this._preventDefault = function (e) {
554
  var tag = (e.target && e.target.tagName) ? e.target.tagName.toUpperCase() : '';
555
  if (tag === 'INPUT' || tag === 'TEXTAREA' || e.isContentEditable) return;
556
-
557
- var code = e.key || '';
558
- if (code === 'ArrowUp' || code === 'ArrowDown' || code === 'ArrowLeft' || code === 'ArrowRight') {
559
  e.preventDefault();
560
  }
561
  };
@@ -566,9 +579,10 @@ OrbitCameraInputKeyboard.prototype.initialize = function () {
566
  });
567
  };
568
 
569
- // Teste si un pitch proposé ferait passer la caméra sous minY (même logique que souris/touch)
570
  OrbitCameraInputKeyboard.prototype._wouldGoBelowMinYWithPitch = function (proposedPitch) {
571
  var cam = this.orbitCamera;
 
572
  var currPitch = cam.pitch;
573
  var currYaw = cam.yaw;
574
  var currDist = cam.distance;
@@ -590,7 +604,7 @@ OrbitCameraInputKeyboard.prototype._wouldGoBelowMinYWithPitch = function (propos
590
  return { wouldGoBelow: proposedY < minY - 1e-4, preY: preY, proposedY: proposedY };
591
  };
592
 
593
- // Pan latéral (droite/gauche) avec respect de minY
594
  OrbitCameraInputKeyboard.prototype._panSideways = function (delta) {
595
  var cam = this.orbitCamera;
596
 
@@ -625,18 +639,18 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
625
 
626
  // --------- Avec SHIFT : zoom & pan latéral ----------
627
  if (shift) {
628
- // Zoom (⇧+↑ / ⇧+↓)
629
  if (kb.isPressed(pc.KEY_UP) || kb.isPressed(pc.KEY_DOWN)) {
630
  var k = this.zoomFactorPerSecond;
631
 
632
  if (this.entity.camera.projection === pc.PROJECTION_PERSPECTIVE) {
633
- var amount = k * dt * (cam.distance * 0.8);
634
- if (kb.isPressed(pc.KEY_UP)) cam.distance = cam.distance - amount;
635
- if (kb.isPressed(pc.KEY_DOWN)) cam.distance = cam.distance + amount;
636
  } else {
637
- var amountO = k * dt * (this.entity.camera.orthoHeight * 0.8);
638
- if (kb.isPressed(pc.KEY_UP)) this.entity.camera.orthoHeight -= amountO;
639
- if (kb.isPressed(pc.KEY_DOWN)) this.entity.camera.orthoHeight += amountO;
640
  }
641
  }
642
 
@@ -653,13 +667,13 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
653
  // --------- Sans SHIFT : orbite yaw/pitch ----------
654
  var orbitRate = this.orbitDegreesPerSecond * dt;
655
 
656
- // Pitch (↑/↓) — protège minY
657
  if (kb.isPressed(pc.KEY_UP) || kb.isPressed(pc.KEY_DOWN)) {
658
- var dir = kb.isPressed(pc.KEY_UP) ? -1 : 1; // ↑ = diminuer le pitch (monter l’orbite)
659
- var proposedPitch = cam.pitch + dir * orbitRate;
 
660
 
661
  var test = this._wouldGoBelowMinYWithPitch(proposedPitch);
662
-
663
  if (!(test.wouldGoBelow && (test.proposedY < test.preY))) {
664
  cam.pitch = proposedPitch;
665
  }
 
4
  /*
5
  * PlayCanvas Orbit Camera for GSplat/SOGS/GLB viewers.
6
  * - Handles min/max zoom, pitch, yaw, minY (don't go below ground)
7
+ * - Mouse, touch & keyboard controls (orbit, pan, zoom)
8
  * - Attributes are auto-injected by viewer.js
9
  */
10
 
 
60
  distance.sub2(lookAtPoint, resetPoint);
61
  this.distance = distance.length();
62
  this.pivotPoint.copy(lookAtPoint);
63
+
64
  var cameraQuat = this.entity.getRotation();
65
  this.yaw = this._calcYaw(cameraQuat);
66
  this.pitch = this._calcPitch(cameraQuat, this.yaw);
67
+
68
  this._removeInertia();
69
  this._updatePosition();
70
  };
 
85
  this.entity.setPosition(position);
86
  this.entity.lookAt(lookAtPoint);
87
  this._pivotPoint.copy(lookAtPoint);
88
+
89
  var distanceVec = new pc.Vec3();
90
  distanceVec.sub2(position, lookAtPoint);
91
  this._targetDistance = this._distance = distanceVec.length();
92
+
93
  var cameraQuat = this.entity.getRotation();
94
  this._targetYaw = this._yaw = this._calcYaw(cameraQuat);
95
  this._targetPitch = this._pitch = this._calcPitch(cameraQuat, this._yaw);
96
+
97
  this._removeInertia();
98
  this._updatePosition();
99
  };
 
110
 
111
  OrbitCamera.prototype.initialize = function () {
112
  var self = this;
113
+
114
  var onWindowResize = function () { self._checkAspectRatio(); };
115
  window.addEventListener('resize', onWindowResize, false);
116
  this._checkAspectRatio();
 
124
  this._pivotPoint.copy(this._modelsAabb.center);
125
 
126
  var cameraQuat = this.entity.getRotation();
127
+ this._yaw = this._calcYaw(cameraQuat); // degrees
128
  this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
129
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
130
 
 
141
  }
142
  this._targetDistance = this._distance;
143
 
144
+ this.on('attr:distanceMin', function () { this._distance = this._clampDistance(this._distance); });
145
+ this.on('attr:distanceMax', function () { this._distance = this._clampDistance(this._distance); });
146
+ this.on('attr:pitchAngleMin', function () { this._pitch = this._clampPitchAngle(this._pitch); });
147
+ this.on('attr:pitchAngleMax', function () { this._pitch = this._clampPitchAngle(this._pitch); });
148
+ this.on('attr:focusEntity', function (value) {
149
  if (this.frameOnStart) { this.focus(value || this.app.root); }
150
  else { this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root); }
151
  });
152
+ this.on('attr:frameOnStart', function (value) {
153
  if (value) { this.focus(this.focusEntity || this.app.root); }
154
  });
155
 
 
169
  OrbitCamera.prototype._updatePosition = function () {
170
  this.entity.setLocalPosition(0, 0, 0);
171
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
172
+
173
  var position = this.entity.getPosition();
174
  position.copy(this.entity.forward);
175
  position.mulScalar(-this._distance);
 
216
  for (i = 0; i < gsplats.length; i++) {
217
  var gsplat = gsplats[i];
218
  var instance = gsplat.instance;
219
+ if (instance && instance.meshInstance) {
220
  meshInstances.push(instance.meshInstance);
221
  }
222
  }
 
231
  };
232
 
233
  OrbitCamera.prototype._calcYaw = function (quat) {
234
+ // FIX: return degrees (was effectively radians before)
235
+ var f = new pc.Vec3();
236
+ quat.transformVector(pc.Vec3.FORWARD, f);
237
+ return Math.atan2(-f.x, -f.z) * pc.math.RAD_TO_DEG;
238
  };
239
 
240
  OrbitCamera.prototype._clampDistance = function (distance) {
 
402
  }
403
  event.event.preventDefault();
404
  };
405
+ OrbitCameraInputMouse.prototype.onMouseOut = function () {
406
  this.lookButtonDown = false;
407
  this.panButtonDown = false;
408
  };
 
545
  var OrbitCameraInputKeyboard = pc.createScript('orbitCameraInputKeyboard');
546
 
547
  /**
548
+ * Réglages clavier :
549
  * - orbitDegreesPerSecond : vitesse d’orbite (deg/s) pour yaw/pitch
550
+ * - zoomFactorPerSecond : facteur de zoom (proportionnel à la distance) par seconde
551
+ * - panUnitsPerSecond : vitesse de pan (en unités monde = distance * facteur) par seconde
552
  */
553
+ OrbitCameraInputKeyboard.attributes.add('orbitDegreesPerSecond', { type: 'number', default: 180, title: 'Orbit Degrees / s' });
554
+ OrbitCameraInputKeyboard.attributes.add('zoomFactorPerSecond', { type: 'number', default: 2.0, title: 'Zoom Factor / s' });
555
+ OrbitCameraInputKeyboard.attributes.add('panUnitsPerSecond', { type: 'number', default: 1.0, title: 'Pan (distance frac) / s' });
556
 
557
  OrbitCameraInputKeyboard.prototype.initialize = function () {
558
  this.orbitCamera = this.entity.script.orbitCamera;
559
 
560
+ // S’assure que le clavier est bien attaché (certains viewers ne l’initialisent pas)
561
+ if (!this.app.keyboard) {
562
+ this.app.keyboard = new pc.Keyboard(window);
563
+ }
564
+
565
+ // Empêche le scroll de la page avec les flèches quand la scène a le focus
566
  var self = this;
567
  this._preventDefault = function (e) {
568
  var tag = (e.target && e.target.tagName) ? e.target.tagName.toUpperCase() : '';
569
  if (tag === 'INPUT' || tag === 'TEXTAREA' || e.isContentEditable) return;
570
+ var key = e.key || '';
571
+ if (key === 'ArrowUp' || key === 'ArrowDown' || key === 'ArrowLeft' || key === 'ArrowRight') {
 
572
  e.preventDefault();
573
  }
574
  };
 
579
  });
580
  };
581
 
582
+ // Teste si un pitch proposé ferait passer la caméra sous minY (même logique que la souris)
583
  OrbitCameraInputKeyboard.prototype._wouldGoBelowMinYWithPitch = function (proposedPitch) {
584
  var cam = this.orbitCamera;
585
+
586
  var currPitch = cam.pitch;
587
  var currYaw = cam.yaw;
588
  var currDist = cam.distance;
 
604
  return { wouldGoBelow: proposedY < minY - 1e-4, preY: preY, proposedY: proposedY };
605
  };
606
 
607
+ // Pan latéral (⇧ + ←/→) avec respect de minY
608
  OrbitCameraInputKeyboard.prototype._panSideways = function (delta) {
609
  var cam = this.orbitCamera;
610
 
 
639
 
640
  // --------- Avec SHIFT : zoom & pan latéral ----------
641
  if (shift) {
642
+ // Zoom (⇧+↑ / ⇧+↓) — proportionnel à la distance / orthoHeight
643
  if (kb.isPressed(pc.KEY_UP) || kb.isPressed(pc.KEY_DOWN)) {
644
  var k = this.zoomFactorPerSecond;
645
 
646
  if (this.entity.camera.projection === pc.PROJECTION_PERSPECTIVE) {
647
+ var dz = k * dt * (cam.distance * 0.8);
648
+ if (kb.isPressed(pc.KEY_UP)) cam.distance = cam.distance - dz;
649
+ if (kb.isPressed(pc.KEY_DOWN)) cam.distance = cam.distance + dz;
650
  } else {
651
+ var dh = k * dt * (this.entity.camera.orthoHeight * 0.8);
652
+ if (kb.isPressed(pc.KEY_UP)) this.entity.camera.orthoHeight -= dh;
653
+ if (kb.isPressed(pc.KEY_DOWN)) this.entity.camera.orthoHeight += dh;
654
  }
655
  }
656
 
 
667
  // --------- Sans SHIFT : orbite yaw/pitch ----------
668
  var orbitRate = this.orbitDegreesPerSecond * dt;
669
 
670
+ // Pitch (↑/↓) — protège minY exactement comme la souris
671
  if (kb.isPressed(pc.KEY_UP) || kb.isPressed(pc.KEY_DOWN)) {
672
+ var dir = kb.isPressed(pc.KEY_UP) ? -1 : 1; // ↑ = réduire le pitch (orbiter vers le nord)
673
+ var currPitch = cam.pitch;
674
+ var proposedPitch = currPitch + dir * orbitRate;
675
 
676
  var test = this._wouldGoBelowMinYWithPitch(proposedPitch);
 
677
  if (!(test.wouldGoBelow && (test.proposedY < test.preY))) {
678
  cam.pitch = proposedPitch;
679
  }