MikaFil commited on
Commit
1b149e1
·
verified ·
1 Parent(s): d6cce84

Update deplacement_dans_env/ctrl_camera_pr_env.js

Browse files
deplacement_dans_env/ctrl_camera_pr_env.js CHANGED
@@ -1,27 +1,18 @@
1
  // ctrl_camera_pr_env.js
2
  // ============================================================================
3
  // FREE CAMERA + COLLISION ROBUSTE (sans Ammo) — version "indoor safe"
4
- // - Souris : look
5
- // - ZQSD/Flèches : déplacement local
6
- // - Molette/Pinch : dolly avant/arrière
7
- // - Collisions : sphère (caméra) vs AABBs NON fusionnées (ou très peu),
8
- // avec filtrage des AABBs "quasi globales" (évite de bloquer toute la pièce)
9
- // - Mouvement "swept" (sous-division) pour éviter le tunneling
10
  // ============================================================================
11
 
12
  var FreeCamera = pc.createScript('orbitCamera'); // garder le nom public "orbitCamera"
13
 
14
  // ======================== Attributs ===========================
15
  FreeCamera.attributes.add('inertiaFactor', { type: 'number', default: 0.12, title: 'Inertia (rotation)' });
16
-
17
- // Limites de pitch
18
  FreeCamera.attributes.add('pitchAngleMin', { type: 'number', default: -89, title: 'Pitch Min (deg)' });
19
  FreeCamera.attributes.add('pitchAngleMax', { type: 'number', default: 89, title: 'Pitch Max (deg)' });
20
 
21
- // minY (altitude min du point CAMÉRA)
22
  FreeCamera.attributes.add('minY', { type: 'number', default: 0, title: 'Minimum camera Y' });
23
 
24
- // Vitesses
25
  FreeCamera.attributes.add('moveSpeed', { type: 'number', default: 2.2, title: 'Move Speed' });
26
  FreeCamera.attributes.add('strafeSpeed', { type: 'number', default: 2.2, title: 'Strafe Speed' });
27
  FreeCamera.attributes.add('dollySpeed', { type: 'number', default: 2.0, title: 'Mouse/Pinch Dolly Speed' });
@@ -34,13 +25,14 @@ FreeCamera.attributes.add('collisionEpsilon', { type: 'number', default: 0.0005,
34
  FreeCamera.attributes.add('maxStepDistance', { type: 'number', default: 0.20, title: 'Max step distance (swept move)' });
35
  FreeCamera.attributes.add('maxResolveIters', { type: 'number', default: 6, title: 'Max resolve iterations per step' });
36
 
37
- // Construction des colliders
38
  FreeCamera.attributes.add('inflateBias', { type: 'number', default: 0.0, title: 'Extra inflate (m, tiny)' });
39
  FreeCamera.attributes.add('mergeGap', { type: 'number', default: 0.0, title: 'Merge AABBs gap (0 = pas de gap)' });
40
-
41
- // Filtrage "anti-coquille"
42
  FreeCamera.attributes.add('globalCullFrac', { type: 'number', default: 0.08, title: 'Cull near-global AABBs (0.08=8%)' });
43
 
 
 
 
44
  // Bounding Box globale optionnelle (Xmin..Zmax)
45
  FreeCamera.attributes.add('Xmin', { type: 'number', default: -Infinity, title: 'BBox Xmin' });
46
  FreeCamera.attributes.add('Xmax', { type: 'number', default: Infinity, title: 'BBox Xmax' });
@@ -85,11 +77,18 @@ FreeCamera.prototype.initialize = function () {
85
  // colliders
86
  this._buildIndoorSafeColliders();
87
 
88
- // Forcer une passe de résolution initiale
89
- var p = this.entity.getPosition().clone();
90
- p = this._moveSweptTo(p, p);
91
- this._clampPosition(p);
92
- this.entity.setPosition(p);
 
 
 
 
 
 
 
93
 
94
  // aspect
95
  var self = this;
@@ -160,7 +159,7 @@ FreeCamera.prototype._buildIndoorSafeColliders = function () {
160
  if (!nearGlobal) filtered.push(boxes[k]);
161
  }
162
 
163
- // 4) Merge strict (chevauchement réel uniquement, gap ~ 0)
164
  var merged = this._mergeAabbs(filtered, Math.max(0, this.mergeGap || 0));
165
 
166
  // 5) Petit gonflage (epsilon), pas du rayon
@@ -181,7 +180,7 @@ FreeCamera.prototype._buildIndoorSafeColliders = function () {
181
  }
182
  };
183
 
184
- // Merge strict (gap==0 => uniquement si chevauchement)
185
  FreeCamera.prototype._mergeAabbs = function (boxes, gap) {
186
  if (!boxes || boxes.length <= 1) return boxes.slice();
187
  var out = boxes.slice();
@@ -223,8 +222,6 @@ FreeCamera.prototype._mergeAabbs = function (boxes, gap) {
223
  Math.max(aMax.z, bMax.z)
224
  );
225
  var nCenter = nMin.clone().add(nMax).mulScalar(0.5);
226
-
227
- // ❗️Pas de .abs() sur pc.Vec3 : calculer composante par composante
228
  var nHalf = new pc.Vec3(
229
  Math.abs(nMax.x - nCenter.x),
230
  Math.abs(nMax.y - nCenter.y),
@@ -244,6 +241,75 @@ FreeCamera.prototype._mergeAabbs = function (boxes, gap) {
244
  return out;
245
  };
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  // ======================== Contraintes génériques ===========================
248
  FreeCamera.prototype._bboxEnabled = function () {
249
  return (this.Xmin < this.Xmax) && (this.Ymin < this.Ymax) && (this.Zmin < this.Zmax);
@@ -297,12 +363,12 @@ FreeCamera.prototype._resolveCollisions = function (pos, maxIters) {
297
 
298
  for (var i = 0; i < this._colliders.length; i++) {
299
  var aabb = this._colliders[i].aabb;
300
- var min = aabb.getMin(); var max = aabb.getMax();
301
 
302
  // Point le plus proche sur l'AABB
303
- var cx = pc.math.clamp(p.x, min.x, max.x);
304
- var cy = pc.math.clamp(p.y, min.y, max.y);
305
- var cz = pc.math.clamp(p.z, min.z, max.z);
306
 
307
  var dx = p.x - cx, dy = p.y - cy, dz = p.z - cz;
308
  var distSq = dx*dx + dy*dy + dz*dz;
@@ -317,12 +383,12 @@ FreeCamera.prototype._resolveCollisions = function (pos, maxIters) {
317
  p.z += (dz / dist) * pen;
318
  } else {
319
  // pousser selon l'axe le plus "proche"
320
- var ex = Math.min(Math.abs(p.x - min.x), Math.abs(max.x - p.x));
321
- var ey = Math.min(Math.abs(p.y - min.y), Math.abs(max.y - p.y));
322
- var ez = Math.min(Math.abs(p.z - min.z), Math.abs(max.z - p.z));
323
- if (ex <= ey && ex <= ez) p.x += (Math.abs(p.x - min.x) < Math.abs(max.x - p.x) ? -pen : pen);
324
- else if (ey <= ex && ey <= ez) p.y += (Math.abs(p.y - min.y) < Math.abs(max.y - p.y) ? -pen : pen);
325
- else p.z += (Math.abs(p.z - min.z) < Math.abs(max.z - p.z) ? -pen : pen);
326
  }
327
  moved = true;
328
  }
 
1
  // ctrl_camera_pr_env.js
2
  // ============================================================================
3
  // FREE CAMERA + COLLISION ROBUSTE (sans Ammo) — version "indoor safe"
4
+ // Respecte la position initiale (JSON). Ne spawne à l’intérieur que si nécessaire.
 
 
 
 
 
5
  // ============================================================================
6
 
7
  var FreeCamera = pc.createScript('orbitCamera'); // garder le nom public "orbitCamera"
8
 
9
  // ======================== Attributs ===========================
10
  FreeCamera.attributes.add('inertiaFactor', { type: 'number', default: 0.12, title: 'Inertia (rotation)' });
 
 
11
  FreeCamera.attributes.add('pitchAngleMin', { type: 'number', default: -89, title: 'Pitch Min (deg)' });
12
  FreeCamera.attributes.add('pitchAngleMax', { type: 'number', default: 89, title: 'Pitch Max (deg)' });
13
 
 
14
  FreeCamera.attributes.add('minY', { type: 'number', default: 0, title: 'Minimum camera Y' });
15
 
 
16
  FreeCamera.attributes.add('moveSpeed', { type: 'number', default: 2.2, title: 'Move Speed' });
17
  FreeCamera.attributes.add('strafeSpeed', { type: 'number', default: 2.2, title: 'Strafe Speed' });
18
  FreeCamera.attributes.add('dollySpeed', { type: 'number', default: 2.0, title: 'Mouse/Pinch Dolly Speed' });
 
25
  FreeCamera.attributes.add('maxStepDistance', { type: 'number', default: 0.20, title: 'Max step distance (swept move)' });
26
  FreeCamera.attributes.add('maxResolveIters', { type: 'number', default: 6, title: 'Max resolve iterations per step' });
27
 
28
+ // Construction des colliders (indoor-safe)
29
  FreeCamera.attributes.add('inflateBias', { type: 'number', default: 0.0, title: 'Extra inflate (m, tiny)' });
30
  FreeCamera.attributes.add('mergeGap', { type: 'number', default: 0.0, title: 'Merge AABBs gap (0 = pas de gap)' });
 
 
31
  FreeCamera.attributes.add('globalCullFrac', { type: 'number', default: 0.08, title: 'Cull near-global AABBs (0.08=8%)' });
32
 
33
+ // Spawn auto (seulement si la position initiale est invalide)
34
+ FreeCamera.attributes.add('autoSpawnInside', { type: 'boolean', default: true, title: 'Auto-spawn inside if needed' });
35
+
36
  // Bounding Box globale optionnelle (Xmin..Zmax)
37
  FreeCamera.attributes.add('Xmin', { type: 'number', default: -Infinity, title: 'BBox Xmin' });
38
  FreeCamera.attributes.add('Xmax', { type: 'number', default: Infinity, title: 'BBox Xmax' });
 
77
  // colliders
78
  this._buildIndoorSafeColliders();
79
 
80
+ // ======== respecter la position initiale, ne spawner dedans que si nécessaire ========
81
+ if (this.autoSpawnInside && this._useCollision) {
82
+ var start = this.entity.getPosition().clone();
83
+ if (this._shouldAutoSpawn(start)) {
84
+ this._spawnInside(); // place au centre + pose sur sol
85
+ } else {
86
+ // juste assainir si on spawn légèrement intersecté
87
+ var safe = this._resolveCollisions(start, this.maxResolveIters);
88
+ this._clampPosition(safe);
89
+ this.entity.setPosition(safe);
90
+ }
91
+ }
92
 
93
  // aspect
94
  var self = this;
 
159
  if (!nearGlobal) filtered.push(boxes[k]);
160
  }
161
 
162
+ // 4) Merge strict (chevauchement réel uniquement)
163
  var merged = this._mergeAabbs(filtered, Math.max(0, this.mergeGap || 0));
164
 
165
  // 5) Petit gonflage (epsilon), pas du rayon
 
180
  }
181
  };
182
 
183
+ // Merge strict
184
  FreeCamera.prototype._mergeAabbs = function (boxes, gap) {
185
  if (!boxes || boxes.length <= 1) return boxes.slice();
186
  var out = boxes.slice();
 
222
  Math.max(aMax.z, bMax.z)
223
  );
224
  var nCenter = nMin.clone().add(nMax).mulScalar(0.5);
 
 
225
  var nHalf = new pc.Vec3(
226
  Math.abs(nMax.x - nCenter.x),
227
  Math.abs(nMax.y - nCenter.y),
 
241
  return out;
242
  };
243
 
244
+ // ======================== SPAWN INTÉRIEUR (optionnel) ===========================
245
+ // décide si on doit "forcer" un spawn intérieur
246
+ FreeCamera.prototype._shouldAutoSpawn = function (pos) {
247
+ if (!this._useCollision || !this._worldAabb) return false;
248
+
249
+ var R = Math.max(0, this.collisionRadius);
250
+ var eps = Math.max(1e-4, this.collisionEpsilon);
251
+
252
+ var wmin = this._worldAabb.getMin();
253
+ var wmax = this._worldAabb.getMax();
254
+
255
+ // 1) hors monde (avec marge du rayon)
256
+ if (pos.x < wmin.x + R + eps || pos.x > wmax.x - R - eps) return true;
257
+ if (pos.z < wmin.z + R + eps || pos.z > wmax.z - R - eps) return true;
258
+ if (pos.y < wmin.y - eps || pos.y > wmax.y + eps) return true;
259
+
260
+ // 2) pas de "sol" sous ce XZ
261
+ var bestY = this._highestYUnderXZ(pos.x, pos.z, eps);
262
+ if (bestY === -Infinity) return true;
263
+
264
+ // 3) si on est très en dessous du sol (ou bien au-dessus du plafond)
265
+ if (pos.y < bestY - 2.0) return true;
266
+ if (pos.y > wmax.y + 0.5) return true;
267
+
268
+ // 4) si on intersecte déjà fortement un collider (la résolution bouge beaucoup)
269
+ var resolved = this._resolveCollisions(pos, this.maxResolveIters);
270
+ var move = resolved.clone().sub(pos).length();
271
+ if (move > R * 0.5) return true;
272
+
273
+ return false;
274
+ };
275
+
276
+ // calcule le maxY des AABBs couvrant (x,z) — "sol" approximé
277
+ FreeCamera.prototype._highestYUnderXZ = function (x, z, eps) {
278
+ var bestY = -Infinity;
279
+ if (!this._colliders) return bestY;
280
+ for (var i = 0; i < this._colliders.length; i++) {
281
+ var aabb = this._colliders[i].aabb;
282
+ var amin = aabb.getMin(), amax = aabb.getMax();
283
+ if (x >= amin.x - eps && x <= amax.x + eps && z >= amin.z - eps && z <= amax.z + eps) {
284
+ if (amax.y > bestY) bestY = amax.y;
285
+ }
286
+ }
287
+ return bestY;
288
+ };
289
+
290
+ // place la caméra à l’intérieur: centre XZ, “drop” sur le sol/marche sous ce XZ
291
+ FreeCamera.prototype._spawnInside = function () {
292
+ if (!this._useCollision || !this._worldAabb) return;
293
+
294
+ var R = Math.max(0, this.collisionRadius);
295
+ var eps = Math.max(1e-4, this.collisionEpsilon);
296
+
297
+ var world = this._worldAabb;
298
+ var c = world.center;
299
+ var min = world.getMin(), max = world.getMax();
300
+
301
+ var x = pc.math.clamp(c.x, min.x + R + eps, max.x - R - eps);
302
+ var z = pc.math.clamp(c.z, min.z + R + eps, max.z - R - eps);
303
+
304
+ var bestY = this._highestYUnderXZ(x, z, eps);
305
+ var y = (bestY > -Infinity) ? (bestY + R + 0.03) : Math.max(this.minY, (min.y + max.y) * 0.5);
306
+
307
+ var target = new pc.Vec3(x, y, z);
308
+ target = this._resolveCollisions(target, this.maxResolveIters);
309
+ this._clampPosition(target);
310
+ this.entity.setPosition(target);
311
+ };
312
+
313
  // ======================== Contraintes génériques ===========================
314
  FreeCamera.prototype._bboxEnabled = function () {
315
  return (this.Xmin < this.Xmax) && (this.Ymin < this.Ymax) && (this.Zmin < this.Zmax);
 
363
 
364
  for (var i = 0; i < this._colliders.length; i++) {
365
  var aabb = this._colliders[i].aabb;
366
+ var amin = aabb.getMin(); var amax = aabb.getMax();
367
 
368
  // Point le plus proche sur l'AABB
369
+ var cx = pc.math.clamp(p.x, amin.x, amax.x);
370
+ var cy = pc.math.clamp(p.y, amin.y, amax.y);
371
+ var cz = pc.math.clamp(p.z, amin.z, amax.z);
372
 
373
  var dx = p.x - cx, dy = p.y - cy, dz = p.z - cz;
374
  var distSq = dx*dx + dy*dy + dz*dz;
 
383
  p.z += (dz / dist) * pen;
384
  } else {
385
  // pousser selon l'axe le plus "proche"
386
+ var ex = Math.min(Math.abs(p.x - amin.x), Math.abs(amax.x - p.x));
387
+ var ey = Math.min(Math.abs(p.y - amin.y), Math.abs(amax.y - p.y));
388
+ var ez = Math.min(Math.abs(p.z - amin.z), Math.abs(amax.z - p.z));
389
+ if (ex <= ey && ex <= ez) p.x += (Math.abs(p.x - amin.x) < Math.abs(amax.x - p.x) ? -pen : pen);
390
+ else if (ey <= ex && ey <= ez) p.y += (Math.abs(p.y - amin.y) < Math.abs(amax.y - p.y) ? -pen : pen);
391
+ else p.z += (Math.abs(p.z - amin.z) < Math.abs(amax.z - p.z) ? -pen : pen);
392
  }
393
  moved = true;
394
  }