storysplat-viewer 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -76232,805 +76232,228 @@ WebGPUEngine._GlslangDefaultOptions = {
76232
76232
  WebGPUEngine._InstanceId = 0;
76233
76233
 
76234
76234
  /**
76235
- * Basic check if the current environment appears to be a mobile device based on user agent.
76236
- * Note: User agent sniffing is generally unreliable; consider feature detection instead if possible.
76237
- * This function might return false positives or negatives.
76238
- * @returns True if the user agent string matches common mobile patterns, false otherwise.
76239
- */
76240
- const isMobileDevice = () => {
76241
- if (typeof navigator === 'undefined' || !navigator.userAgent) {
76242
- return false; // Cannot determine without navigator.userAgent
76243
- }
76244
- return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase());
76245
- };
76246
- /**
76247
- * Converts a simple {r, g, b, a} object to a BabylonJS Color4 object.
76248
- * Handles potential undefined input. Alpha defaults to 1 if not provided.
76249
- * @param colorObj - The color object { r, g, b, a? } (values 0-1).
76250
- * @param defaultColor - The default Color4 to return if input is invalid.
76251
- * @returns A BabylonJS Color4 object.
76252
- */
76253
- const toColor4 = (colorObj, defaultColor = new Color4(1, 1, 1, 1) // Default to opaque white
76254
- ) => {
76255
- if (colorObj && typeof colorObj.r === 'number' && typeof colorObj.g === 'number' && typeof colorObj.b === 'number') {
76256
- const alpha = typeof colorObj.a === 'number' ? colorObj.a : 1.0;
76257
- return new Color4(colorObj.r, colorObj.g, colorObj.b, alpha);
76258
- }
76259
- return defaultColor;
76260
- };
76261
- /**
76262
- * Converts a simple {x, y, z} object to a BabylonJS Vector3 object.
76263
- * Handles potential undefined input.
76264
- * @param vecObj - The vector object { x, y, z }.
76265
- * @param defaultVector - The default Vector3 to return if input is invalid.
76266
- * @returns A BabylonJS Vector3 object.
76267
- */
76268
- const toVector3 = (vecObj, defaultVector = new Vector3(0, 0, 0) // Default to origin
76269
- ) => {
76270
- if (vecObj && typeof vecObj.x === 'number' && typeof vecObj.y === 'number' && typeof vecObj.z === 'number') {
76271
- return new Vector3(vecObj.x, vecObj.y, vecObj.z);
76272
- }
76273
- return defaultVector;
76274
- };
76275
- /**
76276
- * Converts a simple {_x, _y, _z, _w} object to a BabylonJS Quaternion object.
76277
- * Handles potential undefined input.
76278
- * @param quatObj - The quaternion object { _x, _y, _z, _w }.
76279
- * @param defaultQuaternion - The default Quaternion to return if input is invalid.
76280
- * @returns A BabylonJS Quaternion object.
76235
+ * Class used to store geometry data (vertex buffers + index buffer)
76281
76236
  */
76282
- const toQuaternion = (quatObj, defaultQuaternion = Quaternion.Identity() // Default to identity
76283
- ) => {
76284
- if (quatObj && typeof quatObj._x === 'number' && typeof quatObj._y === 'number' && typeof quatObj._z === 'number' && typeof quatObj._w === 'number') {
76285
- return new Quaternion(quatObj._x, quatObj._y, quatObj._z, quatObj._w);
76237
+ class Geometry {
76238
+ /**
76239
+ * Gets or sets the Bias Vector to apply on the bounding elements (box/sphere), the max extend is computed as v += v * bias.x + bias.y, the min is computed as v -= v * bias.x + bias.y
76240
+ */
76241
+ get boundingBias() {
76242
+ return this._boundingBias;
76286
76243
  }
76287
- return defaultQuaternion;
76288
- };
76289
- /**
76290
- * Converts a hex color string (e.g., "#RRGGBB") to a BabylonJS Color3 object.
76291
- * Handles potential undefined or invalid input.
76292
- * @param hex - The hex color string.
76293
- * @param defaultColor - The default Color3 to return if input is invalid.
76294
- * @returns A BabylonJS Color3 object.
76295
- */
76296
- const hexToColor3 = (hex, defaultColor = new Color3(1, 1, 1) // Default to white
76297
- ) => {
76298
- if (!hex) {
76299
- return defaultColor;
76244
+ /**
76245
+ * Gets or sets the Bias Vector to apply on the bounding elements (box/sphere), the max extend is computed as v += v * bias.x + bias.y, the min is computed as v -= v * bias.x + bias.y
76246
+ */
76247
+ set boundingBias(value) {
76248
+ if (this._boundingBias) {
76249
+ this._boundingBias.copyFrom(value);
76250
+ }
76251
+ else {
76252
+ this._boundingBias = value.clone();
76253
+ }
76254
+ this._updateBoundingInfo(true, null);
76300
76255
  }
76301
- try {
76302
- return Color3.FromHexString(hex);
76256
+ /**
76257
+ * Static function used to attach a new empty geometry to a mesh
76258
+ * @param mesh defines the mesh to attach the geometry to
76259
+ * @returns the new Geometry
76260
+ */
76261
+ static CreateGeometryForMesh(mesh) {
76262
+ const geometry = new Geometry(Geometry.RandomId(), mesh.getScene());
76263
+ geometry.applyToMesh(mesh);
76264
+ return geometry;
76303
76265
  }
76304
- catch (error) {
76305
- console.warn(`Invalid hex color format for hexToColor3: ${hex}. Using default.`, error);
76306
- return defaultColor;
76266
+ /** Get the list of meshes using this geometry */
76267
+ get meshes() {
76268
+ return this._meshes;
76307
76269
  }
76308
- };
76309
- /**
76310
- * Applies opacity to a mesh and its children's materials.
76311
- * Handles StandardMaterial and potentially PBRMaterial alpha/transparencyMode.
76312
- * @param mesh - The root mesh to apply opacity to.
76313
- * @param opacity - The opacity value (0-1).
76314
- */
76315
- function applyOpacityToMeshAndChildren(mesh, opacity) {
76316
- if (!mesh)
76317
- return;
76318
- const applyToMaterial = (material) => {
76319
- if (material) {
76320
- const clampedOpacity = Math.max(0, Math.min(1, opacity)); // Ensure opacity is 0-1
76321
- // StandardMaterial
76322
- if ('alpha' in material) {
76323
- material.alpha = clampedOpacity;
76324
- }
76325
- // PBRMaterial - requires transparencyMode adjustment
76326
- if ('transparencyMode' in material) {
76327
- const pbrMaterial = material;
76328
- if (clampedOpacity < 1) {
76329
- // Use ALPHABLEND (numeric value 2) if transparent
76330
- pbrMaterial.transparencyMode = 2; // BABYLON.Constants.PBRMATERIAL_ALPHABLEND;
76331
- pbrMaterial.alpha = clampedOpacity;
76332
- }
76333
- else {
76334
- // Reset to opaque if opacity is 1
76335
- pbrMaterial.transparencyMode = null; // Or 0 for OPAQUE
76336
- pbrMaterial.alpha = 1;
76337
- }
76338
- }
76270
+ /**
76271
+ * Creates a new geometry
76272
+ * @param id defines the unique ID
76273
+ * @param scene defines the hosting scene
76274
+ * @param vertexData defines the VertexData used to get geometry data
76275
+ * @param updatable defines if geometry must be updatable (false by default)
76276
+ * @param mesh defines the mesh that will be associated with the geometry
76277
+ */
76278
+ constructor(id, scene, vertexData, updatable = false, mesh = null) {
76279
+ /**
76280
+ * Gets the delay loading state of the geometry (none by default which means not delayed)
76281
+ */
76282
+ this.delayLoadState = 0;
76283
+ this._totalVertices = 0;
76284
+ this._isDisposed = false;
76285
+ this._extend = {
76286
+ minimum: new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE),
76287
+ maximum: new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE),
76288
+ };
76289
+ this._indexBufferIsUpdatable = false;
76290
+ this._positionsCache = [];
76291
+ /** @internal */
76292
+ this._parentContainer = null;
76293
+ /**
76294
+ * If set to true (false by default), the bounding info applied to the meshes sharing this geometry will be the bounding info defined at the class level
76295
+ * and won't be computed based on the vertex positions (which is what we get when useBoundingInfoFromGeometry = false)
76296
+ */
76297
+ this.useBoundingInfoFromGeometry = false;
76298
+ this._scene = scene || EngineStore.LastCreatedScene;
76299
+ if (!this._scene) {
76300
+ return;
76339
76301
  }
76340
- };
76341
- // Apply to the mesh's own material
76342
- applyToMaterial(mesh.material);
76343
- // Apply to materials of direct child meshes recursively (optional, depending on need)
76344
- // If you only want direct children, use getChildMeshes(true)
76345
- // If you want all descendants, use getChildMeshes(false) - potentially expensive
76346
- const children = mesh.getChildMeshes ? mesh.getChildMeshes(true) : []; // Direct children only
76347
- children.forEach(childMesh => {
76348
- applyOpacityToMeshAndChildren(childMesh, opacity); // Recursive call
76349
- });
76350
- }
76351
-
76352
- /**
76353
- * Initializes the BabylonJS Engine and Scene.
76354
- * Attempts to use WebGPU first, falling back to WebGL2.
76355
- * @param canvas - The HTML canvas element to render to.
76356
- * @param data - The StorySplat data containing scene properties.
76357
- * @param options - Viewer configuration options.
76358
- * @returns A promise resolving with the initialized engine and scene, or rejecting on failure.
76359
- */
76360
- async function initializeSceneAndEngine(canvas, data, options // Allow passing disableWebGPU internally
76361
- ) {
76362
- var _a;
76363
- // Ensure canvas fills its container
76364
- canvas.style.width = '100%';
76365
- canvas.style.height = '100%';
76366
- canvas.style.display = 'block';
76367
- canvas.style.verticalAlign = 'bottom'; // Prevent extra space below canvas
76368
- let engine;
76369
- const disableWebGPU = (_a = options === null || options === void 0 ? void 0 : options.disableWebGPU) !== null && _a !== void 0 ? _a : false;
76370
- if (disableWebGPU) {
76371
- // Force WebGL2
76372
- try {
76373
- engine = new Engine(canvas, true);
76374
- console.log("StorySplat Viewer: WebGPU disabled by option, using WebGL2.");
76302
+ this.id = id;
76303
+ this.uniqueId = this._scene.getUniqueId();
76304
+ this._engine = this._scene.getEngine();
76305
+ this._meshes = [];
76306
+ //Init vertex buffer cache
76307
+ this._vertexBuffers = {};
76308
+ this._indices = [];
76309
+ this._updatable = updatable;
76310
+ // vertexData
76311
+ if (vertexData) {
76312
+ this.setAllVerticesData(vertexData, updatable);
76375
76313
  }
76376
- catch (err) {
76377
- console.error("StorySplat Viewer: Failed to initialize WebGL2 engine:", err);
76378
- throw new Error("Failed to initialize WebGL2 engine.");
76314
+ else {
76315
+ this._totalVertices = 0;
76379
76316
  }
76380
- }
76381
- else {
76382
- // Try WebGPU first, then fallback
76383
- try {
76384
- // Check if running in a secure context, required for WebGPU
76385
- if (!window.isSecureContext) {
76386
- throw new Error("WebGPU requires a secure context (HTTPS or localhost).");
76387
- }
76388
- if (WebGPUEngine && WebGPUEngine.IsSupportedAsync) {
76389
- const supported = await WebGPUEngine.IsSupportedAsync;
76390
- if (supported) {
76391
- const webgpuEngine = new WebGPUEngine(canvas, {
76392
- // Explicitly request high-performance adapter
76393
- powerPreference: "high-performance",
76394
- // Enable validation in development environments if needed
76395
- // enableAllFeatures: true // Use cautiously, might impact performance
76396
- });
76397
- await webgpuEngine.initAsync();
76398
- engine = webgpuEngine; // Use double assertion
76399
- console.log("StorySplat Viewer: WebGPU engine initialized.");
76400
- }
76401
- else {
76402
- throw new Error("WebGPU is not supported on this browser/device.");
76403
- }
76404
- }
76405
- else {
76406
- throw new Error("WebGPUEngine not available or IsSupportedAsync missing.");
76407
- }
76317
+ if (this._engine.getCaps().vertexArrayObject) {
76318
+ this._vertexArrayObjects = {};
76408
76319
  }
76409
- catch (err) {
76410
- // Fallback to WebGL2 if WebGPU fails or is unavailable
76411
- console.warn("StorySplat Viewer: WebGPU unavailable or initialization failed, falling back to WebGL2:", err);
76412
- try {
76413
- engine = new Engine(canvas, true, {
76414
- // Options for WebGL engine if needed
76415
- // preserveDrawingBuffer: true, // Example option
76416
- // stencil: true
76417
- });
76418
- console.log("StorySplat Viewer: Initialized WebGL2 engine as fallback.");
76419
- }
76420
- catch (webglErr) {
76421
- console.error("StorySplat Viewer: Failed to initialize WebGL2 engine as fallback:", webglErr);
76422
- throw new Error("Failed to initialize WebGL2 engine as fallback.");
76423
- }
76320
+ // applyToMesh
76321
+ if (mesh) {
76322
+ this.applyToMesh(mesh);
76323
+ mesh.computeWorldMatrix(true);
76424
76324
  }
76425
76325
  }
76426
- if (!engine) {
76427
- throw new Error("Failed to initialize any Babylon engine.");
76326
+ /**
76327
+ * Gets the current extend of the geometry
76328
+ */
76329
+ get extend() {
76330
+ return this._extend;
76428
76331
  }
76429
- const scene = new Scene(engine);
76430
- // Set background color from data or options, default to dark background to match HTML exports
76431
- const defaultBgColor = new Color4(0.12, 0.12, 0.12, 1.0); // Dark gray background to match HTML exports
76432
- const bgColorOption = options === null || options === void 0 ? void 0 : options.backgroundColor; // Options still use {r,g,b,a} format
76433
- if (bgColorOption) {
76434
- scene.clearColor = toColor4(bgColorOption, defaultBgColor);
76332
+ /**
76333
+ * Gets the hosting scene
76334
+ * @returns the hosting Scene
76335
+ */
76336
+ getScene() {
76337
+ return this._scene;
76435
76338
  }
76436
- else if (data.backgroundColor) {
76437
- // Convert hex from data to Color3, then to Color4
76438
- const defaultColor3 = new Color3(defaultBgColor.r, defaultBgColor.g, defaultBgColor.b); // Create default Color3
76439
- scene.clearColor = hexToColor3(data.backgroundColor, defaultColor3).toColor4(1.0);
76339
+ /**
76340
+ * Gets the hosting engine
76341
+ * @returns the hosting Engine
76342
+ */
76343
+ getEngine() {
76344
+ return this._engine;
76440
76345
  }
76441
- else {
76442
- scene.clearColor = defaultBgColor;
76346
+ /**
76347
+ * Defines if the geometry is ready to use
76348
+ * @returns true if the geometry is ready to be used
76349
+ */
76350
+ isReady() {
76351
+ return this.delayLoadState === 1 || this.delayLoadState === 0;
76443
76352
  }
76444
- // Ambient color is typically handled by lights (e.g., HemisphericLight groundColor)
76445
- // Removing direct ambientColor setting from sceneProperties
76446
- // Set a default ambient if needed, but lightingManager should handle specifics
76447
- scene.ambientColor = new Color3(0.2, 0.2, 0.2); // Default dim ambient
76448
- // Add resize listener
76449
- const resizeHandler = () => {
76450
- engine.resize();
76451
- };
76452
- window.addEventListener("resize", resizeHandler);
76453
- // Add a cleanup function to the scene context
76454
- scene.onDisposeObservable.addOnce(() => {
76455
- console.log("StorySplat Viewer: Disposing scene, removing resize listener.");
76456
- window.removeEventListener("resize", resizeHandler);
76457
- // Engine disposal is handled separately in the destroy function
76458
- });
76459
- return { engine, scene };
76460
- }
76461
-
76462
- // Removed import for shared wheelHandler
76463
- const DEFAULT_CAMERA_POSITION = new Vector3(0, 1.6, -5);
76464
- const DEFAULT_CAMERA_TARGET = Vector3.Zero();
76465
- const DEFAULT_FOV = 0.8;
76466
- const DEFAULT_MIN_Z = 0.1;
76467
- const DEFAULT_MAX_Z = 1000;
76468
- const DEFAULT_SPEED = 0.1;
76469
- const DEFAULT_INERTIA = 0.9;
76470
- const DEFAULT_ANGULAR_SENSIBILITY = 2000;
76471
- // Removed DEFAULT_WHEEL_DELTA_PERCENTAGE and DEFAULT_WHEEL_STEP as they are handled by shared handler or options
76472
- // Walk Mode Defaults
76473
- const DEFAULT_APPLY_GRAVITY = true;
76474
- const DEFAULT_ELLIPSOID = new Vector3(0.5, 0.9, 0.5);
76475
- const DEFAULT_GRAVITY = -9.81;
76476
- class CameraManager {
76477
- constructor(canvas, scene, data, options, // Correct options parameter
76478
- analyticsManager // Correct analyticsManager parameter
76479
- ) {
76480
- var _a, _b, _c;
76481
- this._camera = null;
76482
- this._currentMode = 'explore';
76483
- this._uiManager = null; // Add UIManager reference
76484
- this._analyticsManager = null; // Add AnalyticsManager reference
76485
- this._canvas = canvas;
76486
- this._scene = scene;
76487
- this._data = data; // Store data object
76488
- this._viewerOptions = options;
76489
- this._analyticsManager = analyticsManager || null; // Store analytics manager
76490
- // this._uiManager = uiManager || null; // Don't store here initially
76491
- this._isMobile = isMobileDevice();
76492
- this._initializeCamera();
76493
- // Prioritize initial mode from data, then options, then default to 'explore'
76494
- const initialMode = (_c = (_a = this._data.initialCameraMode) !== null && _a !== void 0 ? _a : (_b = this._viewerOptions) === null || _b === void 0 ? void 0 : _b.defaultCameraMode) !== null && _c !== void 0 ? _c : 'explore';
76495
- this.setCameraMode(initialMode, true); // Assert type as CameraMode
76496
- this._setupSceneDispose();
76497
- // Removed _setupCustomWheelInput call
76353
+ /**
76354
+ * Gets a value indicating that the geometry should not be serialized
76355
+ */
76356
+ get doNotSerialize() {
76357
+ for (let index = 0; index < this._meshes.length; index++) {
76358
+ if (!this._meshes[index].doNotSerialize) {
76359
+ return false;
76360
+ }
76361
+ }
76362
+ return true;
76498
76363
  }
76499
- get camera() {
76500
- return this._camera;
76364
+ /** @internal */
76365
+ _rebuild() {
76366
+ if (this._vertexArrayObjects) {
76367
+ this._vertexArrayObjects = {};
76368
+ }
76369
+ // Index buffer
76370
+ if (this._meshes.length !== 0 && this._indices) {
76371
+ this._indexBuffer = this._engine.createIndexBuffer(this._indices, this._updatable, "Geometry_" + this.id + "_IndexBuffer");
76372
+ }
76373
+ // Vertex buffers
76374
+ const buffers = new Set();
76375
+ for (const key in this._vertexBuffers) {
76376
+ buffers.add(this._vertexBuffers[key].getWrapperBuffer());
76377
+ }
76378
+ buffers.forEach((buffer) => {
76379
+ buffer._rebuild();
76380
+ });
76501
76381
  }
76502
- get currentMode() {
76503
- return this._currentMode;
76382
+ /**
76383
+ * Affects all geometry data in one call
76384
+ * @param vertexData defines the geometry data
76385
+ * @param updatable defines if the geometry must be flagged as updatable (false as default)
76386
+ */
76387
+ setAllVerticesData(vertexData, updatable) {
76388
+ vertexData.applyToGeometry(this, updatable);
76389
+ this._notifyUpdate();
76504
76390
  }
76505
- _initializeCamera() {
76506
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
76507
- let initialPosition = DEFAULT_CAMERA_POSITION;
76508
- let initialRotation = undefined; // Use rotation instead of target
76509
- // Try to get initial pose from the first waypoint
76510
- const firstWaypoint = (_a = this._data.waypoints) === null || _a === void 0 ? void 0 : _a[0];
76511
- if (firstWaypoint) {
76512
- initialPosition = new Vector3(firstWaypoint.x, firstWaypoint.y, firstWaypoint.z);
76513
- initialRotation = toQuaternion(firstWaypoint.rotation); // Use helper
76391
+ /**
76392
+ * Set specific vertex data
76393
+ * @param kind defines the data kind (Position, normal, etc...)
76394
+ * @param data defines the vertex data to use
76395
+ * @param updatable defines if the vertex must be flagged as updatable (false as default)
76396
+ * @param stride defines the stride to use (0 by default). This value is deduced from the kind value if not specified
76397
+ */
76398
+ setVerticesData(kind, data, updatable = false, stride) {
76399
+ if (updatable && Array.isArray(data)) {
76400
+ // to avoid converting to Float32Array at each draw call in engine.updateDynamicVertexBuffer, we make the conversion a single time here
76401
+ data = new Float32Array(data);
76514
76402
  }
76515
- this._camera = new UniversalCamera("viewerCamera", initialPosition, this._scene);
76516
- // Set initial rotation if derived from waypoint, otherwise set default target
76517
- if (initialRotation) {
76518
- this._camera.rotationQuaternion = initialRotation;
76403
+ const buffer = new VertexBuffer(this._engine, data, kind, {
76404
+ updatable,
76405
+ postponeInternalCreation: this._meshes.length === 0,
76406
+ stride,
76407
+ label: "Geometry_" + this.id + "_" + kind,
76408
+ });
76409
+ this.setVerticesBuffer(buffer);
76410
+ }
76411
+ /**
76412
+ * Removes a specific vertex data
76413
+ * @param kind defines the data kind (Position, normal, etc...)
76414
+ */
76415
+ removeVerticesData(kind) {
76416
+ if (this._vertexBuffers[kind]) {
76417
+ this._vertexBuffers[kind].dispose();
76418
+ delete this._vertexBuffers[kind];
76519
76419
  }
76520
- else {
76521
- this._camera.setTarget(DEFAULT_CAMERA_TARGET);
76420
+ if (this._vertexArrayObjects) {
76421
+ this._disposeVertexArrayObjects();
76522
76422
  }
76523
- // --- Add Standard Inputs ---
76524
- this._camera.inputs.addKeyboard();
76525
- this._camera.inputs.addMouse();
76526
- if (this._isMobile) {
76527
- this._camera.inputs.addTouch();
76423
+ }
76424
+ /**
76425
+ * Affect a vertex buffer to the geometry. the vertexBuffer.getKind() function is used to determine where to store the data
76426
+ * @param buffer defines the vertex buffer to use
76427
+ * @param totalVertices defines the total number of vertices for position kind (could be null)
76428
+ * @param disposeExistingBuffer disposes the existing buffer, if any (default: true)
76429
+ */
76430
+ setVerticesBuffer(buffer, totalVertices = null, disposeExistingBuffer = true) {
76431
+ const kind = buffer.getKind();
76432
+ if (this._vertexBuffers[kind] && disposeExistingBuffer) {
76433
+ this._vertexBuffers[kind].dispose();
76528
76434
  }
76529
- // Keyboard bindings need to be set *after* inputs are added
76530
- this._setKeyboardInputs();
76531
- // --- Apply Common Settings ---
76532
- // Get settings from data (StorySplatData) or options (ViewerOptions)
76533
- this._camera.fov = (_b = this._data.fov) !== null && _b !== void 0 ? _b : DEFAULT_FOV; // FOV comes from data only
76534
- this._camera.minZ = (_e = (_d = (_c = this._viewerOptions) === null || _c === void 0 ? void 0 : _c.cameraMinZ) !== null && _d !== void 0 ? _d : this._data.minClipPlane) !== null && _e !== void 0 ? _e : DEFAULT_MIN_Z; // Use minClipPlane from data
76535
- this._camera.maxZ = (_h = (_g = (_f = this._viewerOptions) === null || _f === void 0 ? void 0 : _f.cameraMaxZ) !== null && _g !== void 0 ? _g : this._data.maxClipPlane) !== null && _h !== void 0 ? _h : DEFAULT_MAX_Z; // Use maxClipPlane from data
76536
- this._camera.speed = (_l = (_k = (_j = this._viewerOptions) === null || _j === void 0 ? void 0 : _j.cameraSpeed) !== null && _k !== void 0 ? _k : this._data.cameraMovementSpeed) !== null && _l !== void 0 ? _l : DEFAULT_SPEED; // Use cameraMovementSpeed from data
76537
- this._camera.inertia = (_o = (_m = this._viewerOptions) === null || _m === void 0 ? void 0 : _m.cameraInertia) !== null && _o !== void 0 ? _o : DEFAULT_INERTIA; // Inertia likely only in options
76538
- // Map cameraRotationSensitivity from data to angularSensibility if options don't provide it
76539
- const angularSensibility = (_q = (_p = this._viewerOptions) === null || _p === void 0 ? void 0 : _p.cameraAngularSensibility) !== null && _q !== void 0 ? _q : (this._data.cameraRotationSensitivity ? (DEFAULT_ANGULAR_SENSIBILITY / (this._data.cameraRotationSensitivity / 4000)) : DEFAULT_ANGULAR_SENSIBILITY); // Simple scaling based on default
76540
- this._camera.angularSensibility = angularSensibility;
76541
- this._camera.touchAngularSensibility = (_s = (_r = this._viewerOptions) === null || _r === void 0 ? void 0 : _r.cameraTouchAngularSensibility) !== null && _s !== void 0 ? _s : angularSensibility; // Use same sensitivity for touch by default
76542
- // Apply Y-axis inversion by negating sensitivity if needed
76543
- const shouldInvert = (_v = (_u = (_t = this._viewerOptions) === null || _t === void 0 ? void 0 : _t.invertCameraRotation) !== null && _u !== void 0 ? _u : this._data.invertCameraRotation) !== null && _v !== void 0 ? _v : false;
76544
- if (shouldInvert) {
76545
- this._camera.angularSensibility *= -1;
76546
- this._camera.touchAngularSensibility *= -1;
76547
- console.log("StorySplat Viewer: Inverting camera Y-axis rotation.");
76435
+ if (buffer._buffer && buffer._ownsBuffer) {
76436
+ buffer._buffer._increaseReferences();
76548
76437
  }
76549
- // --- Wheel Input ---
76550
- // Default wheel input is now used unless options customize it elsewhere.
76551
- // No need to remove default or handle custom wheel here.
76552
- // Initial collision/gravity state
76553
- this._camera.checkCollisions = false;
76554
- this._camera.applyGravity = false;
76555
- // Ellipsoid primarily from options
76556
- this._camera.ellipsoid = ((_w = this._viewerOptions) === null || _w === void 0 ? void 0 : _w.cameraEllipsoid) ? toVector3(this._viewerOptions.cameraEllipsoid) : DEFAULT_ELLIPSOID.clone();
76557
- // Gravity primarily from options
76558
- const sceneGravityY = (_y = (_x = this._scene.gravity) === null || _x === void 0 ? void 0 : _x.y) !== null && _y !== void 0 ? _y : ((_0 = (_z = this._viewerOptions) === null || _z === void 0 ? void 0 : _z.gravity) !== null && _0 !== void 0 ? _0 : DEFAULT_GRAVITY);
76559
- this._scene.gravity = new Vector3(0, sceneGravityY, 0);
76560
- // Enable scene collisions if needed - removed checks for _initialSettings
76561
- if (((_2 = (_1 = this._viewerOptions) === null || _1 === void 0 ? void 0 : _1.allowedCameraModes) === null || _2 === void 0 ? void 0 : _2.includes('walk')) ||
76562
- ((_3 = this._viewerOptions) === null || _3 === void 0 ? void 0 : _3.defaultCameraMode) === 'walk' ||
76563
- ((_4 = this._viewerOptions) === null || _4 === void 0 ? void 0 : _4.exploreCollisions) ||
76564
- ((_5 = this._viewerOptions) === null || _5 === void 0 ? void 0 : _5.walkCollisions) ||
76565
- ((_6 = this._viewerOptions) === null || _6 === void 0 ? void 0 : _6.cameraEllipsoid) || // Check if ellipsoid is defined in options
76566
- this._scene.collisionsEnabled) {
76567
- this._scene.collisionsEnabled = true;
76568
- console.log("StorySplat Viewer: Scene collisions enabled for camera.");
76438
+ this._vertexBuffers[kind] = buffer;
76439
+ const meshes = this._meshes;
76440
+ const numOfMeshes = meshes.length;
76441
+ if (kind === VertexBuffer.PositionKind) {
76442
+ this._totalVertices = totalVertices ?? buffer._maxVerticesCount;
76443
+ this._updateExtend(buffer.getFloatData(this._totalVertices));
76444
+ this._resetPointsArrayCache();
76445
+ // this._extend can be empty if buffer.getFloatData(this._totalVertices) returned null
76446
+ const minimum = (this._extend && this._extend.minimum) || new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
76447
+ const maximum = (this._extend && this._extend.maximum) || new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
76448
+ for (let index = 0; index < numOfMeshes; index++) {
76449
+ const mesh = meshes[index];
76450
+ mesh.buildBoundingInfo(minimum, maximum);
76451
+ mesh._createGlobalSubMesh(mesh.isUnIndexed);
76452
+ mesh.computeWorldMatrix(true);
76453
+ mesh.synchronizeInstances();
76454
+ }
76569
76455
  }
76570
- }
76571
- setCameraMode(mode, immediate = false) {
76572
- var _a, _b, _c, _d, _e, _f, _g;
76573
- if (!this._camera)
76574
- return;
76575
- const allowedModes = (_b = (_a = this._viewerOptions) === null || _a === void 0 ? void 0 : _a.allowedCameraModes) !== null && _b !== void 0 ? _b : ['explore', 'walk', 'tour', 'hybrid'];
76576
- if (!allowedModes.includes(mode)) {
76577
- console.warn(`StorySplat Viewer: Camera mode "${mode}" is not allowed by options. Allowed: [${allowedModes.join(', ')}]`);
76578
- return;
76579
- }
76580
- const oldMode = this._currentMode;
76581
- if (oldMode === mode && !immediate)
76582
- return;
76583
- console.log(`StorySplat Viewer: Setting camera mode to ${mode}`);
76584
- this._currentMode = mode;
76585
- // Detach controls before reconfiguring
76586
- this._camera.detachControl();
76587
- // Reset mode-specific properties
76588
- this._camera.checkCollisions = false;
76589
- this._camera.applyGravity = false;
76590
- // Inputs are now added during initialization and persist.
76591
- // No need to remove/re-add input types here.
76592
- // detachControl/attachControl handles focus.
76593
- switch (mode) {
76594
- case 'explore':
76595
- this._configureExploreMode();
76596
- break;
76597
- case 'walk':
76598
- this._configureWalkMode();
76599
- break;
76600
- case 'tour':
76601
- case 'hybrid':
76602
- this._configureTourMode();
76603
- break;
76604
- default:
76605
- console.warn(`StorySplat Viewer: Unknown camera mode "${mode}". Defaulting to explore.`);
76606
- this._configureExploreMode();
76607
- this._currentMode = 'explore';
76608
- }
76609
- // Re-attach controls if globally enabled
76610
- if (((_c = this._viewerOptions) === null || _c === void 0 ? void 0 : _c.cameraControls) !== false) {
76611
- this._camera.attachControl(this._canvas, true);
76612
- // Removed DEBUG log
76613
- // Inputs are added during initialization, attachControl manages focus.
76614
- }
76615
- else {
76616
- console.log("StorySplat Viewer: Camera controls globally disabled by options.");
76617
- // No activation needed if controls are disabled
76618
- }
76619
- // Trigger callback and update UI
76620
- if (oldMode !== mode) {
76621
- (_e = (_d = this._viewerOptions) === null || _d === void 0 ? void 0 : _d.onCameraModeChanged) === null || _e === void 0 ? void 0 : _e.call(_d, mode);
76622
- (_f = this.onCameraModeChange) === null || _f === void 0 ? void 0 : _f.call(this, mode); // Call the new callback
76623
- (_g = this._analyticsManager) === null || _g === void 0 ? void 0 : _g.trackCameraModeChange(oldMode, mode); // Track event
76624
- this._updateUIManager(); // Notify UI Manager of the change
76625
- }
76626
- }
76627
- _configureExploreMode() {
76628
- var _a, _b;
76629
- if (!this._camera)
76630
- return;
76631
- this._camera.checkCollisions = (_b = (_a = this._viewerOptions) === null || _a === void 0 ? void 0 : _a.exploreCollisions) !== null && _b !== void 0 ? _b : false;
76632
- this._camera.applyGravity = false;
76633
- // Inputs are now managed globally during initialization.
76634
- // This method only sets mode-specific camera properties.
76635
- console.log("Explore mode configured: Collisions=" + this._camera.checkCollisions);
76636
- }
76637
- _configureWalkMode() {
76638
- var _a, _b, _c, _d, _e, _f, _g;
76639
- if (!this._camera)
76640
- return;
76641
- this._camera.checkCollisions = (_b = (_a = this._viewerOptions) === null || _a === void 0 ? void 0 : _a.walkCollisions) !== null && _b !== void 0 ? _b : true;
76642
- this._camera.applyGravity = (_d = (_c = this._viewerOptions) === null || _c === void 0 ? void 0 : _c.walkApplyGravity) !== null && _d !== void 0 ? _d : DEFAULT_APPLY_GRAVITY;
76643
- // Gravity primarily from options
76644
- const sceneGravityY = (_f = (_e = this._viewerOptions) === null || _e === void 0 ? void 0 : _e.gravity) !== null && _f !== void 0 ? _f : DEFAULT_GRAVITY; // Already checks options
76645
- this._scene.gravity = new Vector3(0, sceneGravityY, 0);
76646
- // Ellipsoid primarily from options
76647
- this._camera.ellipsoid = ((_g = this._viewerOptions) === null || _g === void 0 ? void 0 : _g.cameraEllipsoid) ? toVector3(this._viewerOptions.cameraEllipsoid) : DEFAULT_ELLIPSOID.clone(); // Already checks options
76648
- // Inputs are now managed globally during initialization.
76649
- // This method only sets mode-specific camera properties.
76650
- console.log(`Walk mode configured: Gravity=${this._camera.applyGravity}, Collisions=${this._camera.checkCollisions}`);
76651
- }
76652
- _configureTourMode() {
76653
- if (!this._camera)
76654
- return;
76655
- this._camera.checkCollisions = false;
76656
- this._camera.applyGravity = false;
76657
- // Inputs are now managed globally during initialization.
76658
- // Mouse/Touch rotation is handled by the default inputs added earlier.
76659
- // This method only sets mode-specific camera properties.
76660
- console.log("Tour/Hybrid mode configured: No gravity, no collisions.");
76661
- }
76662
- // Helper to set standard keyboard bindings
76663
- _setKeyboardInputs() {
76664
- if (!this._camera)
76665
- return;
76666
- const input = this._camera.inputs.attached.keyboard; // Use specific type
76667
- if (input) {
76668
- // Clear existing potentially conflicting bindings (optional but safer)
76669
- input.keysUp = [];
76670
- input.keysDown = [];
76671
- input.keysLeft = [];
76672
- input.keysRight = [];
76673
- input.keysUpward = [];
76674
- input.keysDownward = [];
76675
- // Standard WASD / Arrow Keys
76676
- input.keysUp.push(87, 38); // W, Up Arrow
76677
- input.keysDown.push(83, 40); // S, Down Arrow
76678
- input.keysLeft.push(65, 37); // A, Left Arrow
76679
- input.keysRight.push(68, 39); // D, Right Arrow
76680
- // Add Q/E for vertical movement
76681
- input.keysUpward = [69]; // E key for up
76682
- input.keysDownward = [81]; // Q key for down
76683
- }
76684
- }
76685
- // --- Custom Mouse Wheel Input Handling Removed ---
76686
- // The default FreeCameraMouseWheelInput is now used.
76687
- _setupSceneDispose() {
76688
- this._scene.onDisposeObservable.addOnce(() => {
76689
- console.log("StorySplat Viewer: Disposing camera manager and camera.");
76690
- // No need to remove custom wheel listener anymore
76691
- this._camera = null;
76692
- });
76693
- }
76694
- // --- UI Update Helper ---
76695
- _updateUIManager() {
76696
- if (!this._uiManager)
76697
- return;
76698
- this._uiManager.updateModeToggleButtons();
76699
- this._uiManager.updateExploreWalkToggleButtons();
76700
- this._uiManager.updateScrollControlsVisibility();
76701
- }
76702
- // --- Public API Methods ---
76703
- // Method to set the UIManager after instantiation
76704
- setUIManager(uiManager) {
76705
- this._uiManager = uiManager;
76706
- // Update UI immediately after manager is set
76707
- this._updateUIManager();
76708
- }
76709
- setPosition(position) {
76710
- if (this._camera) {
76711
- this._camera.position = position;
76712
- }
76713
- }
76714
- setTarget(target) {
76715
- if (this._camera) {
76716
- this._camera.setTarget(target);
76717
- }
76718
- }
76719
- }
76720
-
76721
- // Import necessary loaders for .splat files (assuming it uses GLTF internally or a custom loader is registered)
76722
- // If @babylonjs/loaders is needed and not globally imported, ensure it's imported somewhere,
76723
- // e.g., in the main index.ts or here. For now, assume SceneLoader handles it.
76724
- // import "@babylonjs/loaders/glTF"; // Example if GLTF loader is needed explicitly
76725
- /**
76726
- * Loads the primary Gaussian Splat asset into the scene.
76727
- * @param scene - The BabylonJS scene to load the asset into.
76728
- * @param data - The StorySplat data containing the model URL and scaling info.
76729
- * @param onProgress - Optional callback function to report loading progress.
76730
- * Receives percentage (0-100) and an optional status text.
76731
- * @returns A promise that resolves with an array of the loaded AbstractMesh objects.
76732
- */
76733
- async function loadSplatAsset(scene, data, onProgress // Updated signature
76734
- ) {
76735
- var _a, _b, _c;
76736
- const modelUrl = data.loadedModelUrl; // Changed from modelUrl
76737
- if (!modelUrl) {
76738
- console.error("StorySplat Viewer: No loadedModelUrl provided in data.");
76739
- return Promise.reject(new Error("No loadedModelUrl provided in StorySplatData."));
76740
- }
76741
- const splatScale = (_a = data.splatScale) !== null && _a !== void 0 ? _a : 1.0;
76742
- const invertX = (_b = data.invertXScale) !== null && _b !== void 0 ? _b : false;
76743
- const invertY = (_c = data.invertYScale) !== null && _c !== void 0 ? _c : false;
76744
- console.log(`StorySplat Viewer: Loading splat asset from ${modelUrl}`);
76745
- try {
76746
- // Debug: Check what loaders are registered
76747
- console.log("StorySplat Viewer: Available SceneLoader plugins:", Object.keys(SceneLoader._RegisteredPlugins || {}));
76748
- const result = await SceneLoader.ImportMeshAsync("", // meshNames - empty string loads all
76749
- modelUrl, // rootUrl - the full URL
76750
- "", // sceneFilename - empty when using full URL
76751
- scene, (progressEvent) => {
76752
- if (onProgress) {
76753
- // Calculate percentage, handle indeterminate state
76754
- let percentage = 0;
76755
- if (progressEvent.lengthComputable && progressEvent.total > 0) {
76756
- percentage = (progressEvent.loaded / progressEvent.total) * 100;
76757
- }
76758
- else if (progressEvent.loaded > 0) {
76759
- // If not lengthComputable but loaded > 0, maybe indicate activity?
76760
- // This part is tricky without total size. Could use a spinner state.
76761
- // For simplicity, we'll report 0 or the calculated percentage.
76762
- }
76763
- // Report percentage and potentially a status text
76764
- const statusText = progressEvent.lengthComputable ? `${(progressEvent.loaded / 1024 / 1024).toFixed(2)}MB / ${(progressEvent.total / 1024 / 1024).toFixed(2)}MB` : 'Loading...';
76765
- onProgress(Math.round(percentage), statusText);
76766
- }
76767
- });
76768
- if (!result.meshes || result.meshes.length === 0) {
76769
- throw new Error(`No meshes found in the loaded asset: ${modelUrl}`);
76770
- }
76771
- console.log(`StorySplat Viewer: Successfully loaded ${result.meshes.length} meshes from ${modelUrl}.`);
76772
- result.meshes.forEach((mesh) => {
76773
- // Ensure the mesh is visible and pickable
76774
- mesh.isVisible = true;
76775
- mesh.isPickable = true; // Important for interactions like hotspots if they attach to splats
76776
- // Apply scaling and inversion
76777
- const scaleX = splatScale * (invertX ? -1 : 1);
76778
- const scaleY = splatScale * (invertY ? -1 : 1);
76779
- // Invert Z if only one of X or Y is inverted to maintain handedness
76780
- // If both inverted or neither inverted, Z scale matches splatScale sign
76781
- const scaleZ = splatScale * ((invertX !== invertY) ? -1 : 1);
76782
- mesh.scaling.set(scaleX, scaleY, scaleZ);
76783
- // Optional: Set collision properties if needed later
76784
- // mesh.checkCollisions = true; // Enable collisions if required
76785
- // Optional: Assign a name based on the URL for easier identification
76786
- mesh.name = `splatRoot_${modelUrl}`; // Name the root mesh if possible
76787
- });
76788
- // Add cleanup logic for the loaded meshes
76789
- const loadedMeshes = result.meshes; // Capture the meshes in this scope
76790
- const disposeObserver = scene.onDisposeObservable.addOnce(() => {
76791
- console.log(`StorySplat Viewer: Disposing splat asset: ${modelUrl}`);
76792
- loadedMeshes.forEach(mesh => {
76793
- if (mesh && !mesh.isDisposed()) {
76794
- // Check parentage - only dispose root nodes added by the loader?
76795
- // Or dispose all meshes returned by the loader. Assuming the latter for now.
76796
- mesh.dispose(false, true); // Dispose mesh, materials, textures
76797
- }
76798
- });
76799
- });
76800
- // Store the observer reference on the meshes or a manager if needed for explicit cleanup later
76801
- // e.g., loadedMeshes[0].metadata = { disposeObserver };
76802
- return loadedMeshes;
76803
- }
76804
- catch (error) {
76805
- console.error(`StorySplat Viewer: Error loading splat asset from ${modelUrl}:`, error);
76806
- // Propagate the error
76807
- throw error;
76808
- }
76809
- }
76810
-
76811
- /**
76812
- * Class used to store geometry data (vertex buffers + index buffer)
76813
- */
76814
- class Geometry {
76815
- /**
76816
- * Gets or sets the Bias Vector to apply on the bounding elements (box/sphere), the max extend is computed as v += v * bias.x + bias.y, the min is computed as v -= v * bias.x + bias.y
76817
- */
76818
- get boundingBias() {
76819
- return this._boundingBias;
76820
- }
76821
- /**
76822
- * Gets or sets the Bias Vector to apply on the bounding elements (box/sphere), the max extend is computed as v += v * bias.x + bias.y, the min is computed as v -= v * bias.x + bias.y
76823
- */
76824
- set boundingBias(value) {
76825
- if (this._boundingBias) {
76826
- this._boundingBias.copyFrom(value);
76827
- }
76828
- else {
76829
- this._boundingBias = value.clone();
76830
- }
76831
- this._updateBoundingInfo(true, null);
76832
- }
76833
- /**
76834
- * Static function used to attach a new empty geometry to a mesh
76835
- * @param mesh defines the mesh to attach the geometry to
76836
- * @returns the new Geometry
76837
- */
76838
- static CreateGeometryForMesh(mesh) {
76839
- const geometry = new Geometry(Geometry.RandomId(), mesh.getScene());
76840
- geometry.applyToMesh(mesh);
76841
- return geometry;
76842
- }
76843
- /** Get the list of meshes using this geometry */
76844
- get meshes() {
76845
- return this._meshes;
76846
- }
76847
- /**
76848
- * Creates a new geometry
76849
- * @param id defines the unique ID
76850
- * @param scene defines the hosting scene
76851
- * @param vertexData defines the VertexData used to get geometry data
76852
- * @param updatable defines if geometry must be updatable (false by default)
76853
- * @param mesh defines the mesh that will be associated with the geometry
76854
- */
76855
- constructor(id, scene, vertexData, updatable = false, mesh = null) {
76856
- /**
76857
- * Gets the delay loading state of the geometry (none by default which means not delayed)
76858
- */
76859
- this.delayLoadState = 0;
76860
- this._totalVertices = 0;
76861
- this._isDisposed = false;
76862
- this._extend = {
76863
- minimum: new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE),
76864
- maximum: new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE),
76865
- };
76866
- this._indexBufferIsUpdatable = false;
76867
- this._positionsCache = [];
76868
- /** @internal */
76869
- this._parentContainer = null;
76870
- /**
76871
- * If set to true (false by default), the bounding info applied to the meshes sharing this geometry will be the bounding info defined at the class level
76872
- * and won't be computed based on the vertex positions (which is what we get when useBoundingInfoFromGeometry = false)
76873
- */
76874
- this.useBoundingInfoFromGeometry = false;
76875
- this._scene = scene || EngineStore.LastCreatedScene;
76876
- if (!this._scene) {
76877
- return;
76878
- }
76879
- this.id = id;
76880
- this.uniqueId = this._scene.getUniqueId();
76881
- this._engine = this._scene.getEngine();
76882
- this._meshes = [];
76883
- //Init vertex buffer cache
76884
- this._vertexBuffers = {};
76885
- this._indices = [];
76886
- this._updatable = updatable;
76887
- // vertexData
76888
- if (vertexData) {
76889
- this.setAllVerticesData(vertexData, updatable);
76890
- }
76891
- else {
76892
- this._totalVertices = 0;
76893
- }
76894
- if (this._engine.getCaps().vertexArrayObject) {
76895
- this._vertexArrayObjects = {};
76896
- }
76897
- // applyToMesh
76898
- if (mesh) {
76899
- this.applyToMesh(mesh);
76900
- mesh.computeWorldMatrix(true);
76901
- }
76902
- }
76903
- /**
76904
- * Gets the current extend of the geometry
76905
- */
76906
- get extend() {
76907
- return this._extend;
76908
- }
76909
- /**
76910
- * Gets the hosting scene
76911
- * @returns the hosting Scene
76912
- */
76913
- getScene() {
76914
- return this._scene;
76915
- }
76916
- /**
76917
- * Gets the hosting engine
76918
- * @returns the hosting Engine
76919
- */
76920
- getEngine() {
76921
- return this._engine;
76922
- }
76923
- /**
76924
- * Defines if the geometry is ready to use
76925
- * @returns true if the geometry is ready to be used
76926
- */
76927
- isReady() {
76928
- return this.delayLoadState === 1 || this.delayLoadState === 0;
76929
- }
76930
- /**
76931
- * Gets a value indicating that the geometry should not be serialized
76932
- */
76933
- get doNotSerialize() {
76934
- for (let index = 0; index < this._meshes.length; index++) {
76935
- if (!this._meshes[index].doNotSerialize) {
76936
- return false;
76937
- }
76938
- }
76939
- return true;
76940
- }
76941
- /** @internal */
76942
- _rebuild() {
76943
- if (this._vertexArrayObjects) {
76944
- this._vertexArrayObjects = {};
76945
- }
76946
- // Index buffer
76947
- if (this._meshes.length !== 0 && this._indices) {
76948
- this._indexBuffer = this._engine.createIndexBuffer(this._indices, this._updatable, "Geometry_" + this.id + "_IndexBuffer");
76949
- }
76950
- // Vertex buffers
76951
- const buffers = new Set();
76952
- for (const key in this._vertexBuffers) {
76953
- buffers.add(this._vertexBuffers[key].getWrapperBuffer());
76954
- }
76955
- buffers.forEach((buffer) => {
76956
- buffer._rebuild();
76957
- });
76958
- }
76959
- /**
76960
- * Affects all geometry data in one call
76961
- * @param vertexData defines the geometry data
76962
- * @param updatable defines if the geometry must be flagged as updatable (false as default)
76963
- */
76964
- setAllVerticesData(vertexData, updatable) {
76965
- vertexData.applyToGeometry(this, updatable);
76966
- this._notifyUpdate();
76967
- }
76968
- /**
76969
- * Set specific vertex data
76970
- * @param kind defines the data kind (Position, normal, etc...)
76971
- * @param data defines the vertex data to use
76972
- * @param updatable defines if the vertex must be flagged as updatable (false as default)
76973
- * @param stride defines the stride to use (0 by default). This value is deduced from the kind value if not specified
76974
- */
76975
- setVerticesData(kind, data, updatable = false, stride) {
76976
- if (updatable && Array.isArray(data)) {
76977
- // to avoid converting to Float32Array at each draw call in engine.updateDynamicVertexBuffer, we make the conversion a single time here
76978
- data = new Float32Array(data);
76979
- }
76980
- const buffer = new VertexBuffer(this._engine, data, kind, {
76981
- updatable,
76982
- postponeInternalCreation: this._meshes.length === 0,
76983
- stride,
76984
- label: "Geometry_" + this.id + "_" + kind,
76985
- });
76986
- this.setVerticesBuffer(buffer);
76987
- }
76988
- /**
76989
- * Removes a specific vertex data
76990
- * @param kind defines the data kind (Position, normal, etc...)
76991
- */
76992
- removeVerticesData(kind) {
76993
- if (this._vertexBuffers[kind]) {
76994
- this._vertexBuffers[kind].dispose();
76995
- delete this._vertexBuffers[kind];
76996
- }
76997
- if (this._vertexArrayObjects) {
76998
- this._disposeVertexArrayObjects();
76999
- }
77000
- }
77001
- /**
77002
- * Affect a vertex buffer to the geometry. the vertexBuffer.getKind() function is used to determine where to store the data
77003
- * @param buffer defines the vertex buffer to use
77004
- * @param totalVertices defines the total number of vertices for position kind (could be null)
77005
- * @param disposeExistingBuffer disposes the existing buffer, if any (default: true)
77006
- */
77007
- setVerticesBuffer(buffer, totalVertices = null, disposeExistingBuffer = true) {
77008
- const kind = buffer.getKind();
77009
- if (this._vertexBuffers[kind] && disposeExistingBuffer) {
77010
- this._vertexBuffers[kind].dispose();
77011
- }
77012
- if (buffer._buffer && buffer._ownsBuffer) {
77013
- buffer._buffer._increaseReferences();
77014
- }
77015
- this._vertexBuffers[kind] = buffer;
77016
- const meshes = this._meshes;
77017
- const numOfMeshes = meshes.length;
77018
- if (kind === VertexBuffer.PositionKind) {
77019
- this._totalVertices = totalVertices ?? buffer._maxVerticesCount;
77020
- this._updateExtend(buffer.getFloatData(this._totalVertices));
77021
- this._resetPointsArrayCache();
77022
- // this._extend can be empty if buffer.getFloatData(this._totalVertices) returned null
77023
- const minimum = (this._extend && this._extend.minimum) || new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
77024
- const maximum = (this._extend && this._extend.maximum) || new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
77025
- for (let index = 0; index < numOfMeshes; index++) {
77026
- const mesh = meshes[index];
77027
- mesh.buildBoundingInfo(minimum, maximum);
77028
- mesh._createGlobalSubMesh(mesh.isUnIndexed);
77029
- mesh.computeWorldMatrix(true);
77030
- mesh.synchronizeInstances();
77031
- }
77032
- }
77033
- this._notifyUpdate(kind);
76456
+ this._notifyUpdate(kind);
77034
76457
  }
77035
76458
  /**
77036
76459
  * Update a specific vertex buffer
@@ -98988,292 +98411,898 @@ class StandardMaterial extends PushMaterial {
98988
98411
  static get EmissiveTextureEnabled() {
98989
98412
  return MaterialFlags.EmissiveTextureEnabled;
98990
98413
  }
98991
- static set EmissiveTextureEnabled(value) {
98992
- MaterialFlags.EmissiveTextureEnabled = value;
98414
+ static set EmissiveTextureEnabled(value) {
98415
+ MaterialFlags.EmissiveTextureEnabled = value;
98416
+ }
98417
+ /**
98418
+ * Are specular textures enabled in the application.
98419
+ */
98420
+ static get SpecularTextureEnabled() {
98421
+ return MaterialFlags.SpecularTextureEnabled;
98422
+ }
98423
+ static set SpecularTextureEnabled(value) {
98424
+ MaterialFlags.SpecularTextureEnabled = value;
98425
+ }
98426
+ /**
98427
+ * Are bump textures enabled in the application.
98428
+ */
98429
+ static get BumpTextureEnabled() {
98430
+ return MaterialFlags.BumpTextureEnabled;
98431
+ }
98432
+ static set BumpTextureEnabled(value) {
98433
+ MaterialFlags.BumpTextureEnabled = value;
98434
+ }
98435
+ /**
98436
+ * Are lightmap textures enabled in the application.
98437
+ */
98438
+ static get LightmapTextureEnabled() {
98439
+ return MaterialFlags.LightmapTextureEnabled;
98440
+ }
98441
+ static set LightmapTextureEnabled(value) {
98442
+ MaterialFlags.LightmapTextureEnabled = value;
98443
+ }
98444
+ /**
98445
+ * Are refraction textures enabled in the application.
98446
+ */
98447
+ static get RefractionTextureEnabled() {
98448
+ return MaterialFlags.RefractionTextureEnabled;
98449
+ }
98450
+ static set RefractionTextureEnabled(value) {
98451
+ MaterialFlags.RefractionTextureEnabled = value;
98452
+ }
98453
+ /**
98454
+ * Are color grading textures enabled in the application.
98455
+ */
98456
+ static get ColorGradingTextureEnabled() {
98457
+ return MaterialFlags.ColorGradingTextureEnabled;
98458
+ }
98459
+ static set ColorGradingTextureEnabled(value) {
98460
+ MaterialFlags.ColorGradingTextureEnabled = value;
98461
+ }
98462
+ /**
98463
+ * Are fresnels enabled in the application.
98464
+ */
98465
+ static get FresnelEnabled() {
98466
+ return MaterialFlags.FresnelEnabled;
98467
+ }
98468
+ static set FresnelEnabled(value) {
98469
+ MaterialFlags.FresnelEnabled = value;
98470
+ }
98471
+ }
98472
+ /**
98473
+ * Force all the standard materials to compile to glsl even on WebGPU engines.
98474
+ * False by default. This is mostly meant for backward compatibility.
98475
+ */
98476
+ StandardMaterial.ForceGLSL = false;
98477
+ __decorate([
98478
+ serializeAsTexture("diffuseTexture")
98479
+ ], StandardMaterial.prototype, "_diffuseTexture", void 0);
98480
+ __decorate([
98481
+ expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
98482
+ ], StandardMaterial.prototype, "diffuseTexture", void 0);
98483
+ __decorate([
98484
+ serializeAsTexture("ambientTexture")
98485
+ ], StandardMaterial.prototype, "_ambientTexture", void 0);
98486
+ __decorate([
98487
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98488
+ ], StandardMaterial.prototype, "ambientTexture", void 0);
98489
+ __decorate([
98490
+ serializeAsTexture("opacityTexture")
98491
+ ], StandardMaterial.prototype, "_opacityTexture", void 0);
98492
+ __decorate([
98493
+ expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
98494
+ ], StandardMaterial.prototype, "opacityTexture", void 0);
98495
+ __decorate([
98496
+ serializeAsTexture("reflectionTexture")
98497
+ ], StandardMaterial.prototype, "_reflectionTexture", void 0);
98498
+ __decorate([
98499
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98500
+ ], StandardMaterial.prototype, "reflectionTexture", void 0);
98501
+ __decorate([
98502
+ serializeAsTexture("emissiveTexture")
98503
+ ], StandardMaterial.prototype, "_emissiveTexture", void 0);
98504
+ __decorate([
98505
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98506
+ ], StandardMaterial.prototype, "emissiveTexture", void 0);
98507
+ __decorate([
98508
+ serializeAsTexture("specularTexture")
98509
+ ], StandardMaterial.prototype, "_specularTexture", void 0);
98510
+ __decorate([
98511
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98512
+ ], StandardMaterial.prototype, "specularTexture", void 0);
98513
+ __decorate([
98514
+ serializeAsTexture("bumpTexture")
98515
+ ], StandardMaterial.prototype, "_bumpTexture", void 0);
98516
+ __decorate([
98517
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98518
+ ], StandardMaterial.prototype, "bumpTexture", void 0);
98519
+ __decorate([
98520
+ serializeAsTexture("lightmapTexture")
98521
+ ], StandardMaterial.prototype, "_lightmapTexture", void 0);
98522
+ __decorate([
98523
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98524
+ ], StandardMaterial.prototype, "lightmapTexture", void 0);
98525
+ __decorate([
98526
+ serializeAsTexture("refractionTexture")
98527
+ ], StandardMaterial.prototype, "_refractionTexture", void 0);
98528
+ __decorate([
98529
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98530
+ ], StandardMaterial.prototype, "refractionTexture", void 0);
98531
+ __decorate([
98532
+ serializeAsColor3("ambient")
98533
+ ], StandardMaterial.prototype, "ambientColor", void 0);
98534
+ __decorate([
98535
+ serializeAsColor3("diffuse")
98536
+ ], StandardMaterial.prototype, "diffuseColor", void 0);
98537
+ __decorate([
98538
+ serializeAsColor3("specular")
98539
+ ], StandardMaterial.prototype, "specularColor", void 0);
98540
+ __decorate([
98541
+ serializeAsColor3("emissive")
98542
+ ], StandardMaterial.prototype, "emissiveColor", void 0);
98543
+ __decorate([
98544
+ serialize()
98545
+ ], StandardMaterial.prototype, "specularPower", void 0);
98546
+ __decorate([
98547
+ serialize("useAlphaFromDiffuseTexture")
98548
+ ], StandardMaterial.prototype, "_useAlphaFromDiffuseTexture", void 0);
98549
+ __decorate([
98550
+ expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
98551
+ ], StandardMaterial.prototype, "useAlphaFromDiffuseTexture", void 0);
98552
+ __decorate([
98553
+ serialize("useEmissiveAsIllumination")
98554
+ ], StandardMaterial.prototype, "_useEmissiveAsIllumination", void 0);
98555
+ __decorate([
98556
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98557
+ ], StandardMaterial.prototype, "useEmissiveAsIllumination", void 0);
98558
+ __decorate([
98559
+ serialize("linkEmissiveWithDiffuse")
98560
+ ], StandardMaterial.prototype, "_linkEmissiveWithDiffuse", void 0);
98561
+ __decorate([
98562
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98563
+ ], StandardMaterial.prototype, "linkEmissiveWithDiffuse", void 0);
98564
+ __decorate([
98565
+ serialize("useSpecularOverAlpha")
98566
+ ], StandardMaterial.prototype, "_useSpecularOverAlpha", void 0);
98567
+ __decorate([
98568
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98569
+ ], StandardMaterial.prototype, "useSpecularOverAlpha", void 0);
98570
+ __decorate([
98571
+ serialize("useReflectionOverAlpha")
98572
+ ], StandardMaterial.prototype, "_useReflectionOverAlpha", void 0);
98573
+ __decorate([
98574
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98575
+ ], StandardMaterial.prototype, "useReflectionOverAlpha", void 0);
98576
+ __decorate([
98577
+ serialize("disableLighting")
98578
+ ], StandardMaterial.prototype, "_disableLighting", void 0);
98579
+ __decorate([
98580
+ expandToProperty("_markAllSubMeshesAsLightsDirty")
98581
+ ], StandardMaterial.prototype, "disableLighting", void 0);
98582
+ __decorate([
98583
+ serialize("useObjectSpaceNormalMap")
98584
+ ], StandardMaterial.prototype, "_useObjectSpaceNormalMap", void 0);
98585
+ __decorate([
98586
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98587
+ ], StandardMaterial.prototype, "useObjectSpaceNormalMap", void 0);
98588
+ __decorate([
98589
+ serialize("useParallax")
98590
+ ], StandardMaterial.prototype, "_useParallax", void 0);
98591
+ __decorate([
98592
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98593
+ ], StandardMaterial.prototype, "useParallax", void 0);
98594
+ __decorate([
98595
+ serialize("useParallaxOcclusion")
98596
+ ], StandardMaterial.prototype, "_useParallaxOcclusion", void 0);
98597
+ __decorate([
98598
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98599
+ ], StandardMaterial.prototype, "useParallaxOcclusion", void 0);
98600
+ __decorate([
98601
+ serialize()
98602
+ ], StandardMaterial.prototype, "parallaxScaleBias", void 0);
98603
+ __decorate([
98604
+ serialize("roughness")
98605
+ ], StandardMaterial.prototype, "_roughness", void 0);
98606
+ __decorate([
98607
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98608
+ ], StandardMaterial.prototype, "roughness", void 0);
98609
+ __decorate([
98610
+ serialize()
98611
+ ], StandardMaterial.prototype, "indexOfRefraction", void 0);
98612
+ __decorate([
98613
+ serialize()
98614
+ ], StandardMaterial.prototype, "invertRefractionY", void 0);
98615
+ __decorate([
98616
+ serialize()
98617
+ ], StandardMaterial.prototype, "alphaCutOff", void 0);
98618
+ __decorate([
98619
+ serialize("useLightmapAsShadowmap")
98620
+ ], StandardMaterial.prototype, "_useLightmapAsShadowmap", void 0);
98621
+ __decorate([
98622
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98623
+ ], StandardMaterial.prototype, "useLightmapAsShadowmap", void 0);
98624
+ __decorate([
98625
+ serializeAsFresnelParameters("diffuseFresnelParameters")
98626
+ ], StandardMaterial.prototype, "_diffuseFresnelParameters", void 0);
98627
+ __decorate([
98628
+ expandToProperty("_markAllSubMeshesAsFresnelDirty")
98629
+ ], StandardMaterial.prototype, "diffuseFresnelParameters", void 0);
98630
+ __decorate([
98631
+ serializeAsFresnelParameters("opacityFresnelParameters")
98632
+ ], StandardMaterial.prototype, "_opacityFresnelParameters", void 0);
98633
+ __decorate([
98634
+ expandToProperty("_markAllSubMeshesAsFresnelAndMiscDirty")
98635
+ ], StandardMaterial.prototype, "opacityFresnelParameters", void 0);
98636
+ __decorate([
98637
+ serializeAsFresnelParameters("reflectionFresnelParameters")
98638
+ ], StandardMaterial.prototype, "_reflectionFresnelParameters", void 0);
98639
+ __decorate([
98640
+ expandToProperty("_markAllSubMeshesAsFresnelDirty")
98641
+ ], StandardMaterial.prototype, "reflectionFresnelParameters", void 0);
98642
+ __decorate([
98643
+ serializeAsFresnelParameters("refractionFresnelParameters")
98644
+ ], StandardMaterial.prototype, "_refractionFresnelParameters", void 0);
98645
+ __decorate([
98646
+ expandToProperty("_markAllSubMeshesAsFresnelDirty")
98647
+ ], StandardMaterial.prototype, "refractionFresnelParameters", void 0);
98648
+ __decorate([
98649
+ serializeAsFresnelParameters("emissiveFresnelParameters")
98650
+ ], StandardMaterial.prototype, "_emissiveFresnelParameters", void 0);
98651
+ __decorate([
98652
+ expandToProperty("_markAllSubMeshesAsFresnelDirty")
98653
+ ], StandardMaterial.prototype, "emissiveFresnelParameters", void 0);
98654
+ __decorate([
98655
+ serialize("useReflectionFresnelFromSpecular")
98656
+ ], StandardMaterial.prototype, "_useReflectionFresnelFromSpecular", void 0);
98657
+ __decorate([
98658
+ expandToProperty("_markAllSubMeshesAsFresnelDirty")
98659
+ ], StandardMaterial.prototype, "useReflectionFresnelFromSpecular", void 0);
98660
+ __decorate([
98661
+ serialize("useGlossinessFromSpecularMapAlpha")
98662
+ ], StandardMaterial.prototype, "_useGlossinessFromSpecularMapAlpha", void 0);
98663
+ __decorate([
98664
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98665
+ ], StandardMaterial.prototype, "useGlossinessFromSpecularMapAlpha", void 0);
98666
+ __decorate([
98667
+ serialize("maxSimultaneousLights")
98668
+ ], StandardMaterial.prototype, "_maxSimultaneousLights", void 0);
98669
+ __decorate([
98670
+ expandToProperty("_markAllSubMeshesAsLightsDirty")
98671
+ ], StandardMaterial.prototype, "maxSimultaneousLights", void 0);
98672
+ __decorate([
98673
+ serialize("invertNormalMapX")
98674
+ ], StandardMaterial.prototype, "_invertNormalMapX", void 0);
98675
+ __decorate([
98676
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98677
+ ], StandardMaterial.prototype, "invertNormalMapX", void 0);
98678
+ __decorate([
98679
+ serialize("invertNormalMapY")
98680
+ ], StandardMaterial.prototype, "_invertNormalMapY", void 0);
98681
+ __decorate([
98682
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98683
+ ], StandardMaterial.prototype, "invertNormalMapY", void 0);
98684
+ __decorate([
98685
+ serialize("twoSidedLighting")
98686
+ ], StandardMaterial.prototype, "_twoSidedLighting", void 0);
98687
+ __decorate([
98688
+ expandToProperty("_markAllSubMeshesAsTexturesDirty")
98689
+ ], StandardMaterial.prototype, "twoSidedLighting", void 0);
98690
+ __decorate([
98691
+ serialize("applyDecalMapAfterDetailMap")
98692
+ ], StandardMaterial.prototype, "_applyDecalMapAfterDetailMap", void 0);
98693
+ __decorate([
98694
+ expandToProperty("_markAllSubMeshesAsMiscDirty")
98695
+ ], StandardMaterial.prototype, "applyDecalMapAfterDetailMap", void 0);
98696
+ RegisterClass("BABYLON.StandardMaterial", StandardMaterial);
98697
+ Scene.DefaultMaterialFactory = (scene) => {
98698
+ return new StandardMaterial("default material", scene);
98699
+ };
98700
+
98701
+ /**
98702
+ * Basic check if the current environment appears to be a mobile device based on user agent.
98703
+ * Note: User agent sniffing is generally unreliable; consider feature detection instead if possible.
98704
+ * This function might return false positives or negatives.
98705
+ * @returns True if the user agent string matches common mobile patterns, false otherwise.
98706
+ */
98707
+ const isMobileDevice = () => {
98708
+ if (typeof navigator === 'undefined' || !navigator.userAgent) {
98709
+ return false; // Cannot determine without navigator.userAgent
98710
+ }
98711
+ return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase());
98712
+ };
98713
+ /**
98714
+ * Converts a simple {r, g, b, a} object to a BabylonJS Color4 object.
98715
+ * Handles potential undefined input. Alpha defaults to 1 if not provided.
98716
+ * @param colorObj - The color object { r, g, b, a? } (values 0-1).
98717
+ * @param defaultColor - The default Color4 to return if input is invalid.
98718
+ * @returns A BabylonJS Color4 object.
98719
+ */
98720
+ const toColor4 = (colorObj, defaultColor = new Color4(1, 1, 1, 1) // Default to opaque white
98721
+ ) => {
98722
+ if (colorObj && typeof colorObj.r === 'number' && typeof colorObj.g === 'number' && typeof colorObj.b === 'number') {
98723
+ const alpha = typeof colorObj.a === 'number' ? colorObj.a : 1.0;
98724
+ return new Color4(colorObj.r, colorObj.g, colorObj.b, alpha);
98725
+ }
98726
+ return defaultColor;
98727
+ };
98728
+ /**
98729
+ * Converts a simple {x, y, z} object to a BabylonJS Vector3 object.
98730
+ * Handles potential undefined input.
98731
+ * @param vecObj - The vector object { x, y, z }.
98732
+ * @param defaultVector - The default Vector3 to return if input is invalid.
98733
+ * @returns A BabylonJS Vector3 object.
98734
+ */
98735
+ const toVector3 = (vecObj, defaultVector = new Vector3(0, 0, 0) // Default to origin
98736
+ ) => {
98737
+ if (vecObj && typeof vecObj.x === 'number' && typeof vecObj.y === 'number' && typeof vecObj.z === 'number') {
98738
+ return new Vector3(vecObj.x, vecObj.y, vecObj.z);
98739
+ }
98740
+ return defaultVector;
98741
+ };
98742
+ /**
98743
+ * Converts a simple {_x, _y, _z, _w} object to a BabylonJS Quaternion object.
98744
+ * Handles potential undefined input.
98745
+ * @param quatObj - The quaternion object { _x, _y, _z, _w }.
98746
+ * @param defaultQuaternion - The default Quaternion to return if input is invalid.
98747
+ * @returns A BabylonJS Quaternion object.
98748
+ */
98749
+ const toQuaternion = (quatObj, defaultQuaternion = Quaternion.Identity() // Default to identity
98750
+ ) => {
98751
+ if (quatObj && typeof quatObj._x === 'number' && typeof quatObj._y === 'number' && typeof quatObj._z === 'number' && typeof quatObj._w === 'number') {
98752
+ return new Quaternion(quatObj._x, quatObj._y, quatObj._z, quatObj._w);
98753
+ }
98754
+ return defaultQuaternion;
98755
+ };
98756
+ /**
98757
+ * Converts a hex color string (e.g., "#RRGGBB") to a BabylonJS Color3 object.
98758
+ * Handles potential undefined or invalid input.
98759
+ * @param hex - The hex color string.
98760
+ * @param defaultColor - The default Color3 to return if input is invalid.
98761
+ * @returns A BabylonJS Color3 object.
98762
+ */
98763
+ const hexToColor3 = (hex, defaultColor = new Color3(1, 1, 1) // Default to white
98764
+ ) => {
98765
+ if (!hex) {
98766
+ return defaultColor;
98767
+ }
98768
+ try {
98769
+ return Color3.FromHexString(hex);
98770
+ }
98771
+ catch (error) {
98772
+ console.warn(`Invalid hex color format for hexToColor3: ${hex}. Using default.`, error);
98773
+ return defaultColor;
98774
+ }
98775
+ };
98776
+ /**
98777
+ * Applies opacity to a mesh and its children's materials.
98778
+ * Handles StandardMaterial and potentially PBRMaterial alpha/transparencyMode.
98779
+ * @param mesh - The root mesh to apply opacity to.
98780
+ * @param opacity - The opacity value (0-1).
98781
+ */
98782
+ function applyOpacityToMeshAndChildren(mesh, opacity) {
98783
+ if (!mesh)
98784
+ return;
98785
+ const applyToMaterial = (material) => {
98786
+ if (material) {
98787
+ const clampedOpacity = Math.max(0, Math.min(1, opacity)); // Ensure opacity is 0-1
98788
+ // StandardMaterial
98789
+ if ('alpha' in material) {
98790
+ material.alpha = clampedOpacity;
98791
+ }
98792
+ // PBRMaterial - requires transparencyMode adjustment
98793
+ if ('transparencyMode' in material) {
98794
+ const pbrMaterial = material;
98795
+ if (clampedOpacity < 1) {
98796
+ // Use ALPHABLEND (numeric value 2) if transparent
98797
+ pbrMaterial.transparencyMode = 2; // BABYLON.Constants.PBRMATERIAL_ALPHABLEND;
98798
+ pbrMaterial.alpha = clampedOpacity;
98799
+ }
98800
+ else {
98801
+ // Reset to opaque if opacity is 1
98802
+ pbrMaterial.transparencyMode = null; // Or 0 for OPAQUE
98803
+ pbrMaterial.alpha = 1;
98804
+ }
98805
+ }
98806
+ }
98807
+ };
98808
+ // Apply to the mesh's own material
98809
+ applyToMaterial(mesh.material);
98810
+ // Apply to materials of direct child meshes recursively (optional, depending on need)
98811
+ // If you only want direct children, use getChildMeshes(true)
98812
+ // If you want all descendants, use getChildMeshes(false) - potentially expensive
98813
+ const children = mesh.getChildMeshes ? mesh.getChildMeshes(true) : []; // Direct children only
98814
+ children.forEach(childMesh => {
98815
+ applyOpacityToMeshAndChildren(childMesh, opacity); // Recursive call
98816
+ });
98817
+ }
98818
+
98819
+ /**
98820
+ * Initializes the BabylonJS Engine and Scene.
98821
+ * Attempts to use WebGPU first, falling back to WebGL2.
98822
+ * @param canvas - The HTML canvas element to render to.
98823
+ * @param data - The StorySplat data containing scene properties.
98824
+ * @param options - Viewer configuration options.
98825
+ * @returns A promise resolving with the initialized engine and scene, or rejecting on failure.
98826
+ */
98827
+ async function initializeSceneAndEngine(canvas, data, options // Allow passing disableWebGPU internally
98828
+ ) {
98829
+ var _a;
98830
+ // Ensure canvas fills its container
98831
+ canvas.style.width = '100%';
98832
+ canvas.style.height = '100%';
98833
+ canvas.style.display = 'block';
98834
+ canvas.style.verticalAlign = 'bottom'; // Prevent extra space below canvas
98835
+ let engine;
98836
+ const disableWebGPU = (_a = options === null || options === void 0 ? void 0 : options.disableWebGPU) !== null && _a !== void 0 ? _a : false;
98837
+ if (disableWebGPU) {
98838
+ // Force WebGL2
98839
+ try {
98840
+ engine = new Engine(canvas, true);
98841
+ console.log("StorySplat Viewer: WebGPU disabled by option, using WebGL2.");
98842
+ }
98843
+ catch (err) {
98844
+ console.error("StorySplat Viewer: Failed to initialize WebGL2 engine:", err);
98845
+ throw new Error("Failed to initialize WebGL2 engine.");
98846
+ }
98847
+ }
98848
+ else {
98849
+ // Try WebGPU first, then fallback
98850
+ try {
98851
+ // Check if running in a secure context, required for WebGPU
98852
+ if (!window.isSecureContext) {
98853
+ throw new Error("WebGPU requires a secure context (HTTPS or localhost).");
98854
+ }
98855
+ if (WebGPUEngine && WebGPUEngine.IsSupportedAsync) {
98856
+ const supported = await WebGPUEngine.IsSupportedAsync;
98857
+ if (supported) {
98858
+ const webgpuEngine = new WebGPUEngine(canvas, {
98859
+ // Explicitly request high-performance adapter
98860
+ powerPreference: "high-performance",
98861
+ // Enable validation in development environments if needed
98862
+ // enableAllFeatures: true // Use cautiously, might impact performance
98863
+ });
98864
+ await webgpuEngine.initAsync();
98865
+ engine = webgpuEngine; // Use double assertion
98866
+ console.log("StorySplat Viewer: WebGPU engine initialized.");
98867
+ }
98868
+ else {
98869
+ throw new Error("WebGPU is not supported on this browser/device.");
98870
+ }
98871
+ }
98872
+ else {
98873
+ throw new Error("WebGPUEngine not available or IsSupportedAsync missing.");
98874
+ }
98875
+ }
98876
+ catch (err) {
98877
+ // Fallback to WebGL2 if WebGPU fails or is unavailable
98878
+ console.warn("StorySplat Viewer: WebGPU unavailable or initialization failed, falling back to WebGL2:", err);
98879
+ try {
98880
+ engine = new Engine(canvas, true, {
98881
+ // Options for WebGL engine if needed
98882
+ // preserveDrawingBuffer: true, // Example option
98883
+ // stencil: true
98884
+ });
98885
+ console.log("StorySplat Viewer: Initialized WebGL2 engine as fallback.");
98886
+ }
98887
+ catch (webglErr) {
98888
+ console.error("StorySplat Viewer: Failed to initialize WebGL2 engine as fallback:", webglErr);
98889
+ throw new Error("Failed to initialize WebGL2 engine as fallback.");
98890
+ }
98891
+ }
98892
+ }
98893
+ if (!engine) {
98894
+ throw new Error("Failed to initialize any Babylon engine.");
98895
+ }
98896
+ const scene = new Scene(engine);
98897
+ // Set transparent clear color for HTML mesh compatibility
98898
+ scene.clearColor = new Color4(0, 0, 0, 0);
98899
+ // Create background sphere for background color when no skybox is active
98900
+ const defaultBgColor = new Color3(0.12, 0.12, 0.12); // Dark gray background to match HTML exports
98901
+ let bgColor;
98902
+ const bgColorOption = options === null || options === void 0 ? void 0 : options.backgroundColor; // Options still use {r,g,b,a} format
98903
+ if (bgColorOption) {
98904
+ const color4 = toColor4(bgColorOption, new Color4(defaultBgColor.r, defaultBgColor.g, defaultBgColor.b, 1));
98905
+ bgColor = new Color3(color4.r, color4.g, color4.b);
98906
+ }
98907
+ else if (data.backgroundColor) {
98908
+ bgColor = hexToColor3(data.backgroundColor, defaultBgColor);
98909
+ }
98910
+ else {
98911
+ bgColor = defaultBgColor;
98912
+ }
98913
+ // Store the background color for later use
98914
+ scene._backgroundColor = bgColor;
98915
+ // Create default background color skybox
98916
+ const skyboxMesh = MeshBuilder.CreateBox("backgroundColorSkybox", {
98917
+ size: 1000,
98918
+ sideOrientation: Mesh.BACKSIDE
98919
+ }, scene);
98920
+ const skyboxMaterial = new StandardMaterial("backgroundColorMaterial", scene);
98921
+ skyboxMaterial.disableLighting = true;
98922
+ skyboxMaterial.emissiveColor = bgColor;
98923
+ skyboxMaterial.backFaceCulling = false;
98924
+ skyboxMaterial.reflectionTexture = null;
98925
+ skyboxMaterial.diffuseColor = new Color3(0, 0, 0);
98926
+ skyboxMaterial.specularColor = new Color3(0, 0, 0);
98927
+ skyboxMesh.material = skyboxMaterial;
98928
+ skyboxMesh.infiniteDistance = true;
98929
+ skyboxMesh.isPickable = false;
98930
+ skyboxMesh.renderingGroupId = 0;
98931
+ // Apply rotation if provided in data
98932
+ if (data.skyboxRotation !== undefined && typeof data.skyboxRotation === 'number') {
98933
+ skyboxMesh.rotation.y = data.skyboxRotation;
98934
+ }
98935
+ scene._backgroundColorSkybox = skyboxMesh;
98936
+ // Ambient color is typically handled by lights (e.g., HemisphericLight groundColor)
98937
+ // Removing direct ambientColor setting from sceneProperties
98938
+ // Set a default ambient if needed, but lightingManager should handle specifics
98939
+ scene.ambientColor = new Color3(0.2, 0.2, 0.2); // Default dim ambient
98940
+ // Add resize listener
98941
+ const resizeHandler = () => {
98942
+ engine.resize();
98943
+ };
98944
+ window.addEventListener("resize", resizeHandler);
98945
+ // Add a cleanup function to the scene context
98946
+ scene.onDisposeObservable.addOnce(() => {
98947
+ console.log("StorySplat Viewer: Disposing scene, removing resize listener.");
98948
+ window.removeEventListener("resize", resizeHandler);
98949
+ // Engine disposal is handled separately in the destroy function
98950
+ });
98951
+ return { engine, scene };
98952
+ }
98953
+
98954
+ // Removed import for shared wheelHandler
98955
+ const DEFAULT_CAMERA_POSITION = new Vector3(0, 1.6, -5);
98956
+ const DEFAULT_CAMERA_TARGET = Vector3.Zero();
98957
+ const DEFAULT_FOV = 0.8;
98958
+ const DEFAULT_MIN_Z = 0.1;
98959
+ const DEFAULT_MAX_Z = 1000;
98960
+ const DEFAULT_SPEED = 0.1;
98961
+ const DEFAULT_INERTIA = 0.9;
98962
+ const DEFAULT_ANGULAR_SENSIBILITY = 2000;
98963
+ // Removed DEFAULT_WHEEL_DELTA_PERCENTAGE and DEFAULT_WHEEL_STEP as they are handled by shared handler or options
98964
+ // Walk Mode Defaults
98965
+ const DEFAULT_APPLY_GRAVITY = true;
98966
+ const DEFAULT_ELLIPSOID = new Vector3(0.5, 0.9, 0.5);
98967
+ const DEFAULT_GRAVITY = -9.81;
98968
+ class CameraManager {
98969
+ constructor(canvas, scene, data, options, // Correct options parameter
98970
+ analyticsManager // Correct analyticsManager parameter
98971
+ ) {
98972
+ var _a, _b, _c;
98973
+ this._camera = null;
98974
+ this._currentMode = 'explore';
98975
+ this._uiManager = null; // Add UIManager reference
98976
+ this._analyticsManager = null; // Add AnalyticsManager reference
98977
+ this._canvas = canvas;
98978
+ this._scene = scene;
98979
+ this._data = data; // Store data object
98980
+ this._viewerOptions = options;
98981
+ this._analyticsManager = analyticsManager || null; // Store analytics manager
98982
+ // this._uiManager = uiManager || null; // Don't store here initially
98983
+ this._isMobile = isMobileDevice();
98984
+ this._initializeCamera();
98985
+ // Prioritize initial mode from data, then options, then default to 'explore'
98986
+ const initialMode = (_c = (_a = this._data.initialCameraMode) !== null && _a !== void 0 ? _a : (_b = this._viewerOptions) === null || _b === void 0 ? void 0 : _b.defaultCameraMode) !== null && _c !== void 0 ? _c : 'explore';
98987
+ this.setCameraMode(initialMode, true); // Assert type as CameraMode
98988
+ this._setupSceneDispose();
98989
+ // Removed _setupCustomWheelInput call
98990
+ }
98991
+ get camera() {
98992
+ return this._camera;
98993
+ }
98994
+ get currentMode() {
98995
+ return this._currentMode;
98996
+ }
98997
+ _initializeCamera() {
98998
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
98999
+ let initialPosition = DEFAULT_CAMERA_POSITION;
99000
+ let initialRotation = undefined; // Use rotation instead of target
99001
+ // Try to get initial pose from the first waypoint
99002
+ const firstWaypoint = (_a = this._data.waypoints) === null || _a === void 0 ? void 0 : _a[0];
99003
+ if (firstWaypoint) {
99004
+ initialPosition = new Vector3(firstWaypoint.x, firstWaypoint.y, firstWaypoint.z);
99005
+ initialRotation = toQuaternion(firstWaypoint.rotation); // Use helper
99006
+ }
99007
+ this._camera = new UniversalCamera("viewerCamera", initialPosition, this._scene);
99008
+ // Set initial rotation if derived from waypoint, otherwise set default target
99009
+ if (initialRotation) {
99010
+ this._camera.rotationQuaternion = initialRotation;
99011
+ }
99012
+ else {
99013
+ this._camera.setTarget(DEFAULT_CAMERA_TARGET);
99014
+ }
99015
+ // --- Add Standard Inputs ---
99016
+ this._camera.inputs.addKeyboard();
99017
+ this._camera.inputs.addMouse();
99018
+ if (this._isMobile) {
99019
+ this._camera.inputs.addTouch();
99020
+ }
99021
+ // Keyboard bindings need to be set *after* inputs are added
99022
+ this._setKeyboardInputs();
99023
+ // --- Apply Common Settings ---
99024
+ // Get settings from data (StorySplatData) or options (ViewerOptions)
99025
+ this._camera.fov = (_b = this._data.fov) !== null && _b !== void 0 ? _b : DEFAULT_FOV; // FOV comes from data only
99026
+ this._camera.minZ = (_e = (_d = (_c = this._viewerOptions) === null || _c === void 0 ? void 0 : _c.cameraMinZ) !== null && _d !== void 0 ? _d : this._data.minClipPlane) !== null && _e !== void 0 ? _e : DEFAULT_MIN_Z; // Use minClipPlane from data
99027
+ this._camera.maxZ = (_h = (_g = (_f = this._viewerOptions) === null || _f === void 0 ? void 0 : _f.cameraMaxZ) !== null && _g !== void 0 ? _g : this._data.maxClipPlane) !== null && _h !== void 0 ? _h : DEFAULT_MAX_Z; // Use maxClipPlane from data
99028
+ this._camera.speed = (_l = (_k = (_j = this._viewerOptions) === null || _j === void 0 ? void 0 : _j.cameraSpeed) !== null && _k !== void 0 ? _k : this._data.cameraMovementSpeed) !== null && _l !== void 0 ? _l : DEFAULT_SPEED; // Use cameraMovementSpeed from data
99029
+ this._camera.inertia = (_o = (_m = this._viewerOptions) === null || _m === void 0 ? void 0 : _m.cameraInertia) !== null && _o !== void 0 ? _o : DEFAULT_INERTIA; // Inertia likely only in options
99030
+ // Map cameraRotationSensitivity from data to angularSensibility if options don't provide it
99031
+ const angularSensibility = (_q = (_p = this._viewerOptions) === null || _p === void 0 ? void 0 : _p.cameraAngularSensibility) !== null && _q !== void 0 ? _q : (this._data.cameraRotationSensitivity ? (DEFAULT_ANGULAR_SENSIBILITY / (this._data.cameraRotationSensitivity / 4000)) : DEFAULT_ANGULAR_SENSIBILITY); // Simple scaling based on default
99032
+ this._camera.angularSensibility = angularSensibility;
99033
+ this._camera.touchAngularSensibility = (_s = (_r = this._viewerOptions) === null || _r === void 0 ? void 0 : _r.cameraTouchAngularSensibility) !== null && _s !== void 0 ? _s : angularSensibility; // Use same sensitivity for touch by default
99034
+ // Apply Y-axis inversion by negating sensitivity if needed
99035
+ const shouldInvert = (_v = (_u = (_t = this._viewerOptions) === null || _t === void 0 ? void 0 : _t.invertCameraRotation) !== null && _u !== void 0 ? _u : this._data.invertCameraRotation) !== null && _v !== void 0 ? _v : false;
99036
+ if (shouldInvert) {
99037
+ this._camera.angularSensibility *= -1;
99038
+ this._camera.touchAngularSensibility *= -1;
99039
+ console.log("StorySplat Viewer: Inverting camera Y-axis rotation.");
99040
+ }
99041
+ // --- Wheel Input ---
99042
+ // Default wheel input is now used unless options customize it elsewhere.
99043
+ // No need to remove default or handle custom wheel here.
99044
+ // Initial collision/gravity state
99045
+ this._camera.checkCollisions = false;
99046
+ this._camera.applyGravity = false;
99047
+ // Ellipsoid primarily from options
99048
+ this._camera.ellipsoid = ((_w = this._viewerOptions) === null || _w === void 0 ? void 0 : _w.cameraEllipsoid) ? toVector3(this._viewerOptions.cameraEllipsoid) : DEFAULT_ELLIPSOID.clone();
99049
+ // Gravity primarily from options
99050
+ const sceneGravityY = (_y = (_x = this._scene.gravity) === null || _x === void 0 ? void 0 : _x.y) !== null && _y !== void 0 ? _y : ((_0 = (_z = this._viewerOptions) === null || _z === void 0 ? void 0 : _z.gravity) !== null && _0 !== void 0 ? _0 : DEFAULT_GRAVITY);
99051
+ this._scene.gravity = new Vector3(0, sceneGravityY, 0);
99052
+ // Enable scene collisions if needed - removed checks for _initialSettings
99053
+ if (((_2 = (_1 = this._viewerOptions) === null || _1 === void 0 ? void 0 : _1.allowedCameraModes) === null || _2 === void 0 ? void 0 : _2.includes('walk')) ||
99054
+ ((_3 = this._viewerOptions) === null || _3 === void 0 ? void 0 : _3.defaultCameraMode) === 'walk' ||
99055
+ ((_4 = this._viewerOptions) === null || _4 === void 0 ? void 0 : _4.exploreCollisions) ||
99056
+ ((_5 = this._viewerOptions) === null || _5 === void 0 ? void 0 : _5.walkCollisions) ||
99057
+ ((_6 = this._viewerOptions) === null || _6 === void 0 ? void 0 : _6.cameraEllipsoid) || // Check if ellipsoid is defined in options
99058
+ this._scene.collisionsEnabled) {
99059
+ this._scene.collisionsEnabled = true;
99060
+ console.log("StorySplat Viewer: Scene collisions enabled for camera.");
99061
+ }
99062
+ }
99063
+ setCameraMode(mode, immediate = false) {
99064
+ var _a, _b, _c, _d, _e, _f, _g;
99065
+ if (!this._camera)
99066
+ return;
99067
+ const allowedModes = (_b = (_a = this._viewerOptions) === null || _a === void 0 ? void 0 : _a.allowedCameraModes) !== null && _b !== void 0 ? _b : ['explore', 'walk', 'tour', 'hybrid'];
99068
+ if (!allowedModes.includes(mode)) {
99069
+ console.warn(`StorySplat Viewer: Camera mode "${mode}" is not allowed by options. Allowed: [${allowedModes.join(', ')}]`);
99070
+ return;
99071
+ }
99072
+ const oldMode = this._currentMode;
99073
+ if (oldMode === mode && !immediate)
99074
+ return;
99075
+ console.log(`StorySplat Viewer: Setting camera mode to ${mode}`);
99076
+ this._currentMode = mode;
99077
+ // Detach controls before reconfiguring
99078
+ this._camera.detachControl();
99079
+ // Reset mode-specific properties
99080
+ this._camera.checkCollisions = false;
99081
+ this._camera.applyGravity = false;
99082
+ // Inputs are now added during initialization and persist.
99083
+ // No need to remove/re-add input types here.
99084
+ // detachControl/attachControl handles focus.
99085
+ switch (mode) {
99086
+ case 'explore':
99087
+ this._configureExploreMode();
99088
+ break;
99089
+ case 'walk':
99090
+ this._configureWalkMode();
99091
+ break;
99092
+ case 'tour':
99093
+ case 'hybrid':
99094
+ this._configureTourMode();
99095
+ break;
99096
+ default:
99097
+ console.warn(`StorySplat Viewer: Unknown camera mode "${mode}". Defaulting to explore.`);
99098
+ this._configureExploreMode();
99099
+ this._currentMode = 'explore';
99100
+ }
99101
+ // Re-attach controls if globally enabled
99102
+ if (((_c = this._viewerOptions) === null || _c === void 0 ? void 0 : _c.cameraControls) !== false) {
99103
+ this._camera.attachControl(this._canvas, true);
99104
+ // Removed DEBUG log
99105
+ // Inputs are added during initialization, attachControl manages focus.
99106
+ }
99107
+ else {
99108
+ console.log("StorySplat Viewer: Camera controls globally disabled by options.");
99109
+ // No activation needed if controls are disabled
99110
+ }
99111
+ // Trigger callback and update UI
99112
+ if (oldMode !== mode) {
99113
+ (_e = (_d = this._viewerOptions) === null || _d === void 0 ? void 0 : _d.onCameraModeChanged) === null || _e === void 0 ? void 0 : _e.call(_d, mode);
99114
+ (_f = this.onCameraModeChange) === null || _f === void 0 ? void 0 : _f.call(this, mode); // Call the new callback
99115
+ (_g = this._analyticsManager) === null || _g === void 0 ? void 0 : _g.trackCameraModeChange(oldMode, mode); // Track event
99116
+ this._updateUIManager(); // Notify UI Manager of the change
99117
+ }
98993
99118
  }
98994
- /**
98995
- * Are specular textures enabled in the application.
98996
- */
98997
- static get SpecularTextureEnabled() {
98998
- return MaterialFlags.SpecularTextureEnabled;
99119
+ _configureExploreMode() {
99120
+ var _a, _b;
99121
+ if (!this._camera)
99122
+ return;
99123
+ this._camera.checkCollisions = (_b = (_a = this._viewerOptions) === null || _a === void 0 ? void 0 : _a.exploreCollisions) !== null && _b !== void 0 ? _b : false;
99124
+ this._camera.applyGravity = false;
99125
+ // Inputs are now managed globally during initialization.
99126
+ // This method only sets mode-specific camera properties.
99127
+ console.log("Explore mode configured: Collisions=" + this._camera.checkCollisions);
98999
99128
  }
99000
- static set SpecularTextureEnabled(value) {
99001
- MaterialFlags.SpecularTextureEnabled = value;
99129
+ _configureWalkMode() {
99130
+ var _a, _b, _c, _d, _e, _f, _g;
99131
+ if (!this._camera)
99132
+ return;
99133
+ this._camera.checkCollisions = (_b = (_a = this._viewerOptions) === null || _a === void 0 ? void 0 : _a.walkCollisions) !== null && _b !== void 0 ? _b : true;
99134
+ this._camera.applyGravity = (_d = (_c = this._viewerOptions) === null || _c === void 0 ? void 0 : _c.walkApplyGravity) !== null && _d !== void 0 ? _d : DEFAULT_APPLY_GRAVITY;
99135
+ // Gravity primarily from options
99136
+ const sceneGravityY = (_f = (_e = this._viewerOptions) === null || _e === void 0 ? void 0 : _e.gravity) !== null && _f !== void 0 ? _f : DEFAULT_GRAVITY; // Already checks options
99137
+ this._scene.gravity = new Vector3(0, sceneGravityY, 0);
99138
+ // Ellipsoid primarily from options
99139
+ this._camera.ellipsoid = ((_g = this._viewerOptions) === null || _g === void 0 ? void 0 : _g.cameraEllipsoid) ? toVector3(this._viewerOptions.cameraEllipsoid) : DEFAULT_ELLIPSOID.clone(); // Already checks options
99140
+ // Inputs are now managed globally during initialization.
99141
+ // This method only sets mode-specific camera properties.
99142
+ console.log(`Walk mode configured: Gravity=${this._camera.applyGravity}, Collisions=${this._camera.checkCollisions}`);
99002
99143
  }
99003
- /**
99004
- * Are bump textures enabled in the application.
99005
- */
99006
- static get BumpTextureEnabled() {
99007
- return MaterialFlags.BumpTextureEnabled;
99144
+ _configureTourMode() {
99145
+ if (!this._camera)
99146
+ return;
99147
+ this._camera.checkCollisions = false;
99148
+ this._camera.applyGravity = false;
99149
+ // Inputs are now managed globally during initialization.
99150
+ // Mouse/Touch rotation is handled by the default inputs added earlier.
99151
+ // This method only sets mode-specific camera properties.
99152
+ console.log("Tour/Hybrid mode configured: No gravity, no collisions.");
99008
99153
  }
99009
- static set BumpTextureEnabled(value) {
99010
- MaterialFlags.BumpTextureEnabled = value;
99154
+ // Helper to set standard keyboard bindings
99155
+ _setKeyboardInputs() {
99156
+ if (!this._camera)
99157
+ return;
99158
+ const input = this._camera.inputs.attached.keyboard; // Use specific type
99159
+ if (input) {
99160
+ // Clear existing potentially conflicting bindings (optional but safer)
99161
+ input.keysUp = [];
99162
+ input.keysDown = [];
99163
+ input.keysLeft = [];
99164
+ input.keysRight = [];
99165
+ input.keysUpward = [];
99166
+ input.keysDownward = [];
99167
+ // Standard WASD / Arrow Keys
99168
+ input.keysUp.push(87, 38); // W, Up Arrow
99169
+ input.keysDown.push(83, 40); // S, Down Arrow
99170
+ input.keysLeft.push(65, 37); // A, Left Arrow
99171
+ input.keysRight.push(68, 39); // D, Right Arrow
99172
+ // Add Q/E for vertical movement
99173
+ input.keysUpward = [69]; // E key for up
99174
+ input.keysDownward = [81]; // Q key for down
99175
+ }
99011
99176
  }
99012
- /**
99013
- * Are lightmap textures enabled in the application.
99014
- */
99015
- static get LightmapTextureEnabled() {
99016
- return MaterialFlags.LightmapTextureEnabled;
99177
+ // --- Custom Mouse Wheel Input Handling Removed ---
99178
+ // The default FreeCameraMouseWheelInput is now used.
99179
+ _setupSceneDispose() {
99180
+ this._scene.onDisposeObservable.addOnce(() => {
99181
+ console.log("StorySplat Viewer: Disposing camera manager and camera.");
99182
+ // No need to remove custom wheel listener anymore
99183
+ this._camera = null;
99184
+ });
99017
99185
  }
99018
- static set LightmapTextureEnabled(value) {
99019
- MaterialFlags.LightmapTextureEnabled = value;
99186
+ // --- UI Update Helper ---
99187
+ _updateUIManager() {
99188
+ if (!this._uiManager)
99189
+ return;
99190
+ this._uiManager.updateModeToggleButtons();
99191
+ this._uiManager.updateExploreWalkToggleButtons();
99192
+ this._uiManager.updateScrollControlsVisibility();
99020
99193
  }
99021
- /**
99022
- * Are refraction textures enabled in the application.
99023
- */
99024
- static get RefractionTextureEnabled() {
99025
- return MaterialFlags.RefractionTextureEnabled;
99194
+ // --- Public API Methods ---
99195
+ // Method to set the UIManager after instantiation
99196
+ setUIManager(uiManager) {
99197
+ this._uiManager = uiManager;
99198
+ // Update UI immediately after manager is set
99199
+ this._updateUIManager();
99026
99200
  }
99027
- static set RefractionTextureEnabled(value) {
99028
- MaterialFlags.RefractionTextureEnabled = value;
99201
+ setPosition(position) {
99202
+ if (this._camera) {
99203
+ this._camera.position = position;
99204
+ }
99029
99205
  }
99030
- /**
99031
- * Are color grading textures enabled in the application.
99032
- */
99033
- static get ColorGradingTextureEnabled() {
99034
- return MaterialFlags.ColorGradingTextureEnabled;
99206
+ setTarget(target) {
99207
+ if (this._camera) {
99208
+ this._camera.setTarget(target);
99209
+ }
99035
99210
  }
99036
- static set ColorGradingTextureEnabled(value) {
99037
- MaterialFlags.ColorGradingTextureEnabled = value;
99211
+ }
99212
+
99213
+ // Import necessary loaders for .splat files (assuming it uses GLTF internally or a custom loader is registered)
99214
+ // If @babylonjs/loaders is needed and not globally imported, ensure it's imported somewhere,
99215
+ // e.g., in the main index.ts or here. For now, assume SceneLoader handles it.
99216
+ // import "@babylonjs/loaders/glTF"; // Example if GLTF loader is needed explicitly
99217
+ /**
99218
+ * Loads the primary Gaussian Splat asset into the scene.
99219
+ * @param scene - The BabylonJS scene to load the asset into.
99220
+ * @param data - The StorySplat data containing the model URL and scaling info.
99221
+ * @param onProgress - Optional callback function to report loading progress.
99222
+ * Receives percentage (0-100) and an optional status text.
99223
+ * @returns A promise that resolves with an array of the loaded AbstractMesh objects.
99224
+ */
99225
+ async function loadSplatAsset(scene, data, onProgress // Updated signature
99226
+ ) {
99227
+ var _a, _b, _c, _d, _e;
99228
+ const modelUrl = data.loadedModelUrl; // Changed from modelUrl
99229
+ if (!modelUrl) {
99230
+ console.error("StorySplat Viewer: No loadedModelUrl provided in data.");
99231
+ return Promise.reject(new Error("No loadedModelUrl provided in StorySplatData."));
99038
99232
  }
99039
- /**
99040
- * Are fresnels enabled in the application.
99041
- */
99042
- static get FresnelEnabled() {
99043
- return MaterialFlags.FresnelEnabled;
99233
+ const splatScale = (_a = data.splatScale) !== null && _a !== void 0 ? _a : 1.0;
99234
+ const invertX = (_b = data.invertXScale) !== null && _b !== void 0 ? _b : false;
99235
+ const invertY = (_c = data.invertYScale) !== null && _c !== void 0 ? _c : false;
99236
+ const position = (_d = data.splatPosition) !== null && _d !== void 0 ? _d : [0, 0, 0];
99237
+ const rotation = (_e = data.splatRotation) !== null && _e !== void 0 ? _e : [0, 0, 0];
99238
+ console.log(`StorySplat Viewer: Loading splat asset from ${modelUrl}`);
99239
+ try {
99240
+ // Debug: Check what loaders are registered
99241
+ console.log("StorySplat Viewer: Available SceneLoader plugins:", Object.keys(SceneLoader._RegisteredPlugins || {}));
99242
+ const result = await SceneLoader.ImportMeshAsync("", // meshNames - empty string loads all
99243
+ modelUrl, // rootUrl - the full URL
99244
+ "", // sceneFilename - empty when using full URL
99245
+ scene, (progressEvent) => {
99246
+ if (onProgress) {
99247
+ // Calculate percentage, handle indeterminate state
99248
+ let percentage = 0;
99249
+ if (progressEvent.lengthComputable && progressEvent.total > 0) {
99250
+ percentage = (progressEvent.loaded / progressEvent.total) * 100;
99251
+ }
99252
+ else if (progressEvent.loaded > 0) {
99253
+ // If not lengthComputable but loaded > 0, maybe indicate activity?
99254
+ // This part is tricky without total size. Could use a spinner state.
99255
+ // For simplicity, we'll report 0 or the calculated percentage.
99256
+ }
99257
+ // Report percentage and potentially a status text
99258
+ const statusText = progressEvent.lengthComputable ? `${(progressEvent.loaded / 1024 / 1024).toFixed(2)}MB / ${(progressEvent.total / 1024 / 1024).toFixed(2)}MB` : 'Loading...';
99259
+ onProgress(Math.round(percentage), statusText);
99260
+ }
99261
+ });
99262
+ if (!result.meshes || result.meshes.length === 0) {
99263
+ throw new Error(`No meshes found in the loaded asset: ${modelUrl}`);
99264
+ }
99265
+ console.log(`StorySplat Viewer: Successfully loaded ${result.meshes.length} meshes from ${modelUrl}.`);
99266
+ result.meshes.forEach((mesh) => {
99267
+ // Ensure the mesh is visible and pickable
99268
+ mesh.isVisible = true;
99269
+ mesh.isPickable = true; // Important for interactions like hotspots if they attach to splats
99270
+ // Apply scaling and inversion
99271
+ const scaleX = splatScale * (invertX ? -1 : 1);
99272
+ const scaleY = splatScale * (invertY ? -1 : 1);
99273
+ // Invert Z if only one of X or Y is inverted to maintain handedness
99274
+ // If both inverted or neither inverted, Z scale matches splatScale sign
99275
+ const scaleZ = splatScale * ((invertX !== invertY) ? -1 : 1);
99276
+ mesh.scaling.set(scaleX, scaleY, scaleZ);
99277
+ mesh.position.set(position[0], position[1], position[2]);
99278
+ mesh.rotation.set(rotation[0], rotation[1], rotation[2]);
99279
+ // Optional: Set collision properties if needed later
99280
+ // mesh.checkCollisions = true; // Enable collisions if required
99281
+ // Optional: Assign a name based on the URL for easier identification
99282
+ mesh.name = `splatRoot_${modelUrl}`; // Name the root mesh if possible
99283
+ });
99284
+ // Add cleanup logic for the loaded meshes
99285
+ const loadedMeshes = result.meshes; // Capture the meshes in this scope
99286
+ const disposeObserver = scene.onDisposeObservable.addOnce(() => {
99287
+ console.log(`StorySplat Viewer: Disposing splat asset: ${modelUrl}`);
99288
+ loadedMeshes.forEach(mesh => {
99289
+ if (mesh && !mesh.isDisposed()) {
99290
+ // Check parentage - only dispose root nodes added by the loader?
99291
+ // Or dispose all meshes returned by the loader. Assuming the latter for now.
99292
+ mesh.dispose(false, true); // Dispose mesh, materials, textures
99293
+ }
99294
+ });
99295
+ });
99296
+ // Store the observer reference on the meshes or a manager if needed for explicit cleanup later
99297
+ // e.g., loadedMeshes[0].metadata = { disposeObserver };
99298
+ return loadedMeshes;
99044
99299
  }
99045
- static set FresnelEnabled(value) {
99046
- MaterialFlags.FresnelEnabled = value;
99300
+ catch (error) {
99301
+ console.error(`StorySplat Viewer: Error loading splat asset from ${modelUrl}:`, error);
99302
+ // Propagate the error
99303
+ throw error;
99047
99304
  }
99048
99305
  }
99049
- /**
99050
- * Force all the standard materials to compile to glsl even on WebGPU engines.
99051
- * False by default. This is mostly meant for backward compatibility.
99052
- */
99053
- StandardMaterial.ForceGLSL = false;
99054
- __decorate([
99055
- serializeAsTexture("diffuseTexture")
99056
- ], StandardMaterial.prototype, "_diffuseTexture", void 0);
99057
- __decorate([
99058
- expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
99059
- ], StandardMaterial.prototype, "diffuseTexture", void 0);
99060
- __decorate([
99061
- serializeAsTexture("ambientTexture")
99062
- ], StandardMaterial.prototype, "_ambientTexture", void 0);
99063
- __decorate([
99064
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99065
- ], StandardMaterial.prototype, "ambientTexture", void 0);
99066
- __decorate([
99067
- serializeAsTexture("opacityTexture")
99068
- ], StandardMaterial.prototype, "_opacityTexture", void 0);
99069
- __decorate([
99070
- expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
99071
- ], StandardMaterial.prototype, "opacityTexture", void 0);
99072
- __decorate([
99073
- serializeAsTexture("reflectionTexture")
99074
- ], StandardMaterial.prototype, "_reflectionTexture", void 0);
99075
- __decorate([
99076
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99077
- ], StandardMaterial.prototype, "reflectionTexture", void 0);
99078
- __decorate([
99079
- serializeAsTexture("emissiveTexture")
99080
- ], StandardMaterial.prototype, "_emissiveTexture", void 0);
99081
- __decorate([
99082
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99083
- ], StandardMaterial.prototype, "emissiveTexture", void 0);
99084
- __decorate([
99085
- serializeAsTexture("specularTexture")
99086
- ], StandardMaterial.prototype, "_specularTexture", void 0);
99087
- __decorate([
99088
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99089
- ], StandardMaterial.prototype, "specularTexture", void 0);
99090
- __decorate([
99091
- serializeAsTexture("bumpTexture")
99092
- ], StandardMaterial.prototype, "_bumpTexture", void 0);
99093
- __decorate([
99094
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99095
- ], StandardMaterial.prototype, "bumpTexture", void 0);
99096
- __decorate([
99097
- serializeAsTexture("lightmapTexture")
99098
- ], StandardMaterial.prototype, "_lightmapTexture", void 0);
99099
- __decorate([
99100
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99101
- ], StandardMaterial.prototype, "lightmapTexture", void 0);
99102
- __decorate([
99103
- serializeAsTexture("refractionTexture")
99104
- ], StandardMaterial.prototype, "_refractionTexture", void 0);
99105
- __decorate([
99106
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99107
- ], StandardMaterial.prototype, "refractionTexture", void 0);
99108
- __decorate([
99109
- serializeAsColor3("ambient")
99110
- ], StandardMaterial.prototype, "ambientColor", void 0);
99111
- __decorate([
99112
- serializeAsColor3("diffuse")
99113
- ], StandardMaterial.prototype, "diffuseColor", void 0);
99114
- __decorate([
99115
- serializeAsColor3("specular")
99116
- ], StandardMaterial.prototype, "specularColor", void 0);
99117
- __decorate([
99118
- serializeAsColor3("emissive")
99119
- ], StandardMaterial.prototype, "emissiveColor", void 0);
99120
- __decorate([
99121
- serialize()
99122
- ], StandardMaterial.prototype, "specularPower", void 0);
99123
- __decorate([
99124
- serialize("useAlphaFromDiffuseTexture")
99125
- ], StandardMaterial.prototype, "_useAlphaFromDiffuseTexture", void 0);
99126
- __decorate([
99127
- expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
99128
- ], StandardMaterial.prototype, "useAlphaFromDiffuseTexture", void 0);
99129
- __decorate([
99130
- serialize("useEmissiveAsIllumination")
99131
- ], StandardMaterial.prototype, "_useEmissiveAsIllumination", void 0);
99132
- __decorate([
99133
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99134
- ], StandardMaterial.prototype, "useEmissiveAsIllumination", void 0);
99135
- __decorate([
99136
- serialize("linkEmissiveWithDiffuse")
99137
- ], StandardMaterial.prototype, "_linkEmissiveWithDiffuse", void 0);
99138
- __decorate([
99139
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99140
- ], StandardMaterial.prototype, "linkEmissiveWithDiffuse", void 0);
99141
- __decorate([
99142
- serialize("useSpecularOverAlpha")
99143
- ], StandardMaterial.prototype, "_useSpecularOverAlpha", void 0);
99144
- __decorate([
99145
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99146
- ], StandardMaterial.prototype, "useSpecularOverAlpha", void 0);
99147
- __decorate([
99148
- serialize("useReflectionOverAlpha")
99149
- ], StandardMaterial.prototype, "_useReflectionOverAlpha", void 0);
99150
- __decorate([
99151
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99152
- ], StandardMaterial.prototype, "useReflectionOverAlpha", void 0);
99153
- __decorate([
99154
- serialize("disableLighting")
99155
- ], StandardMaterial.prototype, "_disableLighting", void 0);
99156
- __decorate([
99157
- expandToProperty("_markAllSubMeshesAsLightsDirty")
99158
- ], StandardMaterial.prototype, "disableLighting", void 0);
99159
- __decorate([
99160
- serialize("useObjectSpaceNormalMap")
99161
- ], StandardMaterial.prototype, "_useObjectSpaceNormalMap", void 0);
99162
- __decorate([
99163
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99164
- ], StandardMaterial.prototype, "useObjectSpaceNormalMap", void 0);
99165
- __decorate([
99166
- serialize("useParallax")
99167
- ], StandardMaterial.prototype, "_useParallax", void 0);
99168
- __decorate([
99169
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99170
- ], StandardMaterial.prototype, "useParallax", void 0);
99171
- __decorate([
99172
- serialize("useParallaxOcclusion")
99173
- ], StandardMaterial.prototype, "_useParallaxOcclusion", void 0);
99174
- __decorate([
99175
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99176
- ], StandardMaterial.prototype, "useParallaxOcclusion", void 0);
99177
- __decorate([
99178
- serialize()
99179
- ], StandardMaterial.prototype, "parallaxScaleBias", void 0);
99180
- __decorate([
99181
- serialize("roughness")
99182
- ], StandardMaterial.prototype, "_roughness", void 0);
99183
- __decorate([
99184
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99185
- ], StandardMaterial.prototype, "roughness", void 0);
99186
- __decorate([
99187
- serialize()
99188
- ], StandardMaterial.prototype, "indexOfRefraction", void 0);
99189
- __decorate([
99190
- serialize()
99191
- ], StandardMaterial.prototype, "invertRefractionY", void 0);
99192
- __decorate([
99193
- serialize()
99194
- ], StandardMaterial.prototype, "alphaCutOff", void 0);
99195
- __decorate([
99196
- serialize("useLightmapAsShadowmap")
99197
- ], StandardMaterial.prototype, "_useLightmapAsShadowmap", void 0);
99198
- __decorate([
99199
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99200
- ], StandardMaterial.prototype, "useLightmapAsShadowmap", void 0);
99201
- __decorate([
99202
- serializeAsFresnelParameters("diffuseFresnelParameters")
99203
- ], StandardMaterial.prototype, "_diffuseFresnelParameters", void 0);
99204
- __decorate([
99205
- expandToProperty("_markAllSubMeshesAsFresnelDirty")
99206
- ], StandardMaterial.prototype, "diffuseFresnelParameters", void 0);
99207
- __decorate([
99208
- serializeAsFresnelParameters("opacityFresnelParameters")
99209
- ], StandardMaterial.prototype, "_opacityFresnelParameters", void 0);
99210
- __decorate([
99211
- expandToProperty("_markAllSubMeshesAsFresnelAndMiscDirty")
99212
- ], StandardMaterial.prototype, "opacityFresnelParameters", void 0);
99213
- __decorate([
99214
- serializeAsFresnelParameters("reflectionFresnelParameters")
99215
- ], StandardMaterial.prototype, "_reflectionFresnelParameters", void 0);
99216
- __decorate([
99217
- expandToProperty("_markAllSubMeshesAsFresnelDirty")
99218
- ], StandardMaterial.prototype, "reflectionFresnelParameters", void 0);
99219
- __decorate([
99220
- serializeAsFresnelParameters("refractionFresnelParameters")
99221
- ], StandardMaterial.prototype, "_refractionFresnelParameters", void 0);
99222
- __decorate([
99223
- expandToProperty("_markAllSubMeshesAsFresnelDirty")
99224
- ], StandardMaterial.prototype, "refractionFresnelParameters", void 0);
99225
- __decorate([
99226
- serializeAsFresnelParameters("emissiveFresnelParameters")
99227
- ], StandardMaterial.prototype, "_emissiveFresnelParameters", void 0);
99228
- __decorate([
99229
- expandToProperty("_markAllSubMeshesAsFresnelDirty")
99230
- ], StandardMaterial.prototype, "emissiveFresnelParameters", void 0);
99231
- __decorate([
99232
- serialize("useReflectionFresnelFromSpecular")
99233
- ], StandardMaterial.prototype, "_useReflectionFresnelFromSpecular", void 0);
99234
- __decorate([
99235
- expandToProperty("_markAllSubMeshesAsFresnelDirty")
99236
- ], StandardMaterial.prototype, "useReflectionFresnelFromSpecular", void 0);
99237
- __decorate([
99238
- serialize("useGlossinessFromSpecularMapAlpha")
99239
- ], StandardMaterial.prototype, "_useGlossinessFromSpecularMapAlpha", void 0);
99240
- __decorate([
99241
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99242
- ], StandardMaterial.prototype, "useGlossinessFromSpecularMapAlpha", void 0);
99243
- __decorate([
99244
- serialize("maxSimultaneousLights")
99245
- ], StandardMaterial.prototype, "_maxSimultaneousLights", void 0);
99246
- __decorate([
99247
- expandToProperty("_markAllSubMeshesAsLightsDirty")
99248
- ], StandardMaterial.prototype, "maxSimultaneousLights", void 0);
99249
- __decorate([
99250
- serialize("invertNormalMapX")
99251
- ], StandardMaterial.prototype, "_invertNormalMapX", void 0);
99252
- __decorate([
99253
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99254
- ], StandardMaterial.prototype, "invertNormalMapX", void 0);
99255
- __decorate([
99256
- serialize("invertNormalMapY")
99257
- ], StandardMaterial.prototype, "_invertNormalMapY", void 0);
99258
- __decorate([
99259
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99260
- ], StandardMaterial.prototype, "invertNormalMapY", void 0);
99261
- __decorate([
99262
- serialize("twoSidedLighting")
99263
- ], StandardMaterial.prototype, "_twoSidedLighting", void 0);
99264
- __decorate([
99265
- expandToProperty("_markAllSubMeshesAsTexturesDirty")
99266
- ], StandardMaterial.prototype, "twoSidedLighting", void 0);
99267
- __decorate([
99268
- serialize("applyDecalMapAfterDetailMap")
99269
- ], StandardMaterial.prototype, "_applyDecalMapAfterDetailMap", void 0);
99270
- __decorate([
99271
- expandToProperty("_markAllSubMeshesAsMiscDirty")
99272
- ], StandardMaterial.prototype, "applyDecalMapAfterDetailMap", void 0);
99273
- RegisterClass("BABYLON.StandardMaterial", StandardMaterial);
99274
- Scene.DefaultMaterialFactory = (scene) => {
99275
- return new StandardMaterial("default material", scene);
99276
- };
99277
99306
 
99278
99307
  ThinEngine.prototype.updateVideoTexture = function (texture, video, invertY) {
99279
99308
  if (!texture || texture._isDisabled) {
@@ -143326,8 +143355,10 @@ function setupSkybox(scene, options) {
143326
143355
  // Ensure the URL is absolute or correctly relative to the viewer's deployment location
143327
143356
  const texture = new BABYLON$1.CubeTexture(options.activeSkyboxUrl, scene);
143328
143357
  // Assign the texture to the scene's skybox
143329
- // Note: createDefaultSkybox replaces any existing skybox
143330
- scene.createDefaultSkybox(texture, true, 1000, 0.0, false); // PBR=true, scale=1000, blur=0.0, set reflection=false initially
143358
+ const skybox = scene.createDefaultSkybox(texture, true, 1000, 0.0, false); // PBR=true, scale=1000, blur=0.0, set reflection=false initially
143359
+ if (skybox && typeof options.skyboxRotation === "number") {
143360
+ skybox.rotation.y = options.skyboxRotation;
143361
+ }
143331
143362
  console.log(`Skybox loaded from: ${options.activeSkyboxUrl}`);
143332
143363
  }
143333
143364
  catch (error) {
@@ -164198,10 +164229,35 @@ class CustomScriptManager {
164198
164229
  // Clean up any previous executions
164199
164230
  this.cleanup();
164200
164231
  try {
164232
+ const renderObservers = [];
164233
+ // Create a wrapped version of scene.registerBeforeRender that we can track
164234
+ const wrappedScene = Object.create(this.scene);
164235
+ wrappedScene.registerBeforeRender = (callback) => {
164236
+ // Wrap the callback in error handling
164237
+ const safeCallback = () => {
164238
+ try {
164239
+ callback();
164240
+ }
164241
+ catch (err) {
164242
+ console.error('Error in custom script render callback:', err);
164243
+ // Remove all observers to prevent continuous errors
164244
+ renderObservers.forEach(observer => {
164245
+ this.scene.onBeforeRenderObservable.remove(observer);
164246
+ });
164247
+ renderObservers.length = 0;
164248
+ }
164249
+ };
164250
+ const observer = this.scene.onBeforeRenderObservable.add(safeCallback);
164251
+ renderObservers.push(observer);
164252
+ // Store cleanup function
164253
+ this.scriptCleanup.push(() => {
164254
+ this.scene.onBeforeRenderObservable.remove(observer);
164255
+ });
164256
+ };
164201
164257
  // Create the execution context
164202
164258
  const func = new Function('scene', 'camera', 'BABYLON', 'canvas', 'getScrollPercentage', 'getCurrentWaypointIndex', 'getHotspots', 'getHTMLMeshes', 'getSplats', this.customScript);
164203
- // Execute the script
164204
- func(this.scene, this.camera, BABYLON$1, this.scene.getEngine().getRenderingCanvas(), () => this.scrollPercentage, () => this.currentWaypointIndex, () => this.hotspots, () => this.htmlMeshes, () => this.scene.meshes.filter(mesh => mesh.name.includes('splat')));
164259
+ // Execute the script with wrapped scene
164260
+ func(wrappedScene, this.camera, BABYLON$1, this.scene.getEngine().getRenderingCanvas(), () => this.scrollPercentage, () => this.currentWaypointIndex, () => this.hotspots, () => this.htmlMeshes, () => this.scene.meshes.filter(mesh => mesh.name.includes('splat')));
164205
164261
  console.log('Custom script executed successfully in viewer');
164206
164262
  }
164207
164263
  catch (error) {
@@ -165007,7 +165063,7 @@ options) {
165007
165063
  // but keeping original scale/invert settings from transformed data.
165008
165064
  const tempData = Object.assign(Object.assign({}, data), { loadedModelUrl: newSplatUrl,
165009
165065
  // Keep original scale/invert settings
165010
- splatScale: data.splatScale, invertXScale: data.invertXScale, invertYScale: data.invertYScale,
165066
+ splatScale: data.splatScale, invertXScale: data.invertXScale, invertYScale: data.invertYScale, splatPosition: data.splatPosition, splatRotation: data.splatRotation,
165011
165067
  // Ensure other potentially relevant fields from data are present if needed
165012
165068
  waypoints: data.waypoints, hotspots: data.hotspots });
165013
165069
  splatRootMeshes = await loadSplatAsset(scene, tempData, (percentage, text) => {