Spaces:
Running
Running
Update ar.html
Browse files
ar.html
CHANGED
|
@@ -32,7 +32,10 @@
|
|
| 32 |
|
| 33 |
/* --- ENGINE GRAFICO (Camera e Canvas) --- */
|
| 34 |
|
| 35 |
-
video
|
|
|
|
|
|
|
|
|
|
| 36 |
position: fixed !important;
|
| 37 |
top: 0 !important;
|
| 38 |
left: 0 !important;
|
|
@@ -45,6 +48,18 @@
|
|
| 45 |
margin: 0 !important;
|
| 46 |
padding: 0 !important;
|
| 47 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
.a-canvas {
|
| 50 |
position: fixed !important;
|
|
@@ -128,6 +143,15 @@
|
|
| 128 |
color: white;
|
| 129 |
font-weight: bold;
|
| 130 |
box-shadow: 0 0 15px rgba(255, 0, 85, 0.5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
}
|
| 132 |
</style>
|
| 133 |
</head>
|
|
@@ -156,7 +180,6 @@
|
|
| 156 |
<span id="sys-status" class="text-[#00ff41] font-bold text-lg tracking-wider animate-pulse">SCANNING...</span>
|
| 157 |
<span class="text-[8px] text-white/40 font-mono mt-1">FPS: <span id="fps-counter">60</span> | STABLE_MODE: ON</span>
|
| 158 |
</div>
|
| 159 |
-
<!-- Pulsante chiusura (Decorativo o funzionale se necessario) -->
|
| 160 |
<div class="w-10 h-10 flex items-center justify-center border border-[#00ff41]/50 rounded-full bg-black/40 backdrop-blur-md opacity-50">
|
| 161 |
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#00ff41" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
| 162 |
</div>
|
|
@@ -202,7 +225,6 @@
|
|
| 202 |
</button>
|
| 203 |
|
| 204 |
<!-- AR SCENE -->
|
| 205 |
-
<!-- Rimosso il filterBeta troppo basso che causava lag, tornati a valori bilanciati per il JS Smoothing -->
|
| 206 |
<a-scene
|
| 207 |
mindar-image="imageTargetSrc: img/targets.mind; filterMinCF:0.0001; filterBeta: 0.01; uiLoading: no; uiScanning: no; missTolerance: 20; warmupTolerance: 5; maxTrack: 1"
|
| 208 |
color-space="sRGB"
|
|
@@ -211,14 +233,37 @@
|
|
| 211 |
device-orientation-permission-ui="enabled: false">
|
| 212 |
|
| 213 |
<a-assets>
|
|
|
|
| 214 |
<video id="vid" src="./img/video.mp4" preload="auto" loop muted playsinline webkit-playsinline crossorigin="anonymous"></video>
|
| 215 |
</a-assets>
|
| 216 |
|
| 217 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
|
| 219 |
-
<!-- TARGET MINDAR -->
|
| 220 |
<a-entity id="example-target" mindar-image-target="targetIndex: 0">
|
| 221 |
-
<!-- GRUPPO CONTENUTO: Mettiamo tutto qui dentro per poterlo spostare facilmente -->
|
| 222 |
<a-entity id="ar-content-group">
|
| 223 |
<!-- VIDEO -->
|
| 224 |
<a-entity id="myVid"
|
|
@@ -263,8 +308,8 @@
|
|
| 263 |
const fpsCounter = document.querySelector("#fps-counter");
|
| 264 |
const fallbackBtn = document.querySelector("#fallback-btn");
|
| 265 |
|
| 266 |
-
const
|
| 267 |
-
const
|
| 268 |
const manualCloseBtn = document.querySelector("#manual-close-btn");
|
| 269 |
|
| 270 |
let isAudioEnabled = false;
|
|
@@ -275,28 +320,21 @@
|
|
| 275 |
let smoothPositionX = 0;
|
| 276 |
let smoothPositionY = 0;
|
| 277 |
let smoothPositionZ = 0;
|
| 278 |
-
const SMOOTHING_FACTOR = 0.1; //
|
| 279 |
|
| 280 |
// CUSTOM SMOOTHING LAYER
|
| 281 |
const myVidEntity = document.querySelector("#myVid");
|
| 282 |
|
| 283 |
setInterval(() => {
|
| 284 |
-
// Applica solo
|
| 285 |
if(myVidEntity && target.object3D.visible && !isManualMode) {
|
| 286 |
const currentPos = target.object3D.position;
|
| 287 |
-
|
| 288 |
-
// Rimosso il Deadzone Threshold che causava la "frizione"
|
| 289 |
-
// Torniamo a un puro Lerp fluido
|
| 290 |
smoothPositionX += (currentPos.x - smoothPositionX) * SMOOTHING_FACTOR;
|
| 291 |
smoothPositionY += (currentPos.y - smoothPositionY) * SMOOTHING_FACTOR;
|
| 292 |
smoothPositionZ += (currentPos.z - smoothPositionZ) * SMOOTHING_FACTOR;
|
| 293 |
|
| 294 |
-
//
|
| 295 |
-
//
|
| 296 |
-
// Questo script dovrebbe idealmente agire su un oggetto slegato, ma qui lasciamo
|
| 297 |
-
// che MindAR gestisca il grosso e il filterBeta faccia il resto.
|
| 298 |
-
// Per un vero custom smoothing, bisognerebbe disaccoppiare la gerarchia,
|
| 299 |
-
// ma dato che ti piaceva "prima", torniamo alla config standard di MindAR + CSS anims.
|
| 300 |
}
|
| 301 |
}, 16);
|
| 302 |
|
|
@@ -306,7 +344,7 @@
|
|
| 306 |
fpsCounter.innerText = fakeFps;
|
| 307 |
}, 1000);
|
| 308 |
|
| 309 |
-
// Loading
|
| 310 |
const nerdMessages = [
|
| 311 |
"Calibrating sensors...",
|
| 312 |
"Loading neural net...",
|
|
@@ -315,19 +353,15 @@
|
|
| 315 |
];
|
| 316 |
let msgIndex = 0;
|
| 317 |
|
| 318 |
-
// Caricamento Veloce
|
| 319 |
const loadInterval = setInterval(() => {
|
| 320 |
loadProgress += 0.55;
|
| 321 |
-
|
| 322 |
if(loadProgress > 100) loadProgress = 100;
|
| 323 |
loadingPercent.innerText = Math.floor(loadProgress) + "%";
|
| 324 |
progressBar.style.width = loadProgress + "%";
|
| 325 |
-
|
| 326 |
if(Math.floor(loadProgress) % 25 === 0 && msgIndex < nerdMessages.length) {
|
| 327 |
document.querySelector('#load-msg').innerText = nerdMessages[msgIndex];
|
| 328 |
msgIndex++;
|
| 329 |
}
|
| 330 |
-
|
| 331 |
if(loadProgress >= 100) {
|
| 332 |
clearInterval(loadInterval);
|
| 333 |
document.querySelector('#load-msg').innerText = "System Ready.";
|
|
@@ -337,7 +371,6 @@
|
|
| 337 |
// AR PRONTA
|
| 338 |
scene.addEventListener("arReady", () => {
|
| 339 |
console.log("✅ AR SYSTEM ONLINE");
|
| 340 |
-
|
| 341 |
const checkLoad = setInterval(() => {
|
| 342 |
if(loadProgress >= 100) {
|
| 343 |
clearInterval(checkLoad);
|
|
@@ -345,15 +378,13 @@
|
|
| 345 |
loader.style.opacity = "0";
|
| 346 |
setTimeout(() => loader.style.display = "none", 500);
|
| 347 |
|
| 348 |
-
// TIMER 10 SECONDI
|
| 349 |
-
// ORA APPARE SEMPRE (indipendentemente dal marker trovato)
|
| 350 |
setTimeout(() => {
|
| 351 |
if (!isManualMode) {
|
| 352 |
fallbackBtn.classList.remove('hidden');
|
| 353 |
fallbackBtn.style.display = 'flex';
|
| 354 |
}
|
| 355 |
}, 10000);
|
| 356 |
-
|
| 357 |
}, 500);
|
| 358 |
}
|
| 359 |
}, 100);
|
|
@@ -362,23 +393,17 @@
|
|
| 362 |
// TARGET FOUND
|
| 363 |
target.addEventListener("targetFound", () => {
|
| 364 |
if(isManualMode) return;
|
| 365 |
-
|
| 366 |
video.play();
|
| 367 |
statusText.innerText = "LOCKED";
|
| 368 |
statusText.style.color = "#fff";
|
| 369 |
statusText.classList.remove("animate-pulse");
|
| 370 |
aimOverlay.style.opacity = "0";
|
| 371 |
-
|
| 372 |
-
// NON nascondiamo più il bottone fallback se è già apparso
|
| 373 |
-
// Se appare dopo 10 secondi, rimane lì come opzione
|
| 374 |
-
|
| 375 |
if(navigator.vibrate) navigator.vibrate(50);
|
| 376 |
});
|
| 377 |
|
| 378 |
// TARGET LOST
|
| 379 |
target.addEventListener("targetLost", () => {
|
| 380 |
if(isManualMode) return;
|
| 381 |
-
|
| 382 |
video.pause();
|
| 383 |
statusText.innerText = "SEARCHING...";
|
| 384 |
statusText.style.color = "#00ff41";
|
|
@@ -394,9 +419,7 @@
|
|
| 394 |
audioText.innerText = "AUDIO ON";
|
| 395 |
audioBtn.classList.add("bg-[#00ff41]", "text-black");
|
| 396 |
audioBtn.classList.remove("bg-[#00ff41]/10", "text-[#00ff41]");
|
| 397 |
-
|
| 398 |
if(navigator.vibrate) navigator.vibrate(50);
|
| 399 |
-
|
| 400 |
setTimeout(() => {
|
| 401 |
audioBtn.style.opacity = "0";
|
| 402 |
audioBtn.style.pointerEvents = "none";
|
|
@@ -409,18 +432,11 @@
|
|
| 409 |
isManualMode = true;
|
| 410 |
console.log("🛠 MANUAL OVERRIDE ENGAGED");
|
| 411 |
|
| 412 |
-
// 1.
|
| 413 |
-
|
| 414 |
-
camera.appendChild(contentGroup);
|
| 415 |
-
|
| 416 |
-
// FIX IMPORTANTE: Rendiamo il contenuto esplicitamente visibile
|
| 417 |
-
// MindAR potrebbe averlo nascosto quando ha perso il target
|
| 418 |
-
contentGroup.setAttribute("visible", "true");
|
| 419 |
|
| 420 |
-
// 2.
|
| 421 |
-
|
| 422 |
-
contentGroup.setAttribute("scale", "0.5 0.5 0.5");
|
| 423 |
-
contentGroup.setAttribute("rotation", "0 0 0");
|
| 424 |
|
| 425 |
// 3. UI Updates
|
| 426 |
aimOverlay.style.display = "none";
|
|
|
|
| 32 |
|
| 33 |
/* --- ENGINE GRAFICO (Camera e Canvas) --- */
|
| 34 |
|
| 35 |
+
/* FIX Z-INDEX: Applichiamo lo stile SOLO al video della webcam generato da MindAR */
|
| 36 |
+
/* MindAR non da una classe specifica facile, ma è l'unico video diretto nel body solitamente.
|
| 37 |
+
Tuttavia, per sicurezza usiamo una regola che esclude il nostro #vid */
|
| 38 |
+
video:not(#vid) {
|
| 39 |
position: fixed !important;
|
| 40 |
top: 0 !important;
|
| 41 |
left: 0 !important;
|
|
|
|
| 48 |
margin: 0 !important;
|
| 49 |
padding: 0 !important;
|
| 50 |
}
|
| 51 |
+
|
| 52 |
+
/* Nascondiamo il video sorgente dalla vista (ma deve esistere per la texture) */
|
| 53 |
+
#vid {
|
| 54 |
+
position: absolute;
|
| 55 |
+
top: 0;
|
| 56 |
+
left: 0;
|
| 57 |
+
width: 1px;
|
| 58 |
+
height: 1px;
|
| 59 |
+
opacity: 0;
|
| 60 |
+
z-index: -100;
|
| 61 |
+
pointer-events: none;
|
| 62 |
+
}
|
| 63 |
|
| 64 |
.a-canvas {
|
| 65 |
position: fixed !important;
|
|
|
|
| 143 |
color: white;
|
| 144 |
font-weight: bold;
|
| 145 |
box-shadow: 0 0 15px rgba(255, 0, 85, 0.5);
|
| 146 |
+
cursor: pointer;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.animate-fade-in {
|
| 150 |
+
animation: fadeIn 0.5s ease-out forwards;
|
| 151 |
+
}
|
| 152 |
+
@keyframes fadeIn {
|
| 153 |
+
from { opacity: 0; transform: translateY(10px); }
|
| 154 |
+
to { opacity: 1; transform: translateY(0); }
|
| 155 |
}
|
| 156 |
</style>
|
| 157 |
</head>
|
|
|
|
| 180 |
<span id="sys-status" class="text-[#00ff41] font-bold text-lg tracking-wider animate-pulse">SCANNING...</span>
|
| 181 |
<span class="text-[8px] text-white/40 font-mono mt-1">FPS: <span id="fps-counter">60</span> | STABLE_MODE: ON</span>
|
| 182 |
</div>
|
|
|
|
| 183 |
<div class="w-10 h-10 flex items-center justify-center border border-[#00ff41]/50 rounded-full bg-black/40 backdrop-blur-md opacity-50">
|
| 184 |
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#00ff41" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
| 185 |
</div>
|
|
|
|
| 225 |
</button>
|
| 226 |
|
| 227 |
<!-- AR SCENE -->
|
|
|
|
| 228 |
<a-scene
|
| 229 |
mindar-image="imageTargetSrc: img/targets.mind; filterMinCF:0.0001; filterBeta: 0.01; uiLoading: no; uiScanning: no; missTolerance: 20; warmupTolerance: 5; maxTrack: 1"
|
| 230 |
color-space="sRGB"
|
|
|
|
| 233 |
device-orientation-permission-ui="enabled: false">
|
| 234 |
|
| 235 |
<a-assets>
|
| 236 |
+
<!-- IL VIDEO SORGENTE (nascosto visivamente via CSS ma caricato qui) -->
|
| 237 |
<video id="vid" src="./img/video.mp4" preload="auto" loop muted playsinline webkit-playsinline crossorigin="anonymous"></video>
|
| 238 |
</a-assets>
|
| 239 |
|
| 240 |
+
<!-- CAMERA + HUD MANUALE -->
|
| 241 |
+
<a-camera position="0 0 0" look-controls="enabled: false">
|
| 242 |
+
<!-- Questo gruppo è fissato alla camera (HUD) ma inizialmente INVISIBILE -->
|
| 243 |
+
<!-- Lo accendiamo solo in modalità manuale -->
|
| 244 |
+
<a-entity id="manual-hud" visible="false" position="0 0 -1.5" scale="0.5 0.5 0.5">
|
| 245 |
+
<!-- VIDEO -->
|
| 246 |
+
<a-entity
|
| 247 |
+
geometry="primitive: plane; width: 1.6; height: 0.9"
|
| 248 |
+
material="src: #vid; shader: flat; transparent: true; opacity: 1">
|
| 249 |
+
</a-entity>
|
| 250 |
+
|
| 251 |
+
<!-- CORNICI (Copiate dalla versione AR) -->
|
| 252 |
+
<a-plane position="0 0.47 0" width="1.68" height="0.03" color="#00ff41" material="shader: flat">
|
| 253 |
+
<a-animation attribute="opacity" from="1" to="0.7" dur="100" direction="alternate" repeat="indefinite"></a-animation>
|
| 254 |
+
</a-plane>
|
| 255 |
+
<a-plane position="0 -0.47 0" width="1.68" height="0.03" color="#00ff41" material="shader: flat"></a-plane>
|
| 256 |
+
<a-plane position="-0.83 0 0" width="0.03" height="0.96" color="#00ff41" material="shader: flat"></a-plane>
|
| 257 |
+
<a-plane position="0.83 0 0" width="0.03" height="0.96" color="#00ff41" material="shader: flat">
|
| 258 |
+
<a-animation attribute="opacity" from="1" to="0.7" dur="150" direction="alternate" repeat="indefinite"></a-animation>
|
| 259 |
+
</a-plane>
|
| 260 |
+
<a-text value="MANUAL_OVERRIDE" color="#ff0055" align="center" width="1.5" position="0 -0.6 0.01"
|
| 261 |
+
font="https://cdn.aframe.io/fonts/Roboto-msdf.json"></a-text>
|
| 262 |
+
</a-entity>
|
| 263 |
+
</a-camera>
|
| 264 |
|
| 265 |
+
<!-- TARGET MINDAR (AR REALE) -->
|
| 266 |
<a-entity id="example-target" mindar-image-target="targetIndex: 0">
|
|
|
|
| 267 |
<a-entity id="ar-content-group">
|
| 268 |
<!-- VIDEO -->
|
| 269 |
<a-entity id="myVid"
|
|
|
|
| 308 |
const fpsCounter = document.querySelector("#fps-counter");
|
| 309 |
const fallbackBtn = document.querySelector("#fallback-btn");
|
| 310 |
|
| 311 |
+
const arContentGroup = document.querySelector("#ar-content-group");
|
| 312 |
+
const manualHud = document.querySelector("#manual-hud");
|
| 313 |
const manualCloseBtn = document.querySelector("#manual-close-btn");
|
| 314 |
|
| 315 |
let isAudioEnabled = false;
|
|
|
|
| 320 |
let smoothPositionX = 0;
|
| 321 |
let smoothPositionY = 0;
|
| 322 |
let smoothPositionZ = 0;
|
| 323 |
+
const SMOOTHING_FACTOR = 0.1; // Smooth originale
|
| 324 |
|
| 325 |
// CUSTOM SMOOTHING LAYER
|
| 326 |
const myVidEntity = document.querySelector("#myVid");
|
| 327 |
|
| 328 |
setInterval(() => {
|
| 329 |
+
// Applica smoothing solo alla parte AR Reale
|
| 330 |
if(myVidEntity && target.object3D.visible && !isManualMode) {
|
| 331 |
const currentPos = target.object3D.position;
|
|
|
|
|
|
|
|
|
|
| 332 |
smoothPositionX += (currentPos.x - smoothPositionX) * SMOOTHING_FACTOR;
|
| 333 |
smoothPositionY += (currentPos.y - smoothPositionY) * SMOOTHING_FACTOR;
|
| 334 |
smoothPositionZ += (currentPos.z - smoothPositionZ) * SMOOTHING_FACTOR;
|
| 335 |
|
| 336 |
+
// Nota: MindAR controlla il parent, noi non modifichiamo il target qui
|
| 337 |
+
// ma le variabili sono pronte se volessimo sganciare il contenuto
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
}
|
| 339 |
}, 16);
|
| 340 |
|
|
|
|
| 344 |
fpsCounter.innerText = fakeFps;
|
| 345 |
}, 1000);
|
| 346 |
|
| 347 |
+
// Loading
|
| 348 |
const nerdMessages = [
|
| 349 |
"Calibrating sensors...",
|
| 350 |
"Loading neural net...",
|
|
|
|
| 353 |
];
|
| 354 |
let msgIndex = 0;
|
| 355 |
|
|
|
|
| 356 |
const loadInterval = setInterval(() => {
|
| 357 |
loadProgress += 0.55;
|
|
|
|
| 358 |
if(loadProgress > 100) loadProgress = 100;
|
| 359 |
loadingPercent.innerText = Math.floor(loadProgress) + "%";
|
| 360 |
progressBar.style.width = loadProgress + "%";
|
|
|
|
| 361 |
if(Math.floor(loadProgress) % 25 === 0 && msgIndex < nerdMessages.length) {
|
| 362 |
document.querySelector('#load-msg').innerText = nerdMessages[msgIndex];
|
| 363 |
msgIndex++;
|
| 364 |
}
|
|
|
|
| 365 |
if(loadProgress >= 100) {
|
| 366 |
clearInterval(loadInterval);
|
| 367 |
document.querySelector('#load-msg').innerText = "System Ready.";
|
|
|
|
| 371 |
// AR PRONTA
|
| 372 |
scene.addEventListener("arReady", () => {
|
| 373 |
console.log("✅ AR SYSTEM ONLINE");
|
|
|
|
| 374 |
const checkLoad = setInterval(() => {
|
| 375 |
if(loadProgress >= 100) {
|
| 376 |
clearInterval(checkLoad);
|
|
|
|
| 378 |
loader.style.opacity = "0";
|
| 379 |
setTimeout(() => loader.style.display = "none", 500);
|
| 380 |
|
| 381 |
+
// TIMER 10 SECONDI: Il bottone appare SEMPRE
|
|
|
|
| 382 |
setTimeout(() => {
|
| 383 |
if (!isManualMode) {
|
| 384 |
fallbackBtn.classList.remove('hidden');
|
| 385 |
fallbackBtn.style.display = 'flex';
|
| 386 |
}
|
| 387 |
}, 10000);
|
|
|
|
| 388 |
}, 500);
|
| 389 |
}
|
| 390 |
}, 100);
|
|
|
|
| 393 |
// TARGET FOUND
|
| 394 |
target.addEventListener("targetFound", () => {
|
| 395 |
if(isManualMode) return;
|
|
|
|
| 396 |
video.play();
|
| 397 |
statusText.innerText = "LOCKED";
|
| 398 |
statusText.style.color = "#fff";
|
| 399 |
statusText.classList.remove("animate-pulse");
|
| 400 |
aimOverlay.style.opacity = "0";
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
if(navigator.vibrate) navigator.vibrate(50);
|
| 402 |
});
|
| 403 |
|
| 404 |
// TARGET LOST
|
| 405 |
target.addEventListener("targetLost", () => {
|
| 406 |
if(isManualMode) return;
|
|
|
|
| 407 |
video.pause();
|
| 408 |
statusText.innerText = "SEARCHING...";
|
| 409 |
statusText.style.color = "#00ff41";
|
|
|
|
| 419 |
audioText.innerText = "AUDIO ON";
|
| 420 |
audioBtn.classList.add("bg-[#00ff41]", "text-black");
|
| 421 |
audioBtn.classList.remove("bg-[#00ff41]/10", "text-[#00ff41]");
|
|
|
|
| 422 |
if(navigator.vibrate) navigator.vibrate(50);
|
|
|
|
| 423 |
setTimeout(() => {
|
| 424 |
audioBtn.style.opacity = "0";
|
| 425 |
audioBtn.style.pointerEvents = "none";
|
|
|
|
| 432 |
isManualMode = true;
|
| 433 |
console.log("🛠 MANUAL OVERRIDE ENGAGED");
|
| 434 |
|
| 435 |
+
// 1. Spegni AR Reale
|
| 436 |
+
arContentGroup.setAttribute("visible", "false");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
|
| 438 |
+
// 2. Accendi HUD Manuale (Clone)
|
| 439 |
+
manualHud.setAttribute("visible", "true");
|
|
|
|
|
|
|
| 440 |
|
| 441 |
// 3. UI Updates
|
| 442 |
aimOverlay.style.display = "none";
|