MikaFil commited on
Commit
e69004a
·
verified ·
1 Parent(s): f366654

Update viewer_ar_ios.js

Browse files
Files changed (1) hide show
  1. viewer_ar_ios.js +63 -55
viewer_ar_ios.js CHANGED
@@ -1,4 +1,4 @@
1
- /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json (+ "sol" + Z vers plafond en mode mur)
2
  - config.json : { "glb_url": "...", "usdz_url": "...", "sol": true|false }
3
  - sol = true → plans horizontaux (sol) + rotation autour de l’axe local Y (haut)
4
  - sol = false → plans verticaux (mur) : Y local = normale du mur, Z local = plafond (Up projeté), slider = rotation autour de Y
@@ -386,32 +386,75 @@
386
  app.root.addChild(modelRoot);
387
  var modelLoaded = false, placedOnce = false;
388
 
389
- // --- Helpers vecteurs/quaternions ---
390
  var UP = new pc.Vec3(0, 1, 0);
391
- function clamp01(x){ return Math.max(0, Math.min(1, x)); }
392
- function clamp(x,a,b){ return Math.max(a, Math.min(b, x)); }
393
- function projOnPlane(v, n){ // v - dot(v,n)*n
394
  var d = v.dot(n);
395
  return new pc.Vec3(v.x - d*n.x, v.y - d*n.y, v.z - d*n.z);
396
  }
397
- function normalizeOr(v, fallback){
398
  var len = v.length();
399
  if (len > 1e-6) { v.scale(1/len); return v; }
400
  return fallback.clone ? fallback.clone() : new pc.Vec3(fallback.x, fallback.y, fallback.z);
401
  }
402
- function signedAngleAround(a, b, axis){
403
- // angle from a to b around axis (radians, signed)
404
- var dot = clamp(a.dot(b), -1, 1);
405
- var ang = Math.acos(dot);
406
- var cross = new pc.Vec3();
407
- cross.cross(a, b);
408
- var sign = (cross.dot(axis) >= 0) ? 1 : -1;
409
- return ang * sign;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  }
411
 
412
  // Mode mur / sol
413
  var wallMode = !PLACE_ON_FLOOR;
414
- var wallBaseRot = new pc.Quat(); // orientation de base alignée au mur (Y=normale, Z=plafond projeté)
415
  var wallAngleDeg = 0;
416
 
417
  // Ombre blob (sol uniquement)
@@ -491,50 +534,14 @@
491
  } else {
492
  wallAngleDeg = y;
493
  var qLocal = new pc.Quat();
494
- qLocal.setFromAxisAngle(pc.Vec3.UP, y); // rotation locale autour de Y
495
  var qFinal = new pc.Quat();
496
- qFinal.mul2(wallBaseRot, qLocal); // base (mur stabilisée) puis twist utilisateur
497
  modelRoot.setRotation(qFinal);
498
  }
499
  updateKnobFromY(y);
500
  }
501
 
502
- // --- Alignement stable pour le mur : Y = normale, Z → plafond ---
503
- function computeWallBase(rot) {
504
- // 1) Y (normale du mur) en monde
505
- var Yw = new pc.Vec3();
506
- rot.transformVector(new pc.Vec3(0,1,0), Yw);
507
- Yw.normalize();
508
-
509
- // 2) Z actuel du modèle en monde (avec rot)
510
- var Zw = new pc.Vec3();
511
- rot.transformVector(new pc.Vec3(0,0,1), Zw);
512
-
513
- // 3) Z désiré = Up projeté sur le plan perpendiculaire à Y
514
- var desiredZ = projOnPlane(UP, Yw);
515
- if (desiredZ.length() < 1e-4) {
516
- // mur quasi horizontal ? (devrait pas arriver) fallback: projeter un forward monde
517
- desiredZ = projOnPlane(new pc.Vec3(0,0,1), Yw);
518
- }
519
- desiredZ = normalizeOr(desiredZ, new pc.Vec3(0,0,1));
520
-
521
- // 4) Z courant projeté sur le même plan
522
- var zProj = projOnPlane(Zw, Yw);
523
- zProj = normalizeOr(zProj, desiredZ);
524
-
525
- // 5) angle signé entre zProj -> desiredZ autour de Y
526
- var angRad = signedAngleAround(zProj, desiredZ, Yw);
527
- var angDeg = angRad * (180 / Math.PI);
528
-
529
- // 6) twist correctif autour de Y pour que Z pointe vers le plafond
530
- var qTwist = new pc.Quat();
531
- qTwist.setFromAxisAngle(Yw, angDeg);
532
-
533
- var out = new pc.Quat();
534
- out.mul2(rot, qTwist); // d'abord rot (Y=normale), puis twist pour aligner Z avec le plafond
535
- return out;
536
- }
537
-
538
  function updateBlobPositionUnder(pos, rotLikePlane) {
539
  if (!blob) return;
540
  blob.setPosition(pos.x, pos.y + BLOB_OFFSET_Y, pos.z);
@@ -651,7 +658,7 @@
651
 
652
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
653
 
654
- // Hit-test principal (reticle + première pose)
655
  app.xr.hitTest.on("available", function () {
656
  app.xr.hitTest.start({
657
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
@@ -678,6 +685,7 @@
678
  } else {
679
  // --- MODE MUR (première pose) ---
680
  wallBaseRot.copy(computeWallBase(rot)); // Y=normale, Z=plafond
 
681
  applyRotationY(0);
682
  }
683
 
@@ -716,7 +724,7 @@
716
  if (!wallMode) {
717
  updateBlobPositionUnder(pos, rot);
718
  } else {
719
- // Recalcule à CHAQUE déplacement: Y=normale courante, Z→plafond
720
  wallBaseRot.copy(computeWallBase(rot));
721
  var qLocal = new pc.Quat();
722
  qLocal.setFromAxisAngle(pc.Vec3.UP, wallAngleDeg);
 
1
+ /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json (+ "sol" + stabilité mur : Z vers plafond)
2
  - config.json : { "glb_url": "...", "usdz_url": "...", "sol": true|false }
3
  - sol = true → plans horizontaux (sol) + rotation autour de l’axe local Y (haut)
4
  - sol = false → plans verticaux (mur) : Y local = normale du mur, Z local = plafond (Up projeté), slider = rotation autour de Y
 
386
  app.root.addChild(modelRoot);
387
  var modelLoaded = false, placedOnce = false;
388
 
389
+ // --- Helpers vecteurs/quaternions stables ---
390
  var UP = new pc.Vec3(0, 1, 0);
391
+ function projOnPlane(v, n) {
 
 
392
  var d = v.dot(n);
393
  return new pc.Vec3(v.x - d*n.x, v.y - d*n.y, v.z - d*n.z);
394
  }
395
+ function normalizeOr(v, fallback) {
396
  var len = v.length();
397
  if (len > 1e-6) { v.scale(1/len); return v; }
398
  return fallback.clone ? fallback.clone() : new pc.Vec3(fallback.x, fallback.y, fallback.z);
399
  }
400
+ function buildWallBasisFromNormal(N) {
401
+ var Y = N.clone().normalize(); // normale du mur → axe Y local
402
+ var Z = projOnPlane(UP, Y); // Z local vers plafond (projeté dans le plan du mur)
403
+ if (Z.lengthSq() < 1e-8) Z = projOnPlane(new pc.Vec3(0,0,1), Y);
404
+ Z.normalize();
405
+ var X = new pc.Vec3(); X.cross(Z, Y).normalize(); // X = Z × Y
406
+ // Re-orthonormalise Z = Y × X
407
+ Z.cross(Y, X).normalize();
408
+ return { X: X, Y: Y, Z: Z };
409
+ }
410
+ function quatFromBasis(X, Y, Z) {
411
+ // colonnes = X,Y,Z
412
+ var m00 = X.x, m01 = Y.x, m02 = Z.x;
413
+ var m10 = X.y, m11 = Y.y, m12 = Z.y;
414
+ var m20 = X.z, m21 = Y.z, m22 = Z.z;
415
+
416
+ var t = m00 + m11 + m22;
417
+ var q = new pc.Quat();
418
+ if (t > 0) {
419
+ var s = Math.sqrt(t + 1.0) * 2;
420
+ q.w = 0.25 * s;
421
+ q.x = (m21 - m12) / s;
422
+ q.y = (m02 - m20) / s;
423
+ q.z = (m10 - m01) / s;
424
+ } else if (m00 > m11 && m00 > m22) {
425
+ var s = Math.sqrt(1.0 + m00 - m11 - m22) * 2;
426
+ q.w = (m21 - m12) / s;
427
+ q.x = 0.25 * s;
428
+ q.y = (m01 + m10) / s;
429
+ q.z = (m02 + m20) / s;
430
+ } else if (m11 > m22) {
431
+ var s2 = Math.sqrt(1.0 + m11 - m00 - m22) * 2;
432
+ q.w = (m02 - m20) / s2;
433
+ q.x = (m01 + m10) / s2;
434
+ q.y = 0.25 * s2;
435
+ q.z = (m12 + m21) / s2;
436
+ } else {
437
+ var s3 = Math.sqrt(1.0 + m22 - m00 - m11) * 2;
438
+ q.w = (m10 - m01) / s3;
439
+ q.x = (m02 + m20) / s3;
440
+ q.y = (m12 + m21) / s3;
441
+ q.z = 0.25 * s3;
442
+ }
443
+ q.normalize();
444
+ return q;
445
+ }
446
+ function computeWallBase(rot) {
447
+ // Normal du mur = Y de la pose
448
+ var N = new pc.Vec3();
449
+ rot.transformVector(new pc.Vec3(0, 1, 0), N);
450
+ N.normalize();
451
+ var basis = buildWallBasisFromNormal(N);
452
+ return quatFromBasis(basis.X, basis.Y, basis.Z); // Y=normale, Z=plafond
453
  }
454
 
455
  // Mode mur / sol
456
  var wallMode = !PLACE_ON_FLOOR;
457
+ var wallBaseRot = new pc.Quat(); // orientation de base alignée au mur (Y=normale, Z=plafond)
458
  var wallAngleDeg = 0;
459
 
460
  // Ombre blob (sol uniquement)
 
534
  } else {
535
  wallAngleDeg = y;
536
  var qLocal = new pc.Quat();
537
+ qLocal.setFromAxisAngle(pc.Vec3.UP, y); // rotation locale autour de Y
538
  var qFinal = new pc.Quat();
539
+ qFinal.mul2(wallBaseRot, qLocal); // base mur (Z→plafond), puis twist utilisateur
540
  modelRoot.setRotation(qFinal);
541
  }
542
  updateKnobFromY(y);
543
  }
544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  function updateBlobPositionUnder(pos, rotLikePlane) {
546
  if (!blob) return;
547
  blob.setPosition(pos.x, pos.y + BLOB_OFFSET_Y, pos.z);
 
658
 
659
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
660
 
661
+ // Hit-test principal (reticle + premier placement)
662
  app.xr.hitTest.on("available", function () {
663
  app.xr.hitTest.start({
664
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
 
685
  } else {
686
  // --- MODE MUR (première pose) ---
687
  wallBaseRot.copy(computeWallBase(rot)); // Y=normale, Z=plafond
688
+ modelRoot.setRotation(wallBaseRot);
689
  applyRotationY(0);
690
  }
691
 
 
724
  if (!wallMode) {
725
  updateBlobPositionUnder(pos, rot);
726
  } else {
727
+ // Recalcule à CHAQUE déplacement: Y=normale courante, Z→plafond (stable)
728
  wallBaseRot.copy(computeWallBase(rot));
729
  var qLocal = new pc.Quat();
730
  qLocal.setFromAxisAngle(pc.Vec3.UP, wallAngleDeg);