MikaFil commited on
Commit
ace0f0f
·
verified ·
1 Parent(s): 88db6ea

Update deplacement_dans_env/ctrl_camera_pr_env.js

Browse files
deplacement_dans_env/ctrl_camera_pr_env.js CHANGED
@@ -1,20 +1,36 @@
1
- // orbit-camera.js
 
 
 
 
 
2
 
3
  var OrbitCamera = pc.createScript('orbitCamera');
4
 
5
- // Camera attributes
6
- OrbitCamera.attributes.add('distanceMax', { type: 'number', default: 20, title: 'Distance Max' });
7
- OrbitCamera.attributes.add('distanceMin', { type: 'number', default: 1, title: 'Distance Min' });
8
- OrbitCamera.attributes.add('pitchAngleMax', { type: 'number', default: 90, title: 'Pitch Angle Max (degrees)' });
9
- OrbitCamera.attributes.add('pitchAngleMin', { type: 'number', default: 0, title: 'Pitch Angle Min (degrees)' });
10
- OrbitCamera.attributes.add('yawAngleMax', { type: 'number', default: 360, title: 'Yaw Angle Max (degrees)' });
11
- OrbitCamera.attributes.add('yawAngleMin', { type: 'number', default: -360, title: 'Yaw Angle Min (degrees)' });
12
- OrbitCamera.attributes.add('minY', { type: 'number', default: 0, title: 'Minimum Y', description: 'Minimum Y value for camera world position' });
13
 
14
- OrbitCamera.attributes.add('inertiaFactor', { type: 'number', default: 0.2, title: 'Inertia Factor' });
15
- OrbitCamera.attributes.add('focusEntity', { type: 'entity', title: 'Focus Entity' });
16
- OrbitCamera.attributes.add('frameOnStart', { type: 'boolean', default: true, title: 'Frame on Start' });
 
 
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  Object.defineProperty(OrbitCamera.prototype, 'distance', {
19
  get: function () { return this._targetDistance; },
20
  set: function (value) { this._targetDistance = this._clampDistance(value); }
@@ -40,28 +56,37 @@ Object.defineProperty(OrbitCamera.prototype, 'pivotPoint', {
40
  set: function (value) { this._pivotPoint.copy(value); }
41
  });
42
 
 
43
  OrbitCamera.prototype.focus = function (focusEntity) {
44
  this._buildAabb(focusEntity);
45
  var halfExtents = this._modelsAabb.halfExtents;
46
  var radius = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
 
 
47
  this.distance = (radius * 1.5) / Math.sin(0.5 * this.entity.camera.fov * pc.math.DEG_TO_RAD);
 
48
  this._removeInertia();
49
  this._pivotPoint.copy(this._modelsAabb.center);
 
50
  };
51
 
52
  OrbitCamera.distanceBetween = new pc.Vec3();
53
 
54
  OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
55
  this.pivotPoint.copy(lookAtPoint);
 
 
56
  this.entity.setPosition(resetPoint);
57
  this.entity.lookAt(lookAtPoint);
 
58
  var distance = OrbitCamera.distanceBetween;
59
  distance.sub2(lookAtPoint, resetPoint);
60
  this.distance = distance.length();
61
- this.pivotPoint.copy(lookAtPoint);
62
  var cameraQuat = this.entity.getRotation();
63
- this.yaw = this._calcYaw(cameraQuat);
64
  this.pitch = this._calcPitch(cameraQuat, this.yaw);
 
65
  this._removeInertia();
66
  this._updatePosition();
67
  };
@@ -82,12 +107,16 @@ OrbitCamera.prototype.resetToPosition = function (position, lookAtPoint) {
82
  this.entity.setPosition(position);
83
  this.entity.lookAt(lookAtPoint);
84
  this._pivotPoint.copy(lookAtPoint);
 
 
85
  var distanceVec = new pc.Vec3();
86
  distanceVec.sub2(position, lookAtPoint);
87
- this._targetDistance = this._distance = distanceVec.length();
 
88
  var cameraQuat = this.entity.getRotation();
89
- this._targetYaw = this._yaw = this._calcYaw(cameraQuat);
90
  this._targetPitch = this._pitch = this._calcPitch(cameraQuat, this._yaw);
 
91
  this._removeInertia();
92
  this._updatePosition();
93
  };
@@ -103,10 +132,7 @@ OrbitCamera.prototype.worldCameraYForPivot = function(pivot) {
103
  return camPos.y;
104
  };
105
 
106
- ////////////////////////////////////////////////////////////////////////////////
107
- // Private Methods //
108
- ////////////////////////////////////////////////////////////////////////////////
109
-
110
  OrbitCamera.prototype.initialize = function () {
111
  var self = this;
112
  var onWindowResize = function () { self._checkAspectRatio(); };
@@ -119,6 +145,7 @@ OrbitCamera.prototype.initialize = function () {
119
  this.entity.lookAt(this._modelsAabb.center);
120
  this._pivotPoint = new pc.Vec3();
121
  this._pivotPoint.copy(this._modelsAabb.center);
 
122
 
123
  var cameraQuat = this.entity.getRotation();
124
  this._yaw = this._calcYaw(cameraQuat);
@@ -138,8 +165,8 @@ OrbitCamera.prototype.initialize = function () {
138
  }
139
  this._targetDistance = this._distance;
140
 
141
- this.on('attr:distanceMin', function () { this._distance = this._clampDistance(this._distance); });
142
- this.on('attr:distanceMax', function () { this._distance = this._clampDistance(this._distance); });
143
  this.on('attr:pitchAngleMin', function () { this._pitch = this._clampPitchAngle(this._pitch); });
144
  this.on('attr:pitchAngleMax', function () { this._pitch = this._clampPitchAngle(this._pitch); });
145
 
@@ -152,14 +179,18 @@ OrbitCamera.prototype.initialize = function () {
152
  if (value) { this.focus(this.focusEntity || this.app.root); }
153
  });
154
 
 
 
 
 
155
  this.on('destroy', function () { window.removeEventListener('resize', onWindowResize, false); });
156
  };
157
 
158
  OrbitCamera.prototype.update = function (dt) {
159
  var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
160
  this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
161
- this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
162
- this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);
163
  this._updatePosition();
164
  };
165
 
@@ -172,10 +203,16 @@ OrbitCamera.prototype._updatePosition = function () {
172
  position.mulScalar(-this._distance);
173
  position.add(this.pivotPoint);
174
 
175
- // Clamp camera's Y position so it never goes below minY
176
  position.y = Math.max(position.y, this.minY);
177
 
 
 
 
178
  this.entity.setPosition(position);
 
 
 
179
  };
180
 
181
  OrbitCamera.prototype._removeInertia = function () {
@@ -186,7 +223,7 @@ OrbitCamera.prototype._removeInertia = function () {
186
 
187
  OrbitCamera.prototype._checkAspectRatio = function () {
188
  var height = this.app.graphicsDevice.height;
189
- var width = this.app.graphicsDevice.width;
190
  this.entity.camera.horizontalFov = (height > width);
191
  };
192
 
@@ -219,11 +256,8 @@ OrbitCamera.prototype._buildAabb = function (entity) {
219
  }
220
 
221
  for (i = 0; i < meshInstances.length; i++) {
222
- if (i === 0) {
223
- this._modelsAabb.copy(meshInstances[i].aabb);
224
- } else {
225
- this._modelsAabb.add(meshInstances[i].aabb);
226
- }
227
  }
228
  };
229
 
@@ -234,9 +268,7 @@ OrbitCamera.prototype._calcYaw = function (quat) {
234
  };
235
 
236
  OrbitCamera.prototype._clampDistance = function (distance) {
237
- if (this.distanceMax > 0) {
238
- return pc.math.clamp(distance, this.distanceMin, this.distanceMax);
239
- }
240
  return Math.max(distance, this.distanceMin);
241
  };
242
 
@@ -261,9 +293,28 @@ OrbitCamera.prototype._calcPitch = function (quat, yaw) {
261
  return Math.atan2(-transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
262
  };
263
 
264
- // ===================== Orbit Camera Input Mouse Script ========================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  var OrbitCameraInputMouse = pc.createScript('orbitCameraInputMouse');
266
- OrbitCameraInputMouse.attributes.add('orbitSensitivity', { type: 'number', default: 0.3, title: 'Orbit Sensitivity' });
267
  OrbitCameraInputMouse.attributes.add('distanceSensitivity', { type: 'number', default: 0.4, title: 'Distance Sensitivity' });
268
 
269
  OrbitCameraInputMouse.prototype.initialize = function () {
@@ -273,16 +324,16 @@ OrbitCameraInputMouse.prototype.initialize = function () {
273
  var self = this;
274
  var onMouseOut = function () { self.onMouseOut(); };
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
  window.addEventListener('mouseout', onMouseOut, false);
281
 
282
  this.on('destroy', function () {
283
- this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
284
- this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
285
- this.app.mouse.off(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
286
  this.app.mouse.off(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
287
  window.removeEventListener('mouseout', onMouseOut, false);
288
  });
@@ -291,58 +342,60 @@ OrbitCameraInputMouse.prototype.initialize = function () {
291
  this.app.mouse.disableContextMenu();
292
 
293
  this.lookButtonDown = false;
294
- this.panButtonDown = false;
295
  this.lastPoint = new pc.Vec2();
296
  };
297
 
298
  OrbitCameraInputMouse.fromWorldPoint = new pc.Vec3();
299
- OrbitCameraInputMouse.toWorldPoint = new pc.Vec3();
300
- OrbitCameraInputMouse.worldDiff = new pc.Vec3();
301
 
302
  OrbitCameraInputMouse.prototype.pan = function (screenPoint) {
303
  var fromWorldPoint = OrbitCameraInputMouse.fromWorldPoint;
304
- var toWorldPoint = OrbitCameraInputMouse.toWorldPoint;
305
- var worldDiff = OrbitCameraInputMouse.worldDiff;
306
 
307
- var camera = this.entity.camera;
308
  var distance = this.orbitCamera.distance;
309
 
310
- camera.screenToWorld(screenPoint.x, screenPoint.y, distance, fromWorldPoint);
311
  camera.screenToWorld(this.lastPoint.x, this.lastPoint.y, distance, toWorldPoint);
312
 
313
  worldDiff.sub2(toWorldPoint, fromWorldPoint);
314
 
315
- // Enforce minY like vertical/horizontal pan with arrows
316
  var proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
317
  var minY = this.orbitCamera.minY;
318
  var resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
319
 
320
  if (resultingY >= minY - 1e-4) {
321
- this.orbitCamera.pivotPoint.add(worldDiff);
 
322
  } else {
323
- // Try horizontal-only
324
  worldDiff.y = 0;
325
  proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
326
  resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
327
  if (resultingY >= minY - 1e-4) {
328
- this.orbitCamera.pivotPoint.add(worldDiff);
 
329
  }
330
  }
331
  };
332
 
333
  OrbitCameraInputMouse.prototype.onMouseDown = function (event) {
334
  switch (event.button) {
335
- case pc.MOUSEBUTTON_LEFT: this.panButtonDown = true; break;
336
  case pc.MOUSEBUTTON_MIDDLE:
337
- case pc.MOUSEBUTTON_RIGHT: this.lookButtonDown = true; break;
338
  }
339
  };
340
 
341
  OrbitCameraInputMouse.prototype.onMouseUp = function (event) {
342
  switch (event.button) {
343
- case pc.MOUSEBUTTON_LEFT: this.panButtonDown = false; break;
344
  case pc.MOUSEBUTTON_MIDDLE:
345
- case pc.MOUSEBUTTON_RIGHT: this.lookButtonDown = false; break;
346
  }
347
  };
348
 
@@ -351,11 +404,11 @@ OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
351
  var sens = this.orbitSensitivity;
352
 
353
  var deltaPitch = event.dy * sens; // mouse up (dy < 0) raises pitch
354
- var deltaYaw = event.dx * sens;
355
 
356
  var currPitch = this.orbitCamera.pitch;
357
- var currYaw = this.orbitCamera.yaw;
358
- var currDist = this.orbitCamera.distance;
359
  var currPivot = this.orbitCamera.pivotPoint.clone();
360
 
361
  var camQuat = new pc.Quat();
@@ -378,7 +431,7 @@ OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
378
  this.orbitCamera.yaw = currYaw - deltaYaw;
379
  } else {
380
  this.orbitCamera.pitch = proposedPitch;
381
- this.orbitCamera.yaw = currYaw - deltaYaw;
382
  }
383
  } else if (this.panButtonDown) {
384
  this.pan(new pc.Vec2(event.x, event.y));
@@ -398,31 +451,31 @@ OrbitCameraInputMouse.prototype.onMouseWheel = function (event) {
398
 
399
  OrbitCameraInputMouse.prototype.onMouseOut = function () {
400
  this.lookButtonDown = false;
401
- this.panButtonDown = false;
402
  };
403
 
404
- // =================== Orbit Camera Input Touch Script ========================
405
  var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
406
- OrbitCameraInputTouch.attributes.add('orbitSensitivity', { type: 'number', default: 0.6, title: 'Orbit Sensitivity' });
407
  OrbitCameraInputTouch.attributes.add('distanceSensitivity', { type: 'number', default: 0.5, title: 'Distance Sensitivity' });
408
 
409
  OrbitCameraInputTouch.prototype.initialize = function () {
410
  this.orbitCamera = this.entity.script.orbitCamera;
411
- this.lastTouchPoint = new pc.Vec2();
412
  this.lastPinchMidPoint = new pc.Vec2();
413
  this.lastPinchDistance = 0;
414
 
415
  if (this.orbitCamera && this.app.touch) {
416
- this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
417
- this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
418
  this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
419
- this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
420
 
421
  this.on('destroy', function () {
422
- this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
423
- this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
424
  this.app.touch.off(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
425
- this.app.touch.off(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
426
  });
427
  }
428
  };
@@ -451,15 +504,15 @@ OrbitCameraInputTouch.prototype.onTouchStartEndCancel = function (event) {
451
  };
452
 
453
  OrbitCameraInputTouch.fromWorldPoint = new pc.Vec3();
454
- OrbitCameraInputTouch.toWorldPoint = new pc.Vec3();
455
- OrbitCameraInputTouch.worldDiff = new pc.Vec3();
456
 
457
  OrbitCameraInputTouch.prototype.pan = function (midPoint) {
458
  var fromWorldPoint = OrbitCameraInputTouch.fromWorldPoint;
459
- var toWorldPoint = OrbitCameraInputTouch.toWorldPoint;
460
- var worldDiff = OrbitCameraInputTouch.worldDiff;
461
 
462
- var camera = this.entity.camera;
463
  var distance = this.orbitCamera.distance;
464
 
465
  camera.screenToWorld(midPoint.x, midPoint.y, distance, fromWorldPoint);
@@ -472,13 +525,15 @@ OrbitCameraInputTouch.prototype.pan = function (midPoint) {
472
  var resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
473
 
474
  if (resultingY >= minY - 1e-4) {
475
- this.orbitCamera.pivotPoint.add(worldDiff);
 
476
  } else {
477
  worldDiff.y = 0;
478
  proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
479
  resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
480
  if (resultingY >= minY - 1e-4) {
481
- this.orbitCamera.pivotPoint.add(worldDiff);
 
482
  }
483
  }
484
  };
@@ -494,11 +549,11 @@ OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
494
  var sens = this.orbitSensitivity;
495
 
496
  var deltaPitch = (touch.y - this.lastTouchPoint.y) * sens;
497
- var deltaYaw = (touch.x - this.lastTouchPoint.x) * sens;
498
 
499
  var currPitch = this.orbitCamera.pitch;
500
- var currYaw = this.orbitCamera.yaw;
501
- var currDist = this.orbitCamera.distance;
502
  var currPivot = this.orbitCamera.pivotPoint.clone();
503
 
504
  var camQuat = new pc.Quat();
@@ -521,14 +576,14 @@ OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
521
  this.orbitCamera.yaw = currYaw - deltaYaw;
522
  } else {
523
  this.orbitCamera.pitch = proposedPitch;
524
- this.orbitCamera.yaw = currYaw - deltaYaw;
525
  }
526
 
527
  this.lastTouchPoint.set(touch.x, touch.y);
528
  } else if (touches.length === 2) {
529
- var currentPinchDistance = this.getPinchDistance(touches[0], touches[1]);
530
- var diffInPinchDistance = currentPinchDistance - this.lastPinchDistance;
531
- this.lastPinchDistance = currentPinchDistance;
532
 
533
  this.orbitCamera.distance -= (diffInPinchDistance * this.distanceSensitivity * 0.1) * (this.orbitCamera.distance * 0.1);
534
 
@@ -539,13 +594,12 @@ OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
539
  };
540
 
541
  // =================== Orbit Camera Input Keyboard ========================
542
- // Mappages demandés :
543
- // - Flèches : déplacement avant/arrière & droite/gauche dans le repère de la caméra (3D)
544
- // - Ctrl+flèches : orbiter autour du pivot
545
- // - Maj+flèches : rotation libre sur place (pas de limites yaw/pitch)
546
  var OrbitCameraInputKeyboard = pc.createScript('orbitCameraInputKeyboard');
547
 
548
- // Vitesses de déplacement (relatives à la distance pour un ressenti constant)
549
  OrbitCameraInputKeyboard.attributes.add('forwardSpeed', { type: 'number', default: 1.8, title: 'Forward Speed (rel. to distance)' });
550
  OrbitCameraInputKeyboard.attributes.add('strafeSpeed', { type: 'number', default: 1.8, title: 'Strafe Speed (rel. to distance)' });
551
 
@@ -571,17 +625,16 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
571
  var shift = this.keyboard.isPressed(pc.KEY_SHIFT);
572
  var ctrl = this.keyboard.isPressed(pc.KEY_CONTROL);
573
 
574
- // -------- Ctrl + flèches : ORBITER autour du pivot (clamp actif) --------
575
  if (ctrl && (up || dn || lt || rt)) {
576
- var yawDir = (rt ? 1 : 0) - (lt ? 1 : 0); // droite = +1, gauche = -1
577
- var pitchDir = (up ? 1 : 0) - (dn ? 1 : 0); // haut = +1, bas = -1
578
 
579
  if (yawDir !== 0) {
580
  this.orbitCamera.yaw = this.orbitCamera.yaw + yawDir * this.orbitYawSpeedDeg * dt;
581
  }
582
 
583
  if (pitchDir !== 0) {
584
- // on laisse le clamp faire son travail via le setter
585
  var currPitch = this.orbitCamera.pitch;
586
  var currYaw = this.orbitCamera.yaw;
587
  var currDist = this.orbitCamera.distance;
@@ -592,7 +645,7 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
592
  var preY = currPivot.y + (-forward.y) * currDist;
593
 
594
  var testPitch = currPitch + pitchDir * this.orbitPitchSpeedDeg * dt;
595
- var testQuat = new pc.Quat().setFromEulerAngles(testPitch, currYaw, 0);
596
  var testForward = new pc.Vec3(); testQuat.transformVector(pc.Vec3.FORWARD, testForward);
597
  var proposedY = currPivot.y + (-testForward.y) * currDist;
598
 
@@ -606,16 +659,15 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
606
  return;
607
  }
608
 
609
- // -------- Maj + flèches : ROTATION SUR PLACE SANS LIMITE --------
610
  if (shift && (up || dn || lt || rt)) {
611
  var yawDirR = (rt ? 1 : 0) - (lt ? 1 : 0);
612
  var pitchDirR = (up ? 1 : 0) - (dn ? 1 : 0);
613
 
614
- // Position actuelle à préserver
615
  var camPos = this.entity.getPosition().clone();
616
- var dist = this.orbitCamera._distance;
617
 
618
- // *** Bypass des clamps: on écrit directement dans _yaw/_pitch et leurs cibles ***
619
  this.orbitCamera._yaw += yawDirR * this.rotateYawSpeedDeg * dt;
620
  this.orbitCamera._pitch += pitchDirR * this.rotatePitchSpeedDeg * dt;
621
  this.orbitCamera._targetYaw = this.orbitCamera._yaw;
@@ -625,9 +677,9 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
625
  var q = new pc.Quat().setFromEulerAngles(this.orbitCamera._pitch, this.orbitCamera._yaw, 0);
626
  var forward = new pc.Vec3(); q.transformVector(pc.Vec3.FORWARD, forward);
627
  var newPivot = camPos.clone().add(forward.clone().mulScalar(dist));
 
628
  this.orbitCamera._pivotPoint.copy(newPivot);
629
 
630
- // Pas d’inertie pour être exact tout de suite
631
  this.orbitCamera._removeInertia();
632
  this.orbitCamera._updatePosition();
633
  this.entity.setPosition(camPos);
@@ -635,15 +687,14 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
635
  return;
636
  }
637
 
638
- // -------- Flèches seules : DÉPLACEMENT DANS LE REPÈRE DE LA CAMÉRA --------
639
  var moveForward = (up ? 1 : 0) - (dn ? 1 : 0);
640
  var moveRight = (rt ? 1 : 0) - (lt ? 1 : 0);
641
  if (moveForward === 0 && moveRight === 0) return;
642
 
643
- // Vecteurs avant/droite "complets" (incluent Y) → mouvement 3D relatif caméra
644
  var fwd = this.entity.forward.clone();
645
  var right = this.entity.right.clone();
646
- if (fwd.lengthSq() > 1e-8) fwd.normalize();
647
  if (right.lengthSq() > 1e-8) right.normalize();
648
 
649
  var camDist = Math.max(0.1, this.orbitCamera._distance);
@@ -652,15 +703,17 @@ OrbitCameraInputKeyboard.prototype.update = function (dt) {
652
 
653
  var delta = new pc.Vec3();
654
  if (moveForward !== 0) delta.add(fwd.mulScalar(moveForward * speedF * dt));
655
- if (moveRight !== 0) delta.add(right.mulScalar(moveRight * speedR * dt));
656
 
657
  if (delta.lengthSq() > 0) {
658
- // Respecte minY : camY_new = camY_cur + delta.y (car yaw/pitch/distance inchangés)
659
  var currCamY = this.orbitCamera.worldCameraYForPivot(this.orbitCamera._pivotPoint);
660
  var minY = this.orbitCamera.minY;
661
  if (currCamY + delta.y < minY) {
662
- delta.y = minY - currCamY; // on limite juste ce qu'il faut
663
  }
664
- this.orbitCamera._pivotPoint.add(delta);
 
 
665
  }
666
  };
 
1
+ // ctrl_camera_pr_env.js
2
+ // ============================================================================
3
+ // ORBIT CAMERA + INPUTS (PlayCanvas) — version bbox + sans maxZoom
4
+ // - Contraintes : minY (altitude min caméra), Bounding Box (X/Y/Z min/max)
5
+ // - Zoom : seul un minimum est imposé (plus de maxZoom)
6
+ // ============================================================================
7
 
8
  var OrbitCamera = pc.createScript('orbitCamera');
9
 
10
+ // ======================== Attributs Caméra ===========================
11
+ OrbitCamera.attributes.add('distanceMin', { type: 'number', default: 1, title: 'Distance Min' });
 
 
 
 
 
 
12
 
13
+ OrbitCamera.attributes.add('pitchAngleMax', { type: 'number', default: 90, title: 'Pitch Angle Max (degrees)' });
14
+ OrbitCamera.attributes.add('pitchAngleMin', { type: 'number', default: 0, title: 'Pitch Angle Min (degrees)' });
15
+
16
+ OrbitCamera.attributes.add('yawAngleMax', { type: 'number', default: 360, title: 'Yaw Angle Max (degrees)' });
17
+ OrbitCamera.attributes.add('yawAngleMin', { type: 'number', default: -360,title: 'Yaw Angle Min (degrees)' });
18
 
19
+ OrbitCamera.attributes.add('minY', { type: 'number', default: 0, title: 'Minimum Y (camera world Y)' });
20
+
21
+ OrbitCamera.attributes.add('inertiaFactor', { type: 'number', default: 0.2, title: 'Inertia Factor' });
22
+ OrbitCamera.attributes.add('focusEntity', { type: 'entity', title: 'Focus Entity' });
23
+ OrbitCamera.attributes.add('frameOnStart', { type: 'boolean', default: true, title: 'Frame on Start' });
24
+
25
+ // ---- Bounding Box (active si Xmin < Xmax, etc.) ----
26
+ OrbitCamera.attributes.add('Xmin', { type: 'number', default: -Infinity, title: 'BBox Xmin' });
27
+ OrbitCamera.attributes.add('Xmax', { type: 'number', default: Infinity, title: 'BBox Xmax' });
28
+ OrbitCamera.attributes.add('Ymin', { type: 'number', default: -Infinity, title: 'BBox Ymin' });
29
+ OrbitCamera.attributes.add('Ymax', { type: 'number', default: Infinity, title: 'BBox Ymax' });
30
+ OrbitCamera.attributes.add('Zmin', { type: 'number', default: -Infinity, title: 'BBox Zmin' });
31
+ OrbitCamera.attributes.add('Zmax', { type: 'number', default: Infinity, title: 'BBox Zmax' });
32
+
33
+ // ======================== Propriétés ===========================
34
  Object.defineProperty(OrbitCamera.prototype, 'distance', {
35
  get: function () { return this._targetDistance; },
36
  set: function (value) { this._targetDistance = this._clampDistance(value); }
 
56
  set: function (value) { this._pivotPoint.copy(value); }
57
  });
58
 
59
+ // ======================== API Publique ===========================
60
  OrbitCamera.prototype.focus = function (focusEntity) {
61
  this._buildAabb(focusEntity);
62
  var halfExtents = this._modelsAabb.halfExtents;
63
  var radius = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
64
+
65
+ // calcule distance initiale (pas de maxZoom à respecter)
66
  this.distance = (radius * 1.5) / Math.sin(0.5 * this.entity.camera.fov * pc.math.DEG_TO_RAD);
67
+
68
  this._removeInertia();
69
  this._pivotPoint.copy(this._modelsAabb.center);
70
+ this._clampPivotToBBox(this._pivotPoint);
71
  };
72
 
73
  OrbitCamera.distanceBetween = new pc.Vec3();
74
 
75
  OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
76
  this.pivotPoint.copy(lookAtPoint);
77
+ this._clampPivotToBBox(this._pivotPoint);
78
+
79
  this.entity.setPosition(resetPoint);
80
  this.entity.lookAt(lookAtPoint);
81
+
82
  var distance = OrbitCamera.distanceBetween;
83
  distance.sub2(lookAtPoint, resetPoint);
84
  this.distance = distance.length();
85
+
86
  var cameraQuat = this.entity.getRotation();
87
+ this.yaw = this._calcYaw(cameraQuat);
88
  this.pitch = this._calcPitch(cameraQuat, this.yaw);
89
+
90
  this._removeInertia();
91
  this._updatePosition();
92
  };
 
107
  this.entity.setPosition(position);
108
  this.entity.lookAt(lookAtPoint);
109
  this._pivotPoint.copy(lookAtPoint);
110
+ this._clampPivotToBBox(this._pivotPoint);
111
+
112
  var distanceVec = new pc.Vec3();
113
  distanceVec.sub2(position, lookAtPoint);
114
+ this._targetDistance = this._distance = Math.max(this.distanceMin, distanceVec.length());
115
+
116
  var cameraQuat = this.entity.getRotation();
117
+ this._targetYaw = this._yaw = this._calcYaw(cameraQuat);
118
  this._targetPitch = this._pitch = this._calcPitch(cameraQuat, this._yaw);
119
+
120
  this._removeInertia();
121
  this._updatePosition();
122
  };
 
132
  return camPos.y;
133
  };
134
 
135
+ // ======================== Privé ===========================
 
 
 
136
  OrbitCamera.prototype.initialize = function () {
137
  var self = this;
138
  var onWindowResize = function () { self._checkAspectRatio(); };
 
145
  this.entity.lookAt(this._modelsAabb.center);
146
  this._pivotPoint = new pc.Vec3();
147
  this._pivotPoint.copy(this._modelsAabb.center);
148
+ this._clampPivotToBBox(this._pivotPoint);
149
 
150
  var cameraQuat = this.entity.getRotation();
151
  this._yaw = this._calcYaw(cameraQuat);
 
165
  }
166
  this._targetDistance = this._distance;
167
 
168
+ // Listeners d’attributs (note: pas de distanceMax)
169
+ this.on('attr:distanceMin', function () { this._distance = this._clampDistance(this._distance); });
170
  this.on('attr:pitchAngleMin', function () { this._pitch = this._clampPitchAngle(this._pitch); });
171
  this.on('attr:pitchAngleMax', function () { this._pitch = this._clampPitchAngle(this._pitch); });
172
 
 
179
  if (value) { this.focus(this.focusEntity || this.app.root); }
180
  });
181
 
182
+ // Clamp bbox dynamique
183
+ var bboxAttrs = ['Xmin','Xmax','Ymin','Ymax','Zmin','Zmax'];
184
+ bboxAttrs.forEach((a)=> this.on('attr:'+a, function(){ this._clampPivotToBBox(this._pivotPoint); this._updatePosition(); }));
185
+
186
  this.on('destroy', function () { window.removeEventListener('resize', onWindowResize, false); });
187
  };
188
 
189
  OrbitCamera.prototype.update = function (dt) {
190
  var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
191
  this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
192
+ this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
193
+ this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);
194
  this._updatePosition();
195
  };
196
 
 
203
  position.mulScalar(-this._distance);
204
  position.add(this.pivotPoint);
205
 
206
+ // 1) altitude mini
207
  position.y = Math.max(position.y, this.minY);
208
 
209
+ // 2) clamp BBox caméra
210
+ this._applyBoundingBox(position);
211
+
212
  this.entity.setPosition(position);
213
+
214
+ // 3) assure que le pivot reste dans la bbox
215
+ this._clampPivotToBBox(this._pivotPoint);
216
  };
217
 
218
  OrbitCamera.prototype._removeInertia = function () {
 
223
 
224
  OrbitCamera.prototype._checkAspectRatio = function () {
225
  var height = this.app.graphicsDevice.height;
226
+ var width = this.app.graphicsDevice.width;
227
  this.entity.camera.horizontalFov = (height > width);
228
  };
229
 
 
256
  }
257
 
258
  for (i = 0; i < meshInstances.length; i++) {
259
+ if (i === 0) this._modelsAabb.copy(meshInstances[i].aabb);
260
+ else this._modelsAabb.add(meshInstances[i].aabb);
 
 
 
261
  }
262
  };
263
 
 
268
  };
269
 
270
  OrbitCamera.prototype._clampDistance = function (distance) {
271
+ // Plus de distanceMax : uniquement un minimum
 
 
272
  return Math.max(distance, this.distanceMin);
273
  };
274
 
 
293
  return Math.atan2(-transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
294
  };
295
 
296
+ // -------- Helpers Bounding Box --------
297
+ OrbitCamera.prototype._bboxEnabled = function () {
298
+ return (this.Xmin < this.Xmax) && (this.Ymin < this.Ymax) && (this.Zmin < this.Zmax);
299
+ };
300
+
301
+ OrbitCamera.prototype._applyBoundingBox = function (v3 /* camera position */) {
302
+ if (!this._bboxEnabled()) return;
303
+ v3.x = pc.math.clamp(v3.x, this.Xmin, this.Xmax);
304
+ v3.y = pc.math.clamp(v3.y, this.Ymin, this.Ymax);
305
+ v3.z = pc.math.clamp(v3.z, this.Zmin, this.Zmax);
306
+ };
307
+
308
+ OrbitCamera.prototype._clampPivotToBBox = function (v3 /* pivot */) {
309
+ if (!this._bboxEnabled()) return;
310
+ v3.x = pc.math.clamp(v3.x, this.Xmin, this.Xmax);
311
+ v3.y = pc.math.clamp(v3.y, this.Ymin, this.Ymax);
312
+ v3.z = pc.math.clamp(v3.z, this.Zmin, this.Zmax);
313
+ };
314
+
315
+ // ===================== Orbit Camera Input Mouse ========================
316
  var OrbitCameraInputMouse = pc.createScript('orbitCameraInputMouse');
317
+ OrbitCameraInputMouse.attributes.add('orbitSensitivity', { type: 'number', default: 0.3, title: 'Orbit Sensitivity' });
318
  OrbitCameraInputMouse.attributes.add('distanceSensitivity', { type: 'number', default: 0.4, title: 'Distance Sensitivity' });
319
 
320
  OrbitCameraInputMouse.prototype.initialize = function () {
 
324
  var self = this;
325
  var onMouseOut = function () { self.onMouseOut(); };
326
 
327
+ this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
328
+ this.app.mouse.on(pc.EVENT_MOUSEUP, this.onMouseUp, this);
329
+ this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
330
  this.app.mouse.on(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
331
  window.addEventListener('mouseout', onMouseOut, false);
332
 
333
  this.on('destroy', function () {
334
+ this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
335
+ this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
336
+ this.app.mouse.off(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
337
  this.app.mouse.off(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
338
  window.removeEventListener('mouseout', onMouseOut, false);
339
  });
 
342
  this.app.mouse.disableContextMenu();
343
 
344
  this.lookButtonDown = false;
345
+ this.panButtonDown = false;
346
  this.lastPoint = new pc.Vec2();
347
  };
348
 
349
  OrbitCameraInputMouse.fromWorldPoint = new pc.Vec3();
350
+ OrbitCameraInputMouse.toWorldPoint = new pc.Vec3();
351
+ OrbitCameraInputMouse.worldDiff = new pc.Vec3();
352
 
353
  OrbitCameraInputMouse.prototype.pan = function (screenPoint) {
354
  var fromWorldPoint = OrbitCameraInputMouse.fromWorldPoint;
355
+ var toWorldPoint = OrbitCameraInputMouse.toWorldPoint;
356
+ var worldDiff = OrbitCameraInputMouse.worldDiff;
357
 
358
+ var camera = this.entity.camera;
359
  var distance = this.orbitCamera.distance;
360
 
361
+ camera.screenToWorld(screenPoint.x, this.lastPoint.y, distance, fromWorldPoint);
362
  camera.screenToWorld(this.lastPoint.x, this.lastPoint.y, distance, toWorldPoint);
363
 
364
  worldDiff.sub2(toWorldPoint, fromWorldPoint);
365
 
366
+ // Enforce minY + bbox via pivot
367
  var proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
368
  var minY = this.orbitCamera.minY;
369
  var resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
370
 
371
  if (resultingY >= minY - 1e-4) {
372
+ this.orbitCamera._clampPivotToBBox(proposedPivot);
373
+ this.orbitCamera.pivotPoint.copy(proposedPivot);
374
  } else {
375
+ // Horizontal-only si nécessaire
376
  worldDiff.y = 0;
377
  proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
378
  resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
379
  if (resultingY >= minY - 1e-4) {
380
+ this.orbitCamera._clampPivotToBBox(proposedPivot);
381
+ this.orbitCamera.pivotPoint.copy(proposedPivot);
382
  }
383
  }
384
  };
385
 
386
  OrbitCameraInputMouse.prototype.onMouseDown = function (event) {
387
  switch (event.button) {
388
+ case pc.MOUSEBUTTON_LEFT: this.panButtonDown = true; break;
389
  case pc.MOUSEBUTTON_MIDDLE:
390
+ case pc.MOUSEBUTTON_RIGHT: this.lookButtonDown = true; break;
391
  }
392
  };
393
 
394
  OrbitCameraInputMouse.prototype.onMouseUp = function (event) {
395
  switch (event.button) {
396
+ case pc.MOUSEBUTTON_LEFT: this.panButtonDown = false; break;
397
  case pc.MOUSEBUTTON_MIDDLE:
398
+ case pc.MOUSEBUTTON_RIGHT: this.lookButtonDown = false; break;
399
  }
400
  };
401
 
 
404
  var sens = this.orbitSensitivity;
405
 
406
  var deltaPitch = event.dy * sens; // mouse up (dy < 0) raises pitch
407
+ var deltaYaw = event.dx * sens;
408
 
409
  var currPitch = this.orbitCamera.pitch;
410
+ var currYaw = this.orbitCamera.yaw;
411
+ var currDist = this.orbitCamera.distance;
412
  var currPivot = this.orbitCamera.pivotPoint.clone();
413
 
414
  var camQuat = new pc.Quat();
 
431
  this.orbitCamera.yaw = currYaw - deltaYaw;
432
  } else {
433
  this.orbitCamera.pitch = proposedPitch;
434
+ this.orbitCamera.yaw = currYaw - deltaYaw;
435
  }
436
  } else if (this.panButtonDown) {
437
  this.pan(new pc.Vec2(event.x, event.y));
 
451
 
452
  OrbitCameraInputMouse.prototype.onMouseOut = function () {
453
  this.lookButtonDown = false;
454
+ this.panButtonDown = false;
455
  };
456
 
457
+ // =================== Orbit Camera Input Touch ========================
458
  var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
459
+ OrbitCameraInputTouch.attributes.add('orbitSensitivity', { type: 'number', default: 0.6, title: 'Orbit Sensitivity' });
460
  OrbitCameraInputTouch.attributes.add('distanceSensitivity', { type: 'number', default: 0.5, title: 'Distance Sensitivity' });
461
 
462
  OrbitCameraInputTouch.prototype.initialize = function () {
463
  this.orbitCamera = this.entity.script.orbitCamera;
464
+ this.lastTouchPoint = new pc.Vec2();
465
  this.lastPinchMidPoint = new pc.Vec2();
466
  this.lastPinchDistance = 0;
467
 
468
  if (this.orbitCamera && this.app.touch) {
469
+ this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
470
+ this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
471
  this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
472
+ this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
473
 
474
  this.on('destroy', function () {
475
+ this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
476
+ this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
477
  this.app.touch.off(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
478
+ this.app.touch.off(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
479
  });
480
  }
481
  };
 
504
  };
505
 
506
  OrbitCameraInputTouch.fromWorldPoint = new pc.Vec3();
507
+ OrbitCameraInputTouch.toWorldPoint = new pc.Vec3();
508
+ OrbitCameraInputTouch.worldDiff = new pc.Vec3();
509
 
510
  OrbitCameraInputTouch.prototype.pan = function (midPoint) {
511
  var fromWorldPoint = OrbitCameraInputTouch.fromWorldPoint;
512
+ var toWorldPoint = OrbitCameraInputTouch.toWorldPoint;
513
+ var worldDiff = OrbitCameraInputTouch.worldDiff;
514
 
515
+ var camera = this.entity.camera;
516
  var distance = this.orbitCamera.distance;
517
 
518
  camera.screenToWorld(midPoint.x, midPoint.y, distance, fromWorldPoint);
 
525
  var resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
526
 
527
  if (resultingY >= minY - 1e-4) {
528
+ this.orbitCamera._clampPivotToBBox(proposedPivot);
529
+ this.orbitCamera.pivotPoint.copy(proposedPivot);
530
  } else {
531
  worldDiff.y = 0;
532
  proposedPivot = this.orbitCamera.pivotPoint.clone().add(worldDiff);
533
  resultingY = this.orbitCamera.worldCameraYForPivot(proposedPivot);
534
  if (resultingY >= minY - 1e-4) {
535
+ this.orbitCamera._clampPivotToBBox(proposedPivot);
536
+ this.orbitCamera.pivotPoint.copy(proposedPivot);
537
  }
538
  }
539
  };
 
549
  var sens = this.orbitSensitivity;
550
 
551
  var deltaPitch = (touch.y - this.lastTouchPoint.y) * sens;
552
+ var deltaYaw = (touch.x - this.lastTouchPoint.x) * sens;
553
 
554
  var currPitch = this.orbitCamera.pitch;
555
+ var currYaw = this.orbitCamera.yaw;
556
+ var currDist = this.orbitCamera.distance;
557
  var currPivot = this.orbitCamera.pivotPoint.clone();
558
 
559
  var camQuat = new pc.Quat();
 
576
  this.orbitCamera.yaw = currYaw - deltaYaw;
577
  } else {
578
  this.orbitCamera.pitch = proposedPitch;
579
+ this.orbitCamera.yaw = currYaw - deltaYaw;
580
  }
581
 
582
  this.lastTouchPoint.set(touch.x, touch.y);
583
  } else if (touches.length === 2) {
584
+ var currentPinchDistance = this.getPinchDistance(touches[0], touches[1]);
585
+ var diffInPinchDistance = currentPinchDistance - this.lastPinchDistance;
586
+ this.lastPinchDistance = currentPinchDistance;
587
 
588
  this.orbitCamera.distance -= (diffInPinchDistance * this.distanceSensitivity * 0.1) * (this.orbitCamera.distance * 0.1);
589
 
 
594
  };
595
 
596
  // =================== Orbit Camera Input Keyboard ========================
597
+ // Flèches : déplacement avant/arrière & droite/gauche (repère caméra, 3D)
598
+ // Ctrl+flèches : orbiter autour du pivot
599
+ // Maj+flèches : rotation libre sur place (pas de limites yaw/pitch)
 
600
  var OrbitCameraInputKeyboard = pc.createScript('orbitCameraInputKeyboard');
601
 
602
+ // Vitesses (relatives à la distance)
603
  OrbitCameraInputKeyboard.attributes.add('forwardSpeed', { type: 'number', default: 1.8, title: 'Forward Speed (rel. to distance)' });
604
  OrbitCameraInputKeyboard.attributes.add('strafeSpeed', { type: 'number', default: 1.8, title: 'Strafe Speed (rel. to distance)' });
605
 
 
625
  var shift = this.keyboard.isPressed(pc.KEY_SHIFT);
626
  var ctrl = this.keyboard.isPressed(pc.KEY_CONTROL);
627
 
628
+ // -------- Ctrl + flèches : ORBIT autour du pivot --------
629
  if (ctrl && (up || dn || lt || rt)) {
630
+ var yawDir = (rt ? 1 : 0) - (lt ? 1 : 0);
631
+ var pitchDir = (up ? 1 : 0) - (dn ? 1 : 0);
632
 
633
  if (yawDir !== 0) {
634
  this.orbitCamera.yaw = this.orbitCamera.yaw + yawDir * this.orbitYawSpeedDeg * dt;
635
  }
636
 
637
  if (pitchDir !== 0) {
 
638
  var currPitch = this.orbitCamera.pitch;
639
  var currYaw = this.orbitCamera.yaw;
640
  var currDist = this.orbitCamera.distance;
 
645
  var preY = currPivot.y + (-forward.y) * currDist;
646
 
647
  var testPitch = currPitch + pitchDir * this.orbitPitchSpeedDeg * dt;
648
+ var testQuat = new pc.Quat().setFromEulerAngles(testPitch, currYaw, 0);
649
  var testForward = new pc.Vec3(); testQuat.transformVector(pc.Vec3.FORWARD, testForward);
650
  var proposedY = currPivot.y + (-testForward.y) * currDist;
651
 
 
659
  return;
660
  }
661
 
662
+ // -------- Maj + flèches : ROTATION SUR PLACE --------
663
  if (shift && (up || dn || lt || rt)) {
664
  var yawDirR = (rt ? 1 : 0) - (lt ? 1 : 0);
665
  var pitchDirR = (up ? 1 : 0) - (dn ? 1 : 0);
666
 
 
667
  var camPos = this.entity.getPosition().clone();
668
+ var dist = this.orbitCamera._distance;
669
 
670
+ // bypass clamps pour rotation libre
671
  this.orbitCamera._yaw += yawDirR * this.rotateYawSpeedDeg * dt;
672
  this.orbitCamera._pitch += pitchDirR * this.rotatePitchSpeedDeg * dt;
673
  this.orbitCamera._targetYaw = this.orbitCamera._yaw;
 
677
  var q = new pc.Quat().setFromEulerAngles(this.orbitCamera._pitch, this.orbitCamera._yaw, 0);
678
  var forward = new pc.Vec3(); q.transformVector(pc.Vec3.FORWARD, forward);
679
  var newPivot = camPos.clone().add(forward.clone().mulScalar(dist));
680
+ this.orbitCamera._clampPivotToBBox(newPivot);
681
  this.orbitCamera._pivotPoint.copy(newPivot);
682
 
 
683
  this.orbitCamera._removeInertia();
684
  this.orbitCamera._updatePosition();
685
  this.entity.setPosition(camPos);
 
687
  return;
688
  }
689
 
690
+ // -------- Flèches seules : DÉPLACEMENT REPÈRE CAMÉRA --------
691
  var moveForward = (up ? 1 : 0) - (dn ? 1 : 0);
692
  var moveRight = (rt ? 1 : 0) - (lt ? 1 : 0);
693
  if (moveForward === 0 && moveRight === 0) return;
694
 
 
695
  var fwd = this.entity.forward.clone();
696
  var right = this.entity.right.clone();
697
+ if (fwd.lengthSq() > 1e-8) fwd.normalize();
698
  if (right.lengthSq() > 1e-8) right.normalize();
699
 
700
  var camDist = Math.max(0.1, this.orbitCamera._distance);
 
703
 
704
  var delta = new pc.Vec3();
705
  if (moveForward !== 0) delta.add(fwd.mulScalar(moveForward * speedF * dt));
706
+ if (moveRight !== 0) delta.add(right.mulScalar(moveRight * speedR * dt));
707
 
708
  if (delta.lengthSq() > 0) {
709
+ // Respect minY (camY_new = camY_cur + delta.y)
710
  var currCamY = this.orbitCamera.worldCameraYForPivot(this.orbitCamera._pivotPoint);
711
  var minY = this.orbitCamera.minY;
712
  if (currCamY + delta.y < minY) {
713
+ delta.y = minY - currCamY;
714
  }
715
+ var newPivot = this.orbitCamera._pivotPoint.clone().add(delta);
716
+ this.orbitCamera._clampPivotToBBox(newPivot);
717
+ this.orbitCamera._pivotPoint.copy(newPivot);
718
  }
719
  };