Spaces:
Running
Running
Update deplacement_dans_env/viewer_pr_env.js
Browse files
deplacement_dans_env/viewer_pr_env.js
CHANGED
|
@@ -72,7 +72,7 @@ async function ensureOrbitScriptsLoaded() {
|
|
| 72 |
|
| 73 |
window.__PLY_ORBIT_LOADING__ = new Promise((resolve, reject) => {
|
| 74 |
const s = document.createElement("script");
|
| 75 |
-
//
|
| 76 |
s.src = "https://mikafil-viewer-sgos.static.hf.space/deplacement_dans_env/ctrl_camera_pr_env.js";
|
| 77 |
s.async = true;
|
| 78 |
s.onload = () => {
|
|
@@ -96,8 +96,8 @@ async function ensureOrbitScriptsLoaded() {
|
|
| 96 |
let pc;
|
| 97 |
export let app = null;
|
| 98 |
let cameraEntity = null;
|
| 99 |
-
let modelEntity = null;
|
| 100 |
-
let envEntity = null;
|
| 101 |
let viewerInitialized = false;
|
| 102 |
let resizeObserver = null;
|
| 103 |
|
|
@@ -114,7 +114,7 @@ let color_bg_hex, color_bg, espace_expo_bool;
|
|
| 114 |
-------------------------------------------- */
|
| 115 |
|
| 116 |
export async function initializeViewer(config, instanceId) {
|
| 117 |
-
//
|
| 118 |
if (viewerInitialized) return;
|
| 119 |
|
| 120 |
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
@@ -124,12 +124,10 @@ export async function initializeViewer(config, instanceId) {
|
|
| 124 |
sogsUrl = config.sogs_json_url || null;
|
| 125 |
glbUrl = config.glb_url || null;
|
| 126 |
|
| 127 |
-
//
|
| 128 |
distanceMin = config.minZoom !== undefined
|
| 129 |
-
? parseFloat(config.minZoom)
|
| 130 |
-
: (config.distanceMin !== undefined
|
| 131 |
-
? parseFloat(config.distanceMin) // si quelqu'un fournit distanceMin
|
| 132 |
-
: 1);
|
| 133 |
|
| 134 |
minAngle = parseFloat(config.minAngle ?? "-45");
|
| 135 |
maxAngle = parseFloat(config.maxAngle ?? "90");
|
|
@@ -145,7 +143,7 @@ export async function initializeViewer(config, instanceId) {
|
|
| 145 |
modelRotationY = config.modelRotationY !== undefined ? parseFloat(config.modelRotationY) : 0;
|
| 146 |
modelRotationZ = config.modelRotationZ !== undefined ? parseFloat(config.modelRotationZ) : 0;
|
| 147 |
|
| 148 |
-
// défauts à 1 (et non 0) pour éviter l'invisibilité si
|
| 149 |
presentoirScaleX = config.presentoirScaleX !== undefined ? parseFloat(config.presentoirScaleX) : 1;
|
| 150 |
presentoirScaleY = config.presentoirScaleY !== undefined ? parseFloat(config.presentoirScaleY) : 1;
|
| 151 |
presentoirScaleZ = config.presentoirScaleZ !== undefined ? parseFloat(config.presentoirScaleZ) : 1;
|
|
@@ -182,7 +180,7 @@ export async function initializeViewer(config, instanceId) {
|
|
| 182 |
canvas.setAttribute("tabindex", "0");
|
| 183 |
viewerContainer.insertBefore(canvas, progressDialog);
|
| 184 |
|
| 185 |
-
// interactions de base
|
| 186 |
canvas.style.touchAction = "none";
|
| 187 |
canvas.style.webkitTouchCallout = "none";
|
| 188 |
canvas.addEventListener("gesturestart", (e) => e.preventDefault());
|
|
@@ -198,60 +196,33 @@ export async function initializeViewer(config, instanceId) {
|
|
| 198 |
);
|
| 199 |
canvas.addEventListener(
|
| 200 |
"wheel",
|
| 201 |
-
(e) => {
|
| 202 |
-
e.preventDefault();
|
| 203 |
-
},
|
| 204 |
{ passive: false }
|
| 205 |
);
|
| 206 |
|
| 207 |
// Bloque le scroll page uniquement quand le pointeur est sur le canvas
|
| 208 |
-
const scrollKeys = new Set([
|
| 209 |
-
"ArrowUp",
|
| 210 |
-
"ArrowDown",
|
| 211 |
-
"ArrowLeft",
|
| 212 |
-
"ArrowRight",
|
| 213 |
-
"PageUp",
|
| 214 |
-
"PageDown",
|
| 215 |
-
"Home",
|
| 216 |
-
"End",
|
| 217 |
-
" ",
|
| 218 |
-
"Space",
|
| 219 |
-
"Spacebar"
|
| 220 |
-
]);
|
| 221 |
let isPointerOverCanvas = false;
|
| 222 |
const focusCanvas = () => canvas.focus({ preventScroll: true });
|
| 223 |
|
| 224 |
-
const onPointerEnter = () => {
|
| 225 |
-
isPointerOverCanvas = true;
|
| 226 |
-
focusCanvas();
|
| 227 |
-
};
|
| 228 |
const onPointerLeave = () => {
|
| 229 |
isPointerOverCanvas = false;
|
| 230 |
if (document.activeElement === canvas) canvas.blur();
|
| 231 |
};
|
| 232 |
-
const onCanvasBlur = () => {
|
| 233 |
-
isPointerOverCanvas = false;
|
| 234 |
-
};
|
| 235 |
|
| 236 |
canvas.addEventListener("pointerenter", onPointerEnter);
|
| 237 |
canvas.addEventListener("pointerleave", onPointerLeave);
|
| 238 |
canvas.addEventListener("mouseenter", onPointerEnter);
|
| 239 |
canvas.addEventListener("mouseleave", onPointerLeave);
|
| 240 |
canvas.addEventListener("mousedown", focusCanvas);
|
| 241 |
-
canvas.addEventListener(
|
| 242 |
-
"touchstart",
|
| 243 |
-
() => {
|
| 244 |
-
focusCanvas();
|
| 245 |
-
},
|
| 246 |
-
{ passive: false }
|
| 247 |
-
);
|
| 248 |
canvas.addEventListener("blur", onCanvasBlur);
|
| 249 |
|
| 250 |
const onKeyDownCapture = (e) => {
|
| 251 |
if (!isPointerOverCanvas) return;
|
| 252 |
-
if (scrollKeys.has(e.key) || scrollKeys.has(e.code))
|
| 253 |
-
e.preventDefault();
|
| 254 |
-
}
|
| 255 |
};
|
| 256 |
window.addEventListener("keydown", onKeyDownCapture, true);
|
| 257 |
|
|
@@ -260,7 +231,7 @@ export async function initializeViewer(config, instanceId) {
|
|
| 260 |
// --- Charge PlayCanvas lib ESM (une par module/instance) ---
|
| 261 |
if (!pc) {
|
| 262 |
pc = await import("https://esm.run/playcanvas");
|
| 263 |
-
window.pc = pc; // utile pour tooltips.js
|
| 264 |
}
|
| 265 |
|
| 266 |
// --- Crée l'Application ---
|
|
@@ -276,7 +247,7 @@ export async function initializeViewer(config, instanceId) {
|
|
| 276 |
opts.graphicsDevice = device;
|
| 277 |
opts.mouse = new pc.Mouse(canvas);
|
| 278 |
opts.touch = new pc.TouchDevice(canvas);
|
| 279 |
-
opts.keyboard = new pc.Keyboard(canvas); // clavier
|
| 280 |
opts.componentSystems = [
|
| 281 |
pc.RenderComponentSystem,
|
| 282 |
pc.CameraComponentSystem,
|
|
@@ -305,11 +276,8 @@ export async function initializeViewer(config, instanceId) {
|
|
| 305 |
|
| 306 |
// Nettoyage complet
|
| 307 |
app.on("destroy", () => {
|
| 308 |
-
try {
|
| 309 |
-
resizeObserver.disconnect();
|
| 310 |
-
} catch {}
|
| 311 |
if (opts.keyboard && opts.keyboard.detach) opts.keyboard.detach();
|
| 312 |
-
|
| 313 |
window.removeEventListener("keydown", onKeyDownCapture, true);
|
| 314 |
|
| 315 |
canvas.removeEventListener("pointerenter", onPointerEnter);
|
|
@@ -323,21 +291,14 @@ export async function initializeViewer(config, instanceId) {
|
|
| 323 |
|
| 324 |
// --- Enregistre les assets (SAUF orbit script : chargé globalement) ---
|
| 325 |
const assets = {};
|
| 326 |
-
|
| 327 |
-
if (
|
| 328 |
-
assets.sogs = new pc.Asset("gsplat", "gsplat", { url: sogsUrl });
|
| 329 |
-
}
|
| 330 |
-
|
| 331 |
-
// Ajoute le GLB d'environnement / présentoir si fourni
|
| 332 |
-
if (glbUrl) {
|
| 333 |
-
assets.env = new pc.Asset("env", "container", { url: glbUrl });
|
| 334 |
-
}
|
| 335 |
|
| 336 |
for (const k in assets) app.assets.add(assets[k]);
|
| 337 |
|
| 338 |
const loader = new pc.AssetListLoader(Object.values(assets), app.assets);
|
| 339 |
|
| 340 |
-
// Assure le chargement unique des scripts caméra
|
| 341 |
await ensureOrbitScriptsLoaded();
|
| 342 |
|
| 343 |
loader.load(() => {
|
|
@@ -362,7 +323,7 @@ export async function initializeViewer(config, instanceId) {
|
|
| 362 |
receiveShadows: true
|
| 363 |
});
|
| 364 |
|
| 365 |
-
// Position/rotation/échelle
|
| 366 |
envEntity.setLocalPosition(0, 0, 0);
|
| 367 |
envEntity.setLocalEulerAngles(0, 0, 0);
|
| 368 |
envEntity.setLocalScale(presentoirScaleX, presentoirScaleY, presentoirScaleZ);
|
|
@@ -370,10 +331,13 @@ export async function initializeViewer(config, instanceId) {
|
|
| 370 |
app.root.addChild(envEntity);
|
| 371 |
}
|
| 372 |
|
| 373 |
-
//
|
| 374 |
-
const
|
|
|
|
|
|
|
|
|
|
| 375 |
|
| 376 |
-
// --- Caméra + scripts d’input ---
|
| 377 |
cameraEntity = new pc.Entity("camera");
|
| 378 |
cameraEntity.addComponent("camera", {
|
| 379 |
clearColor: new pc.Color(color_bg[0], color_bg[1], color_bg[2], color_bg[3]),
|
|
@@ -381,20 +345,27 @@ export async function initializeViewer(config, instanceId) {
|
|
| 381 |
farClip: 100
|
| 382 |
});
|
| 383 |
cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
|
| 384 |
-
if (
|
| 385 |
cameraEntity.addComponent("script");
|
| 386 |
|
| 387 |
-
//
|
| 388 |
const orbitAttrs = {
|
| 389 |
-
|
|
|
|
|
|
|
|
|
|
| 390 |
inertiaFactor: 0.2,
|
| 391 |
-
distanceMin: distanceMin,
|
| 392 |
pitchAngleMax: maxAngle,
|
| 393 |
pitchAngleMin: minAngle,
|
|
|
|
|
|
|
|
|
|
| 394 |
yawAngleMax: maxAzimuth,
|
| 395 |
yawAngleMin: minAzimuth,
|
| 396 |
-
|
| 397 |
-
|
|
|
|
|
|
|
| 398 |
};
|
| 399 |
|
| 400 |
// Injecte la BBox uniquement si présente dans le config (évite Infinity explicite)
|
|
@@ -413,15 +384,17 @@ export async function initializeViewer(config, instanceId) {
|
|
| 413 |
if (Zmin !== undefined) orbitAttrs.Zmin = Zmin;
|
| 414 |
if (Zmax !== undefined) orbitAttrs.Zmax = Zmax;
|
| 415 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
cameraEntity.script.create("orbitCamera", { attributes: orbitAttrs });
|
| 417 |
cameraEntity.script.create("orbitCameraInputMouse");
|
| 418 |
cameraEntity.script.create("orbitCameraInputTouch");
|
| 419 |
-
cameraEntity.script.create("orbitCameraInputKeyboard", {
|
| 420 |
-
attributes: {
|
| 421 |
-
forwardSpeed: 1.2,
|
| 422 |
-
strafeSpeed: 1.2
|
| 423 |
-
}
|
| 424 |
-
});
|
| 425 |
app.root.addChild(cameraEntity);
|
| 426 |
|
| 427 |
app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
|
|
@@ -437,19 +410,15 @@ export async function initializeViewer(config, instanceId) {
|
|
| 437 |
tooltipsModule.initializeTooltips({
|
| 438 |
app,
|
| 439 |
cameraEntity,
|
| 440 |
-
modelEntity:
|
| 441 |
tooltipsUrl: config.tooltips_url,
|
| 442 |
defaultVisible: !!config.showTooltipsDefault,
|
| 443 |
moveDuration: config.tooltipMoveDuration || 0.6
|
| 444 |
});
|
| 445 |
})
|
| 446 |
-
.catch(() => {
|
| 447 |
-
/* optional */
|
| 448 |
-
});
|
| 449 |
}
|
| 450 |
-
} catch (e) {
|
| 451 |
-
/* optional */
|
| 452 |
-
}
|
| 453 |
|
| 454 |
viewerInitialized = true;
|
| 455 |
});
|
|
@@ -463,7 +432,7 @@ export function resetViewerCamera() {
|
|
| 463 |
try {
|
| 464 |
if (!cameraEntity || !app) return;
|
| 465 |
|
| 466 |
-
//
|
| 467 |
const targetEntity = modelEntity || envEntity;
|
| 468 |
const targetPos = targetEntity ? targetEntity.getPosition().clone() : new pc.Vec3(0, 0, 0);
|
| 469 |
|
|
@@ -481,10 +450,14 @@ export function resetViewerCamera() {
|
|
| 481 |
cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
|
| 482 |
cameraEntity.lookAt(targetPos);
|
| 483 |
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
|
|
|
|
|
|
|
|
|
| 487 |
|
|
|
|
| 488 |
const rot = tempEnt.getRotation();
|
| 489 |
const fwd = new pc.Vec3();
|
| 490 |
rot.transformVector(pc.Vec3.FORWARD, fwd);
|
|
|
|
| 72 |
|
| 73 |
window.__PLY_ORBIT_LOADING__ = new Promise((resolve, reject) => {
|
| 74 |
const s = document.createElement("script");
|
| 75 |
+
// Script caméra libre + collisions (garde le nom public "orbitCamera")
|
| 76 |
s.src = "https://mikafil-viewer-sgos.static.hf.space/deplacement_dans_env/ctrl_camera_pr_env.js";
|
| 77 |
s.async = true;
|
| 78 |
s.onload = () => {
|
|
|
|
| 96 |
let pc;
|
| 97 |
export let app = null;
|
| 98 |
let cameraEntity = null;
|
| 99 |
+
let modelEntity = null; // gsplat principal (oeuvre)
|
| 100 |
+
let envEntity = null; // GLB d'environnement / présentoir
|
| 101 |
let viewerInitialized = false;
|
| 102 |
let resizeObserver = null;
|
| 103 |
|
|
|
|
| 114 |
-------------------------------------------- */
|
| 115 |
|
| 116 |
export async function initializeViewer(config, instanceId) {
|
| 117 |
+
// une seule initialisation par import
|
| 118 |
if (viewerInitialized) return;
|
| 119 |
|
| 120 |
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
|
|
| 124 |
sogsUrl = config.sogs_json_url || null;
|
| 125 |
glbUrl = config.glb_url || null;
|
| 126 |
|
| 127 |
+
// rétro-compat minZoom => distanceMin (conservé pour compat mais non utilisé par la free cam)
|
| 128 |
distanceMin = config.minZoom !== undefined
|
| 129 |
+
? parseFloat(config.minZoom)
|
| 130 |
+
: (config.distanceMin !== undefined ? parseFloat(config.distanceMin) : 1);
|
|
|
|
|
|
|
| 131 |
|
| 132 |
minAngle = parseFloat(config.minAngle ?? "-45");
|
| 133 |
maxAngle = parseFloat(config.maxAngle ?? "90");
|
|
|
|
| 143 |
modelRotationY = config.modelRotationY !== undefined ? parseFloat(config.modelRotationY) : 0;
|
| 144 |
modelRotationZ = config.modelRotationZ !== undefined ? parseFloat(config.modelRotationZ) : 0;
|
| 145 |
|
| 146 |
+
// défauts à 1 (et non 0) pour éviter l'invisibilité si manquants
|
| 147 |
presentoirScaleX = config.presentoirScaleX !== undefined ? parseFloat(config.presentoirScaleX) : 1;
|
| 148 |
presentoirScaleY = config.presentoirScaleY !== undefined ? parseFloat(config.presentoirScaleY) : 1;
|
| 149 |
presentoirScaleZ = config.presentoirScaleZ !== undefined ? parseFloat(config.presentoirScaleZ) : 1;
|
|
|
|
| 180 |
canvas.setAttribute("tabindex", "0");
|
| 181 |
viewerContainer.insertBefore(canvas, progressDialog);
|
| 182 |
|
| 183 |
+
// interactions de base (éviter scroll/gestes par défaut sur le canvas)
|
| 184 |
canvas.style.touchAction = "none";
|
| 185 |
canvas.style.webkitTouchCallout = "none";
|
| 186 |
canvas.addEventListener("gesturestart", (e) => e.preventDefault());
|
|
|
|
| 196 |
);
|
| 197 |
canvas.addEventListener(
|
| 198 |
"wheel",
|
| 199 |
+
(e) => { e.preventDefault(); },
|
|
|
|
|
|
|
| 200 |
{ passive: false }
|
| 201 |
);
|
| 202 |
|
| 203 |
// Bloque le scroll page uniquement quand le pointeur est sur le canvas
|
| 204 |
+
const scrollKeys = new Set([ "ArrowUp","ArrowDown","ArrowLeft","ArrowRight","PageUp","PageDown","Home","End"," ","Space","Spacebar" ]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
let isPointerOverCanvas = false;
|
| 206 |
const focusCanvas = () => canvas.focus({ preventScroll: true });
|
| 207 |
|
| 208 |
+
const onPointerEnter = () => { isPointerOverCanvas = true; focusCanvas(); };
|
|
|
|
|
|
|
|
|
|
| 209 |
const onPointerLeave = () => {
|
| 210 |
isPointerOverCanvas = false;
|
| 211 |
if (document.activeElement === canvas) canvas.blur();
|
| 212 |
};
|
| 213 |
+
const onCanvasBlur = () => { isPointerOverCanvas = false; };
|
|
|
|
|
|
|
| 214 |
|
| 215 |
canvas.addEventListener("pointerenter", onPointerEnter);
|
| 216 |
canvas.addEventListener("pointerleave", onPointerLeave);
|
| 217 |
canvas.addEventListener("mouseenter", onPointerEnter);
|
| 218 |
canvas.addEventListener("mouseleave", onPointerLeave);
|
| 219 |
canvas.addEventListener("mousedown", focusCanvas);
|
| 220 |
+
canvas.addEventListener("touchstart", () => { focusCanvas(); }, { passive: false });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
canvas.addEventListener("blur", onCanvasBlur);
|
| 222 |
|
| 223 |
const onKeyDownCapture = (e) => {
|
| 224 |
if (!isPointerOverCanvas) return;
|
| 225 |
+
if (scrollKeys.has(e.key) || scrollKeys.has(e.code)) e.preventDefault();
|
|
|
|
|
|
|
| 226 |
};
|
| 227 |
window.addEventListener("keydown", onKeyDownCapture, true);
|
| 228 |
|
|
|
|
| 231 |
// --- Charge PlayCanvas lib ESM (une par module/instance) ---
|
| 232 |
if (!pc) {
|
| 233 |
pc = await import("https://esm.run/playcanvas");
|
| 234 |
+
window.pc = pc; // utile pour tooltips.js et debug
|
| 235 |
}
|
| 236 |
|
| 237 |
// --- Crée l'Application ---
|
|
|
|
| 247 |
opts.graphicsDevice = device;
|
| 248 |
opts.mouse = new pc.Mouse(canvas);
|
| 249 |
opts.touch = new pc.TouchDevice(canvas);
|
| 250 |
+
opts.keyboard = new pc.Keyboard(canvas); // scoping clavier au canvas
|
| 251 |
opts.componentSystems = [
|
| 252 |
pc.RenderComponentSystem,
|
| 253 |
pc.CameraComponentSystem,
|
|
|
|
| 276 |
|
| 277 |
// Nettoyage complet
|
| 278 |
app.on("destroy", () => {
|
| 279 |
+
try { resizeObserver.disconnect(); } catch {}
|
|
|
|
|
|
|
| 280 |
if (opts.keyboard && opts.keyboard.detach) opts.keyboard.detach();
|
|
|
|
| 281 |
window.removeEventListener("keydown", onKeyDownCapture, true);
|
| 282 |
|
| 283 |
canvas.removeEventListener("pointerenter", onPointerEnter);
|
|
|
|
| 291 |
|
| 292 |
// --- Enregistre les assets (SAUF orbit script : chargé globalement) ---
|
| 293 |
const assets = {};
|
| 294 |
+
if (sogsUrl) assets.sogs = new pc.Asset("gsplat", "gsplat", { url: sogsUrl });
|
| 295 |
+
if (glbUrl) assets.env = new pc.Asset("env", "container", { url: glbUrl });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
for (const k in assets) app.assets.add(assets[k]);
|
| 298 |
|
| 299 |
const loader = new pc.AssetListLoader(Object.values(assets), app.assets);
|
| 300 |
|
| 301 |
+
// Assure le chargement unique des scripts de caméra
|
| 302 |
await ensureOrbitScriptsLoaded();
|
| 303 |
|
| 304 |
loader.load(() => {
|
|
|
|
| 323 |
receiveShadows: true
|
| 324 |
});
|
| 325 |
|
| 326 |
+
// Position/rotation/échelle (adaptables)
|
| 327 |
envEntity.setLocalPosition(0, 0, 0);
|
| 328 |
envEntity.setLocalEulerAngles(0, 0, 0);
|
| 329 |
envEntity.setLocalScale(presentoirScaleX, presentoirScaleY, presentoirScaleZ);
|
|
|
|
| 331 |
app.root.addChild(envEntity);
|
| 332 |
}
|
| 333 |
|
| 334 |
+
// Entité qui sert d'ancrage visuel (lookAt) : l'oeuvre si dispo, sinon l'env
|
| 335 |
+
const focusVisual = modelEntity || envEntity;
|
| 336 |
+
|
| 337 |
+
// Racine de collision pour la free cam : l'environnement GLB de préférence
|
| 338 |
+
const collisionRoot = envEntity || modelEntity || null;
|
| 339 |
|
| 340 |
+
// --- Caméra + scripts d’input (free cam + collisions) ---
|
| 341 |
cameraEntity = new pc.Entity("camera");
|
| 342 |
cameraEntity.addComponent("camera", {
|
| 343 |
clearColor: new pc.Color(color_bg[0], color_bg[1], color_bg[2], color_bg[3]),
|
|
|
|
| 345 |
farClip: 100
|
| 346 |
});
|
| 347 |
cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
|
| 348 |
+
if (focusVisual) cameraEntity.lookAt(focusVisual.getPosition());
|
| 349 |
cameraEntity.addComponent("script");
|
| 350 |
|
| 351 |
+
// Prépare les attributs : les champs inconnus sont ignorés par le script
|
| 352 |
const orbitAttrs = {
|
| 353 |
+
// Collision root : active le blocage contre le GLB
|
| 354 |
+
focusEntity: collisionRoot || undefined,
|
| 355 |
+
|
| 356 |
+
// Inertie/angles (pitch clampé), yaw libre
|
| 357 |
inertiaFactor: 0.2,
|
|
|
|
| 358 |
pitchAngleMax: maxAngle,
|
| 359 |
pitchAngleMin: minAngle,
|
| 360 |
+
|
| 361 |
+
// Compat (ignorés par le free cam, mais gardés pour ne pas casser la config)
|
| 362 |
+
distanceMin: distanceMin,
|
| 363 |
yawAngleMax: maxAzimuth,
|
| 364 |
yawAngleMin: minAzimuth,
|
| 365 |
+
frameOnStart: false,
|
| 366 |
+
|
| 367 |
+
// Contraintes position
|
| 368 |
+
minY: minY
|
| 369 |
};
|
| 370 |
|
| 371 |
// Injecte la BBox uniquement si présente dans le config (évite Infinity explicite)
|
|
|
|
| 384 |
if (Zmin !== undefined) orbitAttrs.Zmin = Zmin;
|
| 385 |
if (Zmax !== undefined) orbitAttrs.Zmax = Zmax;
|
| 386 |
|
| 387 |
+
// Paramètres collision optionnels depuis la config (sinon valeurs par défaut du script)
|
| 388 |
+
if (config.collisionRadius !== undefined) orbitAttrs.collisionRadius = parseFloat(config.collisionRadius);
|
| 389 |
+
if (config.collisionEpsilon !== undefined) orbitAttrs.collisionEpsilon = parseFloat(config.collisionEpsilon);
|
| 390 |
+
if (config.moveSpeed !== undefined) orbitAttrs.moveSpeed = parseFloat(config.moveSpeed);
|
| 391 |
+
if (config.strafeSpeed !== undefined) orbitAttrs.strafeSpeed = parseFloat(config.strafeSpeed);
|
| 392 |
+
if (config.dollySpeed !== undefined) orbitAttrs.dollySpeed = parseFloat(config.dollySpeed);
|
| 393 |
+
|
| 394 |
cameraEntity.script.create("orbitCamera", { attributes: orbitAttrs });
|
| 395 |
cameraEntity.script.create("orbitCameraInputMouse");
|
| 396 |
cameraEntity.script.create("orbitCameraInputTouch");
|
| 397 |
+
cameraEntity.script.create("orbitCameraInputKeyboard", { attributes: { acceleration: 1.0 } });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 398 |
app.root.addChild(cameraEntity);
|
| 399 |
|
| 400 |
app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
|
|
|
|
| 410 |
tooltipsModule.initializeTooltips({
|
| 411 |
app,
|
| 412 |
cameraEntity,
|
| 413 |
+
modelEntity: focusVisual,
|
| 414 |
tooltipsUrl: config.tooltips_url,
|
| 415 |
defaultVisible: !!config.showTooltipsDefault,
|
| 416 |
moveDuration: config.tooltipMoveDuration || 0.6
|
| 417 |
});
|
| 418 |
})
|
| 419 |
+
.catch(() => { /* optional */ });
|
|
|
|
|
|
|
| 420 |
}
|
| 421 |
+
} catch (e) { /* optional */ }
|
|
|
|
|
|
|
| 422 |
|
| 423 |
viewerInitialized = true;
|
| 424 |
});
|
|
|
|
| 432 |
try {
|
| 433 |
if (!cameraEntity || !app) return;
|
| 434 |
|
| 435 |
+
// cible visuelle : gsplat prioritaire, sinon glb, sinon (0,0,0)
|
| 436 |
const targetEntity = modelEntity || envEntity;
|
| 437 |
const targetPos = targetEntity ? targetEntity.getPosition().clone() : new pc.Vec3(0, 0, 0);
|
| 438 |
|
|
|
|
| 450 |
cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
|
| 451 |
cameraEntity.lookAt(targetPos);
|
| 452 |
|
| 453 |
+
// Ces champs existent dans la version "orbit" historique ; notre free cam les ignore,
|
| 454 |
+
// mais on conserve l'initialisation pour compat.
|
| 455 |
+
if (orbitCam) {
|
| 456 |
+
orbitCam._targetDistance = Math.max(distanceMin, dist);
|
| 457 |
+
orbitCam._distance = Math.max(distanceMin, dist);
|
| 458 |
+
}
|
| 459 |
|
| 460 |
+
// Recalcule yaw/pitch cibles pour aligner l'orientation
|
| 461 |
const rot = tempEnt.getRotation();
|
| 462 |
const fwd = new pc.Vec3();
|
| 463 |
rot.transformVector(pc.Vec3.FORWARD, fwd);
|