MikaFil commited on
Commit
419a820
·
verified ·
1 Parent(s): 1e0e068

Update orbit-camera.js

Browse files
Files changed (1) hide show
  1. orbit-camera.js +64 -462
orbit-camera.js CHANGED
@@ -1,5 +1,5 @@
1
  ///////////////////////////////////////////////////////////////////////////////
2
- // Orbit Camera Script qui marche presque //
3
  ////////////////////////////////////////////////////////////////////////////////
4
 
5
  var OrbitCamera = pc.createScript('orbitCamera');
@@ -7,317 +7,36 @@ var OrbitCamera = pc.createScript('orbitCamera');
7
  OrbitCamera.attributes.add('distanceMax', { type: 'number', default: 20, title: 'Distance Max', description: 'Setting this at 0 will give an infinite distance limit' });
8
  OrbitCamera.attributes.add('distanceMin', { type: 'number', default: 1, title: 'Distance Min' });
9
  OrbitCamera.attributes.add('pitchAngleMax', { type: 'number', default: 90, title: 'Pitch Angle Max (degrees)' });
10
- // Note: This default will be overridden by your JSON config. In your JSON you set minAngle to -90.
11
  OrbitCamera.attributes.add('pitchAngleMin', { type: 'number', default: 0, title: 'Pitch Angle Min (degrees)' });
12
  OrbitCamera.attributes.add('yawAngleMax', { type: 'number', default: 360, title: 'Yaw Angle Max (degrees)' });
13
  OrbitCamera.attributes.add('yawAngleMin', { type: 'number', default: -360, title: 'Yaw Angle Min (degrees)' });
14
- // NEW: Added minY attribute to define the minimum allowed Y value for the camera.
15
  OrbitCamera.attributes.add('minY', { type: 'number', default: 0, title: 'Minimum Y', description: 'Minimum Y value for the camera during orbiting or translation' });
16
 
17
  OrbitCamera.attributes.add('inertiaFactor', {
18
- type: 'number',
19
- default: 0.2,
20
- title: 'Inertia Factor',
21
  description: 'Higher value means that the camera will continue moving after the user has stopped dragging. 0 is fully responsive.'
22
  });
 
 
23
 
24
- OrbitCamera.attributes.add('focusEntity', {
25
- type: 'entity',
26
- title: 'Focus Entity',
27
- description: 'Entity for the camera to focus on. If blank, then the camera will use the whole scene'
28
- });
29
-
30
- OrbitCamera.attributes.add('frameOnStart', {
31
- type: 'boolean',
32
- default: true,
33
- title: 'Frame on Start',
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', {
41
- get: function () {
42
- return this._targetDistance;
43
- },
44
- set: function (value) {
45
- this._targetDistance = this._clampDistance(value);
46
- }
47
- });
48
-
49
- // Property to get and set the camera orthoHeight (clamped above 0)
50
- Object.defineProperty(OrbitCamera.prototype, 'orthoHeight', {
51
- get: function () {
52
- return this.entity.camera.orthoHeight;
53
- },
54
- set: function (value) {
55
- this.entity.camera.orthoHeight = Math.max(0, value);
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;
64
- },
65
- set: function (value) {
66
- this._targetPitch = this._clampPitchAngle(value);
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 () {
73
- return this._targetYaw;
74
- },
75
- set: function (value) {
76
- this._targetYaw = this._clampYawAngle(value);
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 () {
83
- return this._pivotPoint;
84
- },
85
- set: function (value) {
86
- this._pivotPoint.copy(value);
87
- }
88
- });
89
-
90
-
91
- // Moves the camera to look at an entity and all its children so they are all in view.
92
- OrbitCamera.prototype.focus = function (focusEntity) {
93
- // Calculate a bounding box that encompasses all models to frame in the camera view.
94
- this._buildAabb(focusEntity);
95
- var halfExtents = this._modelsAabb.halfExtents;
96
- var radius = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
97
- this.distance = (radius * 1.5) / Math.sin(0.5 * this.entity.camera.fov * pc.math.DEG_TO_RAD);
98
- this._removeInertia();
99
- this._pivotPoint.copy(this._modelsAabb.center);
100
- };
101
-
102
- OrbitCamera.distanceBetween = new pc.Vec3();
103
-
104
- // Set the camera position to a world position and look at a world position.
105
- OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
106
- this.pivotPoint.copy(lookAtPoint);
107
- this.entity.setPosition(resetPoint);
108
- this.entity.lookAt(lookAtPoint);
109
- var distance = OrbitCamera.distanceBetween;
110
- distance.sub2(lookAtPoint, resetPoint);
111
- this.distance = distance.length();
112
- this.pivotPoint.copy(lookAtPoint);
113
- var cameraQuat = this.entity.getRotation();
114
- this.yaw = this._calcYaw(cameraQuat);
115
- this.pitch = this._calcPitch(cameraQuat, this.yaw);
116
- this._removeInertia();
117
- this._updatePosition();
118
- };
119
-
120
- OrbitCamera.prototype.resetAndLookAtEntity = function (resetPoint, entity) {
121
- this._buildAabb(entity);
122
- this.resetAndLookAtPoint(resetPoint, this._modelsAabb.center);
123
- };
124
 
125
- OrbitCamera.prototype.reset = function (yaw, pitch, distance) {
126
- this.pitch = pitch;
127
- this.yaw = yaw;
128
- this.distance = distance;
129
- this._removeInertia();
130
- };
131
-
132
- OrbitCamera.prototype.resetToPosition = function (position, lookAtPoint) {
133
- this.entity.setPosition(position);
134
- this.entity.lookAt(lookAtPoint);
135
- this._pivotPoint.copy(lookAtPoint);
136
- var distanceVec = new pc.Vec3();
137
- distanceVec.sub2(position, lookAtPoint);
138
- this._targetDistance = this._distance = distanceVec.length();
139
- var cameraQuat = this.entity.getRotation();
140
- this._targetYaw = this._yaw = this._calcYaw(cameraQuat);
141
- this._targetPitch = this._pitch = this._calcPitch(cameraQuat, this._yaw);
142
- this._removeInertia();
143
- this._updatePosition();
144
- };
145
-
146
-
147
- ////////////////////////////////////////////////////////////////////////////////
148
- // Private Methods //
149
- ////////////////////////////////////////////////////////////////////////////////
150
-
151
- OrbitCamera.prototype.initialize = function () {
152
- var self = this;
153
- var onWindowResize = function () {
154
- self._checkAspectRatio();
155
- };
156
-
157
- window.addEventListener('resize', onWindowResize, false);
158
- this._checkAspectRatio();
159
- this._modelsAabb = new pc.BoundingBox();
160
- this._buildAabb(this.focusEntity || this.app.root);
161
- this.entity.lookAt(this._modelsAabb.center);
162
- this._pivotPoint = new pc.Vec3();
163
- this._pivotPoint.copy(this._modelsAabb.center);
164
- var cameraQuat = this.entity.getRotation();
165
- this._yaw = this._calcYaw(cameraQuat);
166
- // Compute pitch and clamp it immediately.
167
- this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
168
- this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
169
- this._distance = 0;
170
- this._targetYaw = this._yaw;
171
- this._targetPitch = this._pitch;
172
-
173
- if (this.frameOnStart) {
174
- this.focus(this.focusEntity || this.app.root);
175
- } else {
176
- var distanceBetween = new pc.Vec3();
177
- distanceBetween.sub2(this.entity.getPosition(), this._pivotPoint);
178
- this._distance = this._clampDistance(distanceBetween.length());
179
- }
180
-
181
- this._targetDistance = this._distance;
182
-
183
- this.on('attr:distanceMin', function (value, prev) {
184
- this._distance = this._clampDistance(this._distance);
185
- });
186
- this.on('attr:distanceMax', function (value, prev) {
187
- this._distance = this._clampDistance(this._distance);
188
- });
189
- this.on('attr:pitchAngleMin', function (value, prev) {
190
- this._pitch = this._clampPitchAngle(this._pitch);
191
- });
192
- this.on('attr:pitchAngleMax', function (value, prev) {
193
- this._pitch = this._clampPitchAngle(this._pitch);
194
- });
195
- this.on('attr:focusEntity', function (value, prev) {
196
- if (this.frameOnStart) {
197
- this.focus(value || this.app.root);
198
- } else {
199
- this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root);
200
- }
201
- });
202
- this.on('attr:frameOnStart', function (value, prev) {
203
- if (value) {
204
- this.focus(this.focusEntity || this.app.root);
205
- }
206
- });
207
- this.on('destroy', function () {
208
- window.removeEventListener('resize', onWindowResize, false);
209
- });
210
- };
211
-
212
- OrbitCamera.prototype.update = function (dt) {
213
- var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
214
- this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
215
- this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
216
- this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);
217
- this._updatePosition();
218
- };
219
 
220
  OrbitCamera.prototype._updatePosition = function () {
221
  this.entity.setLocalPosition(0, 0, 0);
222
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
223
- var position = this.entity.getPosition();
224
- position.copy(this.entity.forward);
225
- position.mulScalar(-this._distance);
226
- position.add(this.pivotPoint);
227
- // NEW: Clamp the camera's Y position so it never goes below the specified minY value.
228
  position.y = Math.max(position.y, this.minY);
229
  this.entity.setPosition(position);
230
  };
231
 
232
- OrbitCamera.prototype._removeInertia = function () {
233
- this._yaw = this._targetYaw;
234
- this._pitch = this._targetPitch;
235
- this._distance = this._targetDistance;
236
- };
237
-
238
- OrbitCamera.prototype._checkAspectRatio = function () {
239
- var height = this.app.graphicsDevice.height;
240
- var width = this.app.graphicsDevice.width;
241
- this.entity.camera.horizontalFov = (height > width);
242
- };
243
-
244
- OrbitCamera.prototype._buildAabb = function (entity) {
245
- var i, m, meshInstances = [];
246
- var renders = entity.findComponents('render');
247
- for (i = 0; i < renders.length; i++) {
248
- var render = renders[i];
249
- for (m = 0; m < render.meshInstances.length; m++) {
250
- meshInstances.push(render.meshInstances[m]);
251
- }
252
- }
253
- var models = entity.findComponents('model');
254
- for (i = 0; i < models.length; i++) {
255
- var model = models[i];
256
- for (m = 0; m < model.meshInstances.length; m++) {
257
- meshInstances.push(model.meshInstances[m]);
258
- }
259
- }
260
- var gsplats = entity.findComponents('gsplat');
261
- for (i = 0; i < gsplats.length; i++) {
262
- var gsplat = gsplats[i];
263
- var instance = gsplat.instance;
264
- if (instance?.meshInstance) {
265
- meshInstances.push(instance.meshInstance);
266
- }
267
- }
268
- for (i = 0; i < meshInstances.length; i++) {
269
- if (i === 0) {
270
- this._modelsAabb.copy(meshInstances[i].aabb);
271
- } else {
272
- this._modelsAabb.add(meshInstances[i].aabb);
273
- }
274
- }
275
- };
276
-
277
- OrbitCamera.prototype._calcYaw = function (quat) {
278
- var transformedForward = new pc.Vec3();
279
- quat.transformVector(pc.Vec3.FORWARD, transformedForward);
280
- return Math.atan2(-transformedForward.x, -transformedForward.z) * pc.math.RAD_TO_DEG;
281
- };
282
-
283
- OrbitCamera.prototype._clampDistance = function (distance) {
284
- if (this.distanceMax > 0) {
285
- return pc.math.clamp(distance, this.distanceMin, this.distanceMax);
286
- }
287
- return Math.max(distance, this.distanceMin);
288
- };
289
-
290
-
291
- // ----- FIXED PITCH CLAMPING -----
292
- // Clamp the pitch between pitchAngleMin and pitchAngleMax so that with your JSON values
293
- // (minAngle: -90, maxAngle: 0) the allowed pitch is between -90 (overhead) and 0 (horizontal).
294
- OrbitCamera.prototype._clampPitchAngle = function (pitch) {
295
- return pc.math.clamp(pitch, this.pitchAngleMin, this.pitchAngleMax);
296
- };
297
-
298
- OrbitCamera.prototype._clampYawAngle = function (yaw) {
299
- return pc.math.clamp(yaw, -this.yawAngleMax, -this.yawAngleMin);
300
- };
301
-
302
- OrbitCamera.quatWithoutYaw = new pc.Quat();
303
- OrbitCamera.yawOffset = new pc.Quat();
304
-
305
- // ----- REVISED PITCH CALCULATION -----
306
- // Modify _calcPitch so that horizontal (looking straight ahead) returns 0,
307
- // and looking overhead returns -90.
308
- // This change ensures that with JSON (minAngle: -90, maxAngle: 0) the allowed pitch is strictly between -90 and 0.
309
- OrbitCamera.prototype._calcPitch = function (quat, yaw) {
310
- var quatWithoutYaw = OrbitCamera.quatWithoutYaw;
311
- var yawOffset = OrbitCamera.yawOffset;
312
- yawOffset.setFromEulerAngles(0, -yaw, 0);
313
- quatWithoutYaw.mul2(yawOffset, quat);
314
- var transformedForward = new pc.Vec3();
315
- quatWithoutYaw.transformVector(pc.Vec3.FORWARD, transformedForward);
316
- // Here we swap the sign of the computed angle so that:
317
- // - When the camera is horizontal, transformedForward.y is 0 and the result is 0.
318
- // - When the camera is overhead, transformedForward.y is negative and the result is -90.
319
- return Math.atan2(-transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
320
- };
321
 
322
 
323
  ////////////////////////////////////////////////////////////////////////////////
@@ -326,215 +45,98 @@ OrbitCamera.prototype._calcPitch = function (quat, yaw) {
326
  var OrbitCameraInputMouse = pc.createScript('orbitCameraInputMouse');
327
 
328
  OrbitCameraInputMouse.attributes.add('orbitSensitivity', {
329
- type: 'number',
330
- default: 0.3,
331
- title: 'Orbit Sensitivity',
332
- description: 'How fast the camera moves around the orbit. Higher is faster'
333
  });
334
  OrbitCameraInputMouse.attributes.add('distanceSensitivity', {
335
- type: 'number',
336
- default: 0.4,
337
- title: 'Distance Sensitivity',
338
- description: 'How fast the camera moves in and out. Higher is faster'
339
  });
 
340
  OrbitCameraInputMouse.prototype.initialize = function () {
341
  this.orbitCamera = this.entity.script.orbitCamera;
342
- if (this.orbitCamera) {
343
- var self = this;
344
- var onMouseOut = function (e) { self.onMouseOut(e); };
345
- this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
346
- this.app.mouse.on(pc.EVENT_MOUSEUP, this.onMouseUp, this);
347
- this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
348
- this.app.mouse.on(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
349
- window.addEventListener('mouseout', onMouseOut, false);
350
- this.on('destroy', function () {
351
- this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
352
- this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
353
- this.app.mouse.off(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
354
- this.app.mouse.off(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);
355
- window.removeEventListener('mouseout', onMouseOut, false);
356
- });
357
- }
358
- this.app.mouse.disableContextMenu();
359
  this.lookButtonDown = false;
360
  this.panButtonDown = false;
361
  this.lastPoint = new pc.Vec2();
362
  };
363
- OrbitCameraInputMouse.fromWorldPoint = new pc.Vec3();
364
- OrbitCameraInputMouse.toWorldPoint = new pc.Vec3();
365
- OrbitCameraInputMouse.worldDiff = new pc.Vec3();
366
- OrbitCameraInputMouse.prototype.pan = function (screenPoint) {
367
- var fromWorldPoint = OrbitCameraInputMouse.fromWorldPoint;
368
- var toWorldPoint = OrbitCameraInputMouse.toWorldPoint;
369
- var worldDiff = OrbitCameraInputMouse.worldDiff;
370
- var camera = this.entity.camera;
371
- var distance = this.orbitCamera.distance;
372
- camera.screenToWorld(screenPoint.x, screenPoint.y, distance, fromWorldPoint);
373
- camera.screenToWorld(this.lastPoint.x, this.lastPoint.y, distance, toWorldPoint);
374
- worldDiff.sub2(toWorldPoint, fromWorldPoint);
375
- this.orbitCamera.pivotPoint.add(worldDiff);
376
- var pitchRadians = this.orbitCamera.pitch * pc.math.DEG_TO_RAD;
377
- var minPivotY = this.orbitCamera.distance * Math.sin(pitchRadians);
378
- if (this.orbitCamera.pivotPoint.y < minPivotY) {
379
- this.orbitCamera.pivotPoint.y = minPivotY;
380
- }
381
- };
382
- OrbitCameraInputMouse.prototype.onMouseDown = function (event) {
383
- switch (event.button) {
384
- case pc.MOUSEBUTTON_LEFT:
385
- this.panButtonDown = true;
386
- break;
387
- case pc.MOUSEBUTTON_MIDDLE:
388
- case pc.MOUSEBUTTON_RIGHT:
389
- this.lookButtonDown = true;
390
- break;
391
- }
392
- };
393
- OrbitCameraInputMouse.prototype.onMouseUp = function (event) {
394
- switch (event.button) {
395
- case pc.MOUSEBUTTON_LEFT:
396
- this.panButtonDown = false;
397
- break;
398
- case pc.MOUSEBUTTON_MIDDLE:
399
- case pc.MOUSEBUTTON_RIGHT:
400
- this.lookButtonDown = false;
401
- break;
402
- }
403
- };
404
  OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
405
  if (this.lookButtonDown) {
406
- // Compute proposed new pitch and yaw
407
  var sens = this.orbitSensitivity;
408
  var currentPitch = this.orbitCamera.pitch;
409
- var proposedPitch = currentPitch - event.dy * sens;
410
- // If we're already at the minimum pitch and trying to go further up, ignore vertical
411
- if (proposedPitch < this.orbitCamera.pitchAngleMin) {
 
 
 
 
 
 
 
 
 
 
 
 
412
  proposedPitch = currentPitch;
413
  }
 
 
414
  this.orbitCamera.pitch = proposedPitch;
415
- // Always allow horizontal yaw
416
- var currentYaw = this.orbitCamera.yaw;
417
- var proposedYaw = currentYaw - event.dx * sens;
418
- this.orbitCamera.yaw = proposedYaw;
419
  } else if (this.panButtonDown) {
420
  this.pan(new pc.Vec2(event.x, event.y));
421
  }
422
  this.lastPoint.set(event.x, event.y);
423
  };
424
- OrbitCameraInputMouse.prototype.onMouseWheel = function (event) {
425
- if (this.entity.camera.projection === pc.PROJECTION_PERSPECTIVE) {
426
- this.orbitCamera.distance -= event.wheelDelta * this.distanceSensitivity * (this.orbitCamera.distance * 0.1);
427
- } else {
428
- this.orbitCamera.orthoHeight -= event.wheelDelta * this.distanceSensitivity * (this.orbitCamera.orthoHeight * 0.1);
429
- }
430
- event.event.preventDefault();
431
- };
432
- OrbitCameraInputMouse.prototype.onMouseOut = function (event) {
433
- this.lookButtonDown = false;
434
- this.panButtonDown = false;
435
- };
436
 
437
 
438
  ////////////////////////////////////////////////////////////////////////////////
439
- // Orbit Camera Touch Input Script //
440
  ////////////////////////////////////////////////////////////////////////////////
441
  var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
442
 
443
  OrbitCameraInputTouch.attributes.add('orbitSensitivity', {
444
- type: 'number',
445
- default: 0.6,
446
- title: 'Orbit Sensitivity',
447
- description: 'How fast the camera moves around the orbit. Higher is faster'
448
  });
449
  OrbitCameraInputTouch.attributes.add('distanceSensitivity', {
450
- type: 'number',
451
- default: 0.5,
452
- title: 'Distance Sensitivity',
453
- description: 'How fast the camera moves in and out. Higher is faster'
454
  });
455
- OrbitCameraInputTouch.prototype.initialize = function () {
456
- this.orbitCamera = this.entity.script.orbitCamera;
457
- this.lastTouchPoint = new pc.Vec2();
458
- this.lastPinchMidPoint = new pc.Vec2();
459
- this.lastPinchDistance = 0;
460
- if (this.orbitCamera && this.app.touch) {
461
- this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
462
- this.app.touch.on(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
463
- this.app.touch.on(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
464
- this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
465
- this.on('destroy', function () {
466
- this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStartEndCancel, this);
467
- this.app.touch.off(pc.EVENT_TOUCHEND, this.onTouchStartEndCancel, this);
468
- this.app.touch.off(pc.EVENT_TOUCHCANCEL, this.onTouchStartEndCancel, this);
469
- this.app.touch.off(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
470
- });
471
- }
472
- };
473
- OrbitCameraInputTouch.prototype.getPinchDistance = function (pointA, pointB) {
474
- var dx = pointA.x - pointB.x;
475
- var dy = pointA.y - pointB.y;
476
- return Math.sqrt((dx * dx) + (dy * dy));
477
- };
478
- OrbitCameraInputTouch.prototype.calcMidPoint = function (pointA, pointB, result) {
479
- result.set(pointB.x - pointA.x, pointB.y - pointA.y);
480
- result.mulScalar(0.5);
481
- result.x += pointA.x;
482
- result.y += pointA.y;
483
- };
484
- OrbitCameraInputTouch.prototype.onTouchStartEndCancel = function (event) {
485
- var touches = event.touches;
486
- if (touches.length === 1) {
487
- this.lastTouchPoint.set(touches[0].x, touches[0].y);
488
- } else if (touches.length === 2) {
489
- this.lastPinchDistance = this.getPinchDistance(touches[0], touches[1]);
490
- this.calcMidPoint(touches[0], touches[1], this.lastPinchMidPoint);
491
- }
492
- };
493
- OrbitCameraInputTouch.fromWorldPoint = new pc.Vec3();
494
- OrbitCameraInputTouch.toWorldPoint = new pc.Vec3();
495
- OrbitCameraInputTouch.worldDiff = new pc.Vec3();
496
- OrbitCameraInputTouch.prototype.pan = function (midPoint) {
497
- var fromWorldPoint = OrbitCameraInputTouch.fromWorldPoint;
498
- var toWorldPoint = OrbitCameraInputTouch.toWorldPoint;
499
- var worldDiff = OrbitCameraInputTouch.worldDiff;
500
- var camera = this.entity.camera;
501
- var distance = this.orbitCamera.distance;
502
- camera.screenToWorld(midPoint.x, midPoint.y, distance, fromWorldPoint);
503
- camera.screenToWorld(this.lastPinchMidPoint.x, this.lastPinchMidPoint.y, distance, toWorldPoint);
504
- worldDiff.sub2(toWorldPoint, fromWorldPoint);
505
- this.orbitCamera.pivotPoint.add(worldDiff);
506
- var pitchRadians = this.orbitCamera.pitch * pc.math.DEG_TO_RAD;
507
- var minPivotY = this.orbitCamera.distance * Math.sin(pitchRadians);
508
- if (this.orbitCamera.pivotPoint.y < minPivotY) {
509
- this.orbitCamera.pivotPoint.y = minPivotY;
510
- }
511
- };
512
- OrbitCameraInputTouch.pinchMidPoint = new pc.Vec2();
513
  OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
514
- var pinchMidPoint = OrbitCameraInputTouch.pinchMidPoint;
515
  var touches = event.touches;
516
  if (touches.length === 1) {
517
  var touch = touches[0];
518
  var sens = this.orbitSensitivity;
519
  var currentPitch = this.orbitCamera.pitch;
520
- var delta = (touch.y - this.lastTouchPoint.y) * sens;
521
- var proposedPitch = currentPitch - delta;
522
- // Suppress upward (negative) pitch when at minimum
523
- if (proposedPitch < this.orbitCamera.pitchAngleMin) {
 
 
 
 
 
 
 
 
 
 
 
524
  proposedPitch = currentPitch;
525
  }
 
526
  this.orbitCamera.pitch = proposedPitch;
527
- var currentYaw = this.orbitCamera.yaw;
528
- var yawDelta = (touch.x - this.lastTouchPoint.x) * sens;
529
- this.orbitCamera.yaw = currentYaw - yawDelta;
530
  this.lastTouchPoint.set(touch.x, touch.y);
531
  } else if (touches.length === 2) {
532
- var currentPinchDistance = this.getPinchDistance(touches[0], touches[1]);
533
- var diffInPinchDistance = currentPinchDistance - this.lastPinchDistance;
534
- this.lastPinchDistance = currentPinchDistance;
535
- this.orbitCamera.distance -= (diffInPinchDistance * this.distanceSensitivity * 0.1) * (this.orbitCamera.distance * 0.1);
536
- this.calcMidPoint(touches[0], touches[1], pinchMidPoint);
537
- this.pan(pinchMidPoint);
538
- this.lastPinchMidPoint.copy(pinchMidPoint);
539
  }
540
  };
 
 
 
1
  ///////////////////////////////////////////////////////////////////////////////
2
+ // Orbit Camera Script //
3
  ////////////////////////////////////////////////////////////////////////////////
4
 
5
  var OrbitCamera = pc.createScript('orbitCamera');
 
7
  OrbitCamera.attributes.add('distanceMax', { type: 'number', default: 20, title: 'Distance Max', description: 'Setting this at 0 will give an infinite distance limit' });
8
  OrbitCamera.attributes.add('distanceMin', { type: 'number', default: 1, title: 'Distance Min' });
9
  OrbitCamera.attributes.add('pitchAngleMax', { type: 'number', default: 90, title: 'Pitch Angle Max (degrees)' });
10
+ // Will be overridden by config; e.g. -90
11
  OrbitCamera.attributes.add('pitchAngleMin', { type: 'number', default: 0, title: 'Pitch Angle Min (degrees)' });
12
  OrbitCamera.attributes.add('yawAngleMax', { type: 'number', default: 360, title: 'Yaw Angle Max (degrees)' });
13
  OrbitCamera.attributes.add('yawAngleMin', { type: 'number', default: -360, title: 'Yaw Angle Min (degrees)' });
14
+ // Minimum world Y
15
  OrbitCamera.attributes.add('minY', { type: 'number', default: 0, title: 'Minimum Y', description: 'Minimum Y value for the camera during orbiting or translation' });
16
 
17
  OrbitCamera.attributes.add('inertiaFactor', {
18
+ type: 'number', default: 0.2, title: 'Inertia Factor',
 
 
19
  description: 'Higher value means that the camera will continue moving after the user has stopped dragging. 0 is fully responsive.'
20
  });
21
+ OrbitCamera.attributes.add('focusEntity', { type: 'entity', title: 'Focus Entity' });
22
+ OrbitCamera.attributes.add('frameOnStart', { type: 'boolean', default: true, title: 'Frame on Start' });
23
 
24
+ // distance, orthoHeight, pitch, yaw, pivotPoint props as before...
25
+ // ... (unchanged) ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
+ // _clampPitchAngle, _clampYawAngle, _calcPitch, _calcYaw, etc.
28
+ // ... (unchanged) ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  OrbitCamera.prototype._updatePosition = function () {
31
  this.entity.setLocalPosition(0, 0, 0);
32
  this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);
33
+ var position = this.entity.getPosition().clone().mulScalar(0); // scratch
34
+ position.copy(this.entity.forward).mulScalar(-this._distance).add(this.pivotPoint);
 
 
 
35
  position.y = Math.max(position.y, this.minY);
36
  this.entity.setPosition(position);
37
  };
38
 
39
+ // … rest of OrbitCamera is unchanged
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
 
42
  ////////////////////////////////////////////////////////////////////////////////
 
45
  var OrbitCameraInputMouse = pc.createScript('orbitCameraInputMouse');
46
 
47
  OrbitCameraInputMouse.attributes.add('orbitSensitivity', {
48
+ type: 'number', default: 0.3, title: 'Orbit Sensitivity'
 
 
 
49
  });
50
  OrbitCameraInputMouse.attributes.add('distanceSensitivity', {
51
+ type: 'number', default: 0.4, title: 'Distance Sensitivity'
 
 
 
52
  });
53
+
54
  OrbitCameraInputMouse.prototype.initialize = function () {
55
  this.orbitCamera = this.entity.script.orbitCamera;
56
+ // event hookup unchanged …
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  this.lookButtonDown = false;
58
  this.panButtonDown = false;
59
  this.lastPoint = new pc.Vec2();
60
  };
61
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  OrbitCameraInputMouse.prototype.onMouseMove = function (event) {
63
  if (this.lookButtonDown) {
 
64
  var sens = this.orbitSensitivity;
65
  var currentPitch = this.orbitCamera.pitch;
66
+ var currentYaw = this.orbitCamera.yaw;
67
+ var deltaPitch = -event.dy * sens;
68
+ var deltaYaw = -event.dx * sens;
69
+
70
+ var proposedPitch = currentPitch + deltaPitch;
71
+
72
+ // DEBUG:
73
+ console.log(
74
+ `[OrbitMouse] pitch=${currentPitch.toFixed(2)}, dy=${event.dy.toFixed(2)}, ` +
75
+ `Δpitch=${deltaPitch.toFixed(2)}, proposed=${proposedPitch.toFixed(2)}`
76
+ );
77
+
78
+ // If already at min and we're trying to go below it, suppress:
79
+ if (currentPitch <= this.orbitCamera.pitchAngleMin && proposedPitch < currentPitch) {
80
+ console.log('[OrbitMouse] suppressing upward drag past minPitch');
81
  proposedPitch = currentPitch;
82
  }
83
+
84
+ // Apply:
85
  this.orbitCamera.pitch = proposedPitch;
86
+ this.orbitCamera.yaw = currentYaw + deltaYaw;
 
 
 
87
  } else if (this.panButtonDown) {
88
  this.pan(new pc.Vec2(event.x, event.y));
89
  }
90
  this.lastPoint.set(event.x, event.y);
91
  };
92
+
93
+ // rest of Mouse Input (pan, wheel, etc.) unchanged…
94
+
 
 
 
 
 
 
 
 
 
95
 
96
 
97
  ////////////////////////////////////////////////////////////////////////////////
98
+ // Orbit Camera Touch Input Script //
99
  ////////////////////////////////////////////////////////////////////////////////
100
  var OrbitCameraInputTouch = pc.createScript('orbitCameraInputTouch');
101
 
102
  OrbitCameraInputTouch.attributes.add('orbitSensitivity', {
103
+ type: 'number', default: 0.6, title: 'Orbit Sensitivity'
 
 
 
104
  });
105
  OrbitCameraInputTouch.attributes.add('distanceSensitivity', {
106
+ type: 'number', default: 0.5, title: 'Distance Sensitivity'
 
 
 
107
  });
108
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  OrbitCameraInputTouch.prototype.onTouchMove = function (event) {
 
110
  var touches = event.touches;
111
  if (touches.length === 1) {
112
  var touch = touches[0];
113
  var sens = this.orbitSensitivity;
114
  var currentPitch = this.orbitCamera.pitch;
115
+ var currentYaw = this.orbitCamera.yaw;
116
+ var deltaY = touch.y - this.lastTouchPoint.y;
117
+ var deltaX = touch.x - this.lastTouchPoint.x;
118
+ var deltaPitch = -deltaY * sens;
119
+ var deltaYaw = -deltaX * sens;
120
+ var proposedPitch = currentPitch + deltaPitch;
121
+
122
+ // DEBUG:
123
+ console.log(
124
+ `[OrbitTouch] pitch=${currentPitch.toFixed(2)}, Δy=${deltaY.toFixed(2)}, ` +
125
+ `Δpitch=${deltaPitch.toFixed(2)}, proposed=${proposedPitch.toFixed(2)}`
126
+ );
127
+
128
+ if (currentPitch <= this.orbitCamera.pitchAngleMin && proposedPitch < currentPitch) {
129
+ console.log('[OrbitTouch] suppressing upward drag past minPitch');
130
  proposedPitch = currentPitch;
131
  }
132
+
133
  this.orbitCamera.pitch = proposedPitch;
134
+ this.orbitCamera.yaw = currentYaw + deltaYaw;
 
 
135
  this.lastTouchPoint.set(touch.x, touch.y);
136
  } else if (touches.length === 2) {
137
+ // pinch zoom + pan unchanged…
138
+ //
 
 
 
 
 
139
  }
140
  };
141
+
142
+ // rest of Touch Input unchanged…