MikaFil commited on
Commit
f2a61e1
·
verified ·
1 Parent(s): 0746f01

Update orbit-camera.js

Browse files
Files changed (1) hide show
  1. orbit-camera.js +183 -1
orbit-camera.js CHANGED
@@ -108,18 +108,24 @@ OrbitCamera.prototype.initialize = function () {
108
  var onWindowResize = function () { self._checkAspectRatio(); };
109
  window.addEventListener('resize', onWindowResize, false);
110
  this._checkAspectRatio();
 
111
  this._modelsAabb = new pc.BoundingBox();
112
  this._buildAabb(this.focusEntity || this.app.root);
 
113
  this.entity.lookAt(this._modelsAabb.center);
 
114
  this._pivotPoint = new pc.Vec3();
115
  this._pivotPoint.copy(this._modelsAabb.center);
 
116
  var cameraQuat = this.entity.getRotation();
117
  this._yaw = this._calcYaw(cameraQuat);
118
  this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
119
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
 
120
  this._distance = 0;
121
  this._targetYaw = this._yaw;
122
  this._targetPitch = this._pitch;
 
123
  if (this.frameOnStart) {
124
  this.focus(this.focusEntity || this.app.root);
125
  } else {
@@ -128,6 +134,7 @@ OrbitCamera.prototype.initialize = function () {
128
  this._distance = this._clampDistance(distanceBetween.length());
129
  }
130
  this._targetDistance = this._distance;
 
131
  this.on('attr:distanceMin', function (value, prev) { this._distance = this._clampDistance(this._distance); });
132
  this.on('attr:distanceMax', function (value, prev) { this._distance = this._clampDistance(this._distance); });
133
  this.on('attr:pitchAngleMin', function (value, prev) { this._pitch = this._clampPitchAngle(this._pitch); });
@@ -139,7 +146,10 @@ OrbitCamera.prototype.initialize = function () {
139
  this.on('attr:frameOnStart', function (value, prev) {
140
  if (value) { this.focus(this.focusEntity || this.app.root); }
141
  });
142
- this.on('destroy', function () { window.removeEventListener('resize', onWindowResize, false); });
 
 
 
143
  };
144
 
145
  OrbitCamera.prototype.update = function (dt) {
@@ -157,8 +167,10 @@ OrbitCamera.prototype._updatePosition = function () {
157
  position.copy(this.entity.forward);
158
  position.mulScalar(-this._distance);
159
  position.add(this.pivotPoint);
 
160
  // Clamp camera's Y position so it never goes below minY
161
  position.y = Math.max(position.y, this.minY);
 
162
  this.entity.setPosition(position);
163
  };
164
 
@@ -176,6 +188,7 @@ OrbitCamera.prototype._checkAspectRatio = function () {
176
 
177
  OrbitCamera.prototype._buildAabb = function (entity) {
178
  var i, m, meshInstances = [];
 
179
  var renders = entity.findComponents('render');
180
  for (i = 0; i < renders.length; i++) {
181
  var render = renders[i];
@@ -183,6 +196,7 @@ OrbitCamera.prototype._buildAabb = function (entity) {
183
  meshInstances.push(render.meshInstances[m]);
184
  }
185
  }
 
186
  var models = entity.findComponents('model');
187
  for (i = 0; i < models.length; i++) {
188
  var model = models[i];
@@ -190,6 +204,7 @@ OrbitCamera.prototype._buildAabb = function (entity) {
190
  meshInstances.push(model.meshInstances[m]);
191
  }
192
  }
 
193
  var gsplats = entity.findComponents('gsplat');
194
  for (i = 0; i < gsplats.length; i++) {
195
  var gsplat = gsplats[i];
@@ -198,6 +213,7 @@ OrbitCamera.prototype._buildAabb = function (entity) {
198
  meshInstances.push(instance.meshInstance);
199
  }
200
  }
 
201
  for (i = 0; i < meshInstances.length; i++) {
202
  if (i === 0) {
203
  this._modelsAabb.copy(meshInstances[i].aabb);
@@ -234,10 +250,13 @@ OrbitCamera.yawOffset = new pc.Quat();
234
  OrbitCamera.prototype._calcPitch = function (quat, yaw) {
235
  var quatWithoutYaw = OrbitCamera.quatWithoutYaw;
236
  var yawOffset = OrbitCamera.yawOffset;
 
237
  yawOffset.setFromEulerAngles(0, -yaw, 0);
238
  quatWithoutYaw.mul2(yawOffset, quat);
 
239
  var transformedForward = new pc.Vec3();
240
  quatWithoutYaw.transformVector(pc.Vec3.FORWARD, transformedForward);
 
241
  return Math.atan2(-transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
242
  };
243
 
@@ -249,14 +268,18 @@ OrbitCameraInputMouse.attributes.add('distanceSensitivity',{ type: 'number', def
249
 
250
  OrbitCameraInputMouse.prototype.initialize = function () {
251
  this.orbitCamera = this.entity.script.orbitCamera;
 
252
  if (this.orbitCamera) {
253
  var self = this;
254
  var onMouseOut = function (e) { self.onMouseOut(e); };
 
255
  this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
256
  this.app.mouse.on(pc.EVENT_MOUSEUP, this.onMouseUp, this);
257
  this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
258
  this.app.mouse.on(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
 
259
  window.addEventListener('mouseout', onMouseOut, false);
 
260
  this.on('destroy', function () {
261
  this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
262
  this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
@@ -265,9 +288,12 @@ OrbitCameraInputMouse.prototype.initialize = function () {
265
  window.removeEventListener('mouseout', onMouseOut, false);
266
  });
267
  }
 
268
  this.app.mouse.disableContextMenu();
 
269
  this.lookButtonDown = false;
270
  this.panButtonDown = false;
 
271
  this.lastPoint = new pc.Vec2();
272
  };
273
  OrbitCameraInputMouse.fromWorldPoint = new pc.Vec3();
@@ -278,10 +304,13 @@ OrbitCameraInputMouse.prototype.pan = function (screenPoint) {
278
  var fromWorldPoint = OrbitCameraInputMouse.fromWorldPoint;
279
  var toWorldPoint = OrbitCameraInputMouse.toWorldPoint;
280
  var worldDiff = OrbitCameraInputMouse.worldDiff;
 
281
  var camera = this.entity.camera;
282
  var distance = this.orbitCamera.distance;
 
283
  camera.screenToWorld(screenPoint.x, screenPoint.y, distance, fromWorldPoint);
284
  camera.screenToWorld(this.lastPoint.x, this.lastPoint.y, distance, toWorldPoint);
 
285
  worldDiff.sub2(toWorldPoint, fromWorldPoint);
286
 
287
  // Clamp so camera never goes below minY
@@ -343,6 +372,7 @@ OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
343
 
344
  var minY = this.orbitCamera.minY;
345
  var wouldGoBelow = proposedY < minY - 1e-4;
 
346
  if (wouldGoBelow && (proposedY < preY)) {
347
  this.orbitCamera.yaw = currYaw - deltaYaw;
348
  } else {
@@ -352,6 +382,7 @@ OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
352
  } else if (this.panButtonDown) {
353
  this.pan(new pc.Vec2(event.x, event.y));
354
  }
 
355
  this.lastPoint.set(event.x, event.y);
356
  };
357
 
@@ -372,16 +403,20 @@ OrbitCameraInputMouse.prototype.onMouseOut = function (event) {
372
  var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
373
  OrbitCameraInputTouch.attributes.add('orbitSensitivity', { type: 'number', default: 0.6, title: 'Orbit Sensitivity' });
374
  OrbitCameraInputTouch.attributes.add('distanceSensitivity', { type: 'number', default: 0.5, title: 'Distance Sensitivity' });
 
375
  OrbitCameraInputTouch.prototype.initialize = function () {
376
  this.orbitCamera = this.entity.script.orbitCamera;
 
377
  this.lastTouchPoint = new pc.Vec2();
378
  this.lastPinchMidPoint = new pc.Vec2();
379
  this.lastPinchDistance = 0;
 
380
  if (this.orbitCamera && this.app.touch) {
381
  this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
382
  this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
383
  this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
384
  this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
 
385
  this.on('destroy', function () {
386
  this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
387
  this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
@@ -413,14 +448,18 @@ OrbitCameraInputTouch.prototype.onTouchStartEndCancel = function (event) {
413
  OrbitCameraInputTouch.fromWorldPoint = new pc.Vec3();
414
  OrbitCameraInputTouch.toWorldPoint = new pc.Vec3();
415
  OrbitCameraInputTouch.worldDiff = new pc.Vec3();
 
416
  OrbitCameraInputTouch.prototype.pan = function (midPoint) {
417
  var fromWorldPoint = OrbitCameraInputTouch.fromWorldPoint;
418
  var toWorldPoint = OrbitCameraInputTouch.toWorldPoint;
419
  var worldDiff = OrbitCameraInputTouch.worldDiff;
 
420
  var camera = this.entity.camera;
421
  var distance = this.orbitCamera.distance;
 
422
  camera.screenToWorld(midPoint.x, midPoint.y, distance, fromWorldPoint);
423
  camera.screenToWorld(this.lastPinchMidPoint.x, this.lastPinchMidPoint.y, distance, toWorldPoint);
 
424
  worldDiff.sub2(toWorldPoint, fromWorldPoint);
425
 
426
  var proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
@@ -444,8 +483,10 @@ OrbitCameraInputTouch.pinchMidPoint = new pc.Vec2();
444
  OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
445
  var pinchMidPoint = OrbitCameraInputTouch.pinchMidPoint;
446
  var touches = event.touches;
 
447
  if (touches.length === 1) {
448
  var touch = touches[0];
 
449
  var sens = this.orbitSensitivity;
450
  var deltaPitch = (touch.y - this.lastTouchPoint.y) * sens;
451
  var deltaYaw = (touch.x - this.lastTouchPoint.x) * sens;
@@ -470,20 +511,161 @@ OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
470
 
471
  var minY = this.orbitCamera.minY;
472
  var wouldGoBelow = proposedY < minY - 1e-4;
 
473
  if (wouldGoBelow && (proposedY < preY)) {
474
  this.orbitCamera.yaw = currYaw - deltaYaw;
475
  } else {
476
  this.orbitCamera.pitch = proposedPitch;
477
  this.orbitCamera.yaw = currYaw - deltaYaw;
478
  }
 
479
  this.lastTouchPoint.set(touch.x, touch.y);
480
  } else if (touches.length === 2) {
481
  var currentPinchDistance = this.getPinchDistance(touches[0], touches[1]);
482
  var diffInPinchDistance = currentPinchDistance - this.lastPinchDistance;
483
  this.lastPinchDistance = currentPinchDistance;
 
484
  this.orbitCamera.distance -= (diffInPinchDistance * this.distanceSensitivity * 0.1) * (this.orbitCamera.distance * 0.1);
 
485
  this.calcMidPoint(touches[0], touches[1], pinchMidPoint);
486
  this.pan(pinchMidPoint);
487
  this.lastPinchMidPoint.copy(pinchMidPoint);
488
  }
489
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  var onWindowResize = function () { self._checkAspectRatio(); };
109
  window.addEventListener('resize', onWindowResize, false);
110
  this._checkAspectRatio();
111
+
112
  this._modelsAabb = new pc.BoundingBox();
113
  this._buildAabb(this.focusEntity || this.app.root);
114
+
115
  this.entity.lookAt(this._modelsAabb.center);
116
+
117
  this._pivotPoint = new pc.Vec3();
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
+
125
  this._distance = 0;
126
  this._targetYaw = this._yaw;
127
  this._targetPitch = this._pitch;
128
+
129
  if (this.frameOnStart) {
130
  this.focus(this.focusEntity || this.app.root);
131
  } else {
 
134
  this._distance = this._clampDistance(distanceBetween.length());
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); });
 
146
  this.on('attr:frameOnStart', function (value, prev) {
147
  if (value) { this.focus(this.focusEntity || this.app.root); }
148
  });
149
+
150
+ this.on('destroy', function () {
151
+ window.removeEventListener('resize', onWindowResize, false);
152
+ });
153
  };
154
 
155
  OrbitCamera.prototype.update = function (dt) {
 
167
  position.copy(this.entity.forward);
168
  position.mulScalar(-this._distance);
169
  position.add(this.pivotPoint);
170
+
171
  // Clamp camera's Y position so it never goes below minY
172
  position.y = Math.max(position.y, this.minY);
173
+
174
  this.entity.setPosition(position);
175
  };
176
 
 
188
 
189
  OrbitCamera.prototype._buildAabb = function (entity) {
190
  var i, m, meshInstances = [];
191
+
192
  var renders = entity.findComponents('render');
193
  for (i = 0; i < renders.length; i++) {
194
  var render = renders[i];
 
196
  meshInstances.push(render.meshInstances[m]);
197
  }
198
  }
199
+
200
  var models = entity.findComponents('model');
201
  for (i = 0; i < models.length; i++) {
202
  var model = models[i];
 
204
  meshInstances.push(model.meshInstances[m]);
205
  }
206
  }
207
+
208
  var gsplats = entity.findComponents('gsplat');
209
  for (i = 0; i < gsplats.length; i++) {
210
  var gsplat = gsplats[i];
 
213
  meshInstances.push(instance.meshInstance);
214
  }
215
  }
216
+
217
  for (i = 0; i < meshInstances.length; i++) {
218
  if (i === 0) {
219
  this._modelsAabb.copy(meshInstances[i].aabb);
 
250
  OrbitCamera.prototype._calcPitch = function (quat, yaw) {
251
  var quatWithoutYaw = OrbitCamera.quatWithoutYaw;
252
  var yawOffset = OrbitCamera.yawOffset;
253
+
254
  yawOffset.setFromEulerAngles(0, -yaw, 0);
255
  quatWithoutYaw.mul2(yawOffset, quat);
256
+
257
  var transformedForward = new pc.Vec3();
258
  quatWithoutYaw.transformVector(pc.Vec3.FORWARD, transformedForward);
259
+
260
  return Math.atan2(-transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
261
  };
262
 
 
268
 
269
  OrbitCameraInputMouse.prototype.initialize = function () {
270
  this.orbitCamera = this.entity.script.orbitCamera;
271
+
272
  if (this.orbitCamera) {
273
  var self = this;
274
  var onMouseOut = function (e) { self.onMouseOut(e); };
275
+
276
  this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
277
  this.app.mouse.on(pc.EVENT_MOUSEUP, this.onMouseUp, this);
278
  this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
279
  this.app.mouse.on(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
280
+
281
  window.addEventListener('mouseout', onMouseOut, false);
282
+
283
  this.on('destroy', function () {
284
  this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
285
  this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
 
288
  window.removeEventListener('mouseout', onMouseOut, false);
289
  });
290
  }
291
+
292
  this.app.mouse.disableContextMenu();
293
+
294
  this.lookButtonDown = false;
295
  this.panButtonDown = false;
296
+
297
  this.lastPoint = new pc.Vec2();
298
  };
299
  OrbitCameraInputMouse.fromWorldPoint = new pc.Vec3();
 
304
  var fromWorldPoint = OrbitCameraInputMouse.fromWorldPoint;
305
  var toWorldPoint = OrbitCameraInputMouse.toWorldPoint;
306
  var worldDiff = OrbitCameraInputMouse.worldDiff;
307
+
308
  var camera = this.entity.camera;
309
  var distance = this.orbitCamera.distance;
310
+
311
  camera.screenToWorld(screenPoint.x, screenPoint.y, distance, fromWorldPoint);
312
  camera.screenToWorld(this.lastPoint.x, this.lastPoint.y, distance, toWorldPoint);
313
+
314
  worldDiff.sub2(toWorldPoint, fromWorldPoint);
315
 
316
  // Clamp so camera never goes below minY
 
372
 
373
  var minY = this.orbitCamera.minY;
374
  var wouldGoBelow = proposedY < minY - 1e-4;
375
+
376
  if (wouldGoBelow && (proposedY < preY)) {
377
  this.orbitCamera.yaw = currYaw - deltaYaw;
378
  } else {
 
382
  } else if (this.panButtonDown) {
383
  this.pan(new pc.Vec2(event.x, event.y));
384
  }
385
+
386
  this.lastPoint.set(event.x, event.y);
387
  };
388
 
 
403
  var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
404
  OrbitCameraInputTouch.attributes.add('orbitSensitivity', { type: 'number', default: 0.6, title: 'Orbit Sensitivity' });
405
  OrbitCameraInputTouch.attributes.add('distanceSensitivity', { type: 'number', default: 0.5, title: 'Distance Sensitivity' });
406
+
407
  OrbitCameraInputTouch.prototype.initialize = function () {
408
  this.orbitCamera = this.entity.script.orbitCamera;
409
+
410
  this.lastTouchPoint = new pc.Vec2();
411
  this.lastPinchMidPoint = new pc.Vec2();
412
  this.lastPinchDistance = 0;
413
+
414
  if (this.orbitCamera && this.app.touch) {
415
  this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
416
  this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
417
  this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
418
  this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
419
+
420
  this.on('destroy', function () {
421
  this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
422
  this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
 
448
  OrbitCameraInputTouch.fromWorldPoint = new pc.Vec3();
449
  OrbitCameraInputTouch.toWorldPoint = new pc.Vec3();
450
  OrbitCameraInputTouch.worldDiff = new pc.Vec3();
451
+
452
  OrbitCameraInputTouch.prototype.pan = function (midPoint) {
453
  var fromWorldPoint = OrbitCameraInputTouch.fromWorldPoint;
454
  var toWorldPoint = OrbitCameraInputTouch.toWorldPoint;
455
  var worldDiff = OrbitCameraInputTouch.worldDiff;
456
+
457
  var camera = this.entity.camera;
458
  var distance = this.orbitCamera.distance;
459
+
460
  camera.screenToWorld(midPoint.x, midPoint.y, distance, fromWorldPoint);
461
  camera.screenToWorld(this.lastPinchMidPoint.x, this.lastPinchMidPoint.y, distance, toWorldPoint);
462
+
463
  worldDiff.sub2(toWorldPoint, fromWorldPoint);
464
 
465
  var proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
 
483
  OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
484
  var pinchMidPoint = OrbitCameraInputTouch.pinchMidPoint;
485
  var touches = event.touches;
486
+
487
  if (touches.length === 1) {
488
  var touch = touches[0];
489
+
490
  var sens = this.orbitSensitivity;
491
  var deltaPitch = (touch.y - this.lastTouchPoint.y) * sens;
492
  var deltaYaw = (touch.x - this.lastTouchPoint.x) * sens;
 
511
 
512
  var minY = this.orbitCamera.minY;
513
  var wouldGoBelow = proposedY < minY - 1e-4;
514
+
515
  if (wouldGoBelow && (proposedY < preY)) {
516
  this.orbitCamera.yaw = currYaw - deltaYaw;
517
  } else {
518
  this.orbitCamera.pitch = proposedPitch;
519
  this.orbitCamera.yaw = currYaw - deltaYaw;
520
  }
521
+
522
  this.lastTouchPoint.set(touch.x, touch.y);
523
  } else if (touches.length === 2) {
524
  var currentPinchDistance = this.getPinchDistance(touches[0], touches[1]);
525
  var diffInPinchDistance = currentPinchDistance - this.lastPinchDistance;
526
  this.lastPinchDistance = currentPinchDistance;
527
+
528
  this.orbitCamera.distance -= (diffInPinchDistance * this.distanceSensitivity * 0.1) * (this.orbitCamera.distance * 0.1);
529
+
530
  this.calcMidPoint(touches[0], touches[1], pinchMidPoint);
531
  this.pan(pinchMidPoint);
532
  this.lastPinchMidPoint.copy(pinchMidPoint);
533
  }
534
  };
535
+
536
+ // =================== Orbit Camera Input Keyboard Script ========================
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
+ };
562
+ window.addEventListener('keydown', this._preventDefault, { passive: false });
563
+
564
+ this.on('destroy', function () {
565
+ window.removeEventListener('keydown', self._preventDefault, { passive: false });
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;
575
+ var pivot = cam.pivotPoint.clone();
576
+
577
+ var camQuat = new pc.Quat();
578
+ camQuat.setFromEulerAngles(currPitch, currYaw, 0);
579
+ var forward = new pc.Vec3();
580
+ camQuat.transformVector(pc.Vec3.FORWARD, forward);
581
+ var preY = pivot.y + (-forward.y) * currDist;
582
+
583
+ var testQuat = new pc.Quat();
584
+ testQuat.setFromEulerAngles(proposedPitch, currYaw, 0);
585
+ var testForward = new pc.Vec3();
586
+ testQuat.transformVector(pc.Vec3.FORWARD, testForward);
587
+ var proposedY = pivot.y + (-testForward.y) * currDist;
588
+
589
+ var minY = cam.minY;
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
+
597
+ var quat = new pc.Quat();
598
+ quat.setFromEulerAngles(cam.pitch, cam.yaw, 0);
599
+
600
+ var right = new pc.Vec3();
601
+ quat.transformVector(pc.Vec3.RIGHT, right);
602
+
603
+ var worldDiff = right.scale(delta);
604
+
605
+ var minY = cam.minY;
606
+ var proposedPivot = cam.pivotPoint.clone().add(worldDiff);
607
+ var resultingY = cam.worldCameraYForPivot(proposedPivot);
608
+
609
+ if (resultingY >= minY - 1e-4) {
610
+ cam.pivotPoint.add(worldDiff);
611
+ } else {
612
+ worldDiff.y = 0;
613
+ proposedPivot = cam.pivotPoint.clone().add(worldDiff);
614
+ resultingY = cam.worldCameraYForPivot(proposedPivot);
615
+ if (resultingY >= minY - 1e-4) cam.pivotPoint.add(worldDiff);
616
+ }
617
+ };
618
+
619
+ OrbitCameraInputKeyboard.prototype.update = function (dt) {
620
+ if (!this.orbitCamera || !this.app.keyboard) return;
621
+
622
+ var kb = this.app.keyboard;
623
+ var shift = kb.isPressed(pc.KEY_SHIFT);
624
+ var cam = this.orbitCamera;
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
+
643
+ // Pan latéral (⇧+← / ⇧+→)
644
+ if (kb.isPressed(pc.KEY_LEFT) || kb.isPressed(pc.KEY_RIGHT)) {
645
+ var speed = this.panUnitsPerSecond * cam.distance; // unités/s
646
+ var delta = speed * dt * (kb.isPressed(pc.KEY_LEFT) ? -1 : 1);
647
+ this._panSideways(delta);
648
+ }
649
+
650
+ return; // pas d’orbite quand SHIFT est enfoncé
651
+ }
652
+
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
+ }
666
+ }
667
+
668
+ // Yaw (←/→) — orbite horizontale
669
+ if (kb.isPressed(pc.KEY_LEFT)) cam.yaw = cam.yaw + orbitRate; // vers la gauche
670
+ if (kb.isPressed(pc.KEY_RIGHT)) cam.yaw = cam.yaw - orbitRate; // vers la droite
671
+ };