babylonjs-loaders 9.22.2 → 9.23.0

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.
@@ -755,6 +755,7 @@ export * from "babylonjs-loaders/glTF/2.0/glTFLoader.pure";
755
755
  export * from "babylonjs-loaders/glTF/2.0/glTFLoaderExtension";
756
756
  export * from "babylonjs-loaders/glTF/2.0/glTFLoaderExtensionRegistry";
757
757
  export * from "babylonjs-loaders/glTF/2.0/glTFLoaderInterfaces";
758
+ export * from "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation.pure";
758
759
  export * from "babylonjs-loaders/glTF/2.0/openpbrMaterialLoadingAdapter";
759
760
  export * from "babylonjs-loaders/glTF/2.0/pbrMaterialLoadingAdapter";
760
761
  export * from "babylonjs-loaders/glTF/2.0/Extensions/pure";
@@ -3081,7 +3082,8 @@ export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposa
3081
3082
  }
3082
3083
 
3083
3084
  }
3084
- declare module "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation" {
3085
+ declare module "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation.pure" {
3086
+ /** This file must only contain pure code and pure imports */
3085
3087
  import { Animation } from "babylonjs/Animations/animation.pure";
3086
3088
  import { Quaternion, Vector3 } from "babylonjs/Maths/math.vector.pure";
3087
3089
  import { INode } from "babylonjs-loaders/glTF/2.0/glTFLoaderInterfaces";
@@ -3124,6 +3126,14 @@ export class WeightAnimationPropertyInfo extends AnimationPropertyInfo {
3124
3126
  babylonAnimation: Animation;
3125
3127
  }[];
3126
3128
  }
3129
+ /**
3130
+ * Registers the core glTF animation interpolation mappings.
3131
+ */
3132
+ export function RegisterGLTFLoaderAnimation(): void;
3133
+
3134
+ }
3135
+ declare module "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation" {
3136
+ export * from "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation.pure";
3127
3137
 
3128
3138
  }
3129
3139
  declare module "babylonjs-loaders/glTF/2.0/glTFLoader.pure" {
@@ -3147,7 +3157,7 @@ import { IDataBuffer } from "babylonjs/Misc/dataReader";
3147
3157
  import { Light } from "babylonjs/Lights/light";
3148
3158
  import { BoundingInfo } from "babylonjs/Culling/boundingInfo";
3149
3159
  import { AssetContainer } from "babylonjs/assetContainer";
3150
- import { AnimationPropertyInfo } from "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation";
3160
+ import { AnimationPropertyInfo } from "babylonjs-loaders/glTF/2.0/glTFLoaderAnimation.pure";
3151
3161
  import { IObjectInfo } from "babylonjs/ObjectModel/objectModelInterfaces";
3152
3162
  import { GLTFExtensionFactory } from "babylonjs-loaders/glTF/2.0/glTFLoaderExtensionRegistry";
3153
3163
  import { IInterpolationPropertyInfo } from "babylonjs/FlowGraph/typeDefinitions";
@@ -5112,6 +5122,11 @@ export class KHR_node_visibility implements IGLTFLoaderExtension {
5112
5122
  onReady(): void;
5113
5123
  dispose(): void;
5114
5124
  }
5125
+ /**
5126
+ * @internal
5127
+ * Registers KHR_node_visibility runtime dependencies without changing the extension registry.
5128
+ */
5129
+ export function _RegisterKHRNodeVisibilityRuntime(): void;
5115
5130
  /**
5116
5131
  * Registers the KHR_node_visibility glTF loader extension.
5117
5132
  * Safe to call multiple times; only the first call has an effect.
@@ -5163,6 +5178,11 @@ export class KHR_node_selectability implements IGLTFLoaderExtension {
5163
5178
  onReady(): Promise<void>;
5164
5179
  dispose(): void;
5165
5180
  }
5181
+ /**
5182
+ * @internal
5183
+ * Registers KHR_node_selectability runtime dependencies without changing the extension registry.
5184
+ */
5185
+ export function _RegisterKHRNodeSelectabilityRuntime(): void;
5166
5186
  /**
5167
5187
  * Registers the KHR_node_selectability glTF loader extension.
5168
5188
  * Safe to call multiple times; only the first call has an effect.
@@ -5215,6 +5235,11 @@ export class KHR_node_hoverability implements IGLTFLoaderExtension {
5215
5235
  onReady(): Promise<void>;
5216
5236
  dispose(): void;
5217
5237
  }
5238
+ /**
5239
+ * @internal
5240
+ * Registers KHR_node_hoverability runtime dependencies without changing the extension registry.
5241
+ */
5242
+ export function _RegisterKHRNodeHoverabilityRuntime(): void;
5218
5243
  /**
5219
5244
  * Registers the KHR_node_hoverability glTF loader extension.
5220
5245
  * Safe to call multiple times; only the first call has an effect.
@@ -6620,6 +6645,11 @@ export class KHR_interactivity implements IGLTFLoaderExtension {
6620
6645
  * populates the object model with the interactivity extension
6621
6646
  */
6622
6647
  export function _AddInteractivityObjectModel(scene: Scene): void;
6648
+ /**
6649
+ * @internal
6650
+ * Registers KHR_interactivity runtime dependencies without changing the extension registry.
6651
+ */
6652
+ export function _RegisterKHRInteractivityRuntime(): void;
6623
6653
  /**
6624
6654
  * Registers the KHR_interactivity glTF loader extension.
6625
6655
  * Safe to call multiple times; only the first call has an effect.
@@ -9617,7 +9647,7 @@ export class GaussianSplattingWorkBuffer {
9617
9647
 
9618
9648
  }
9619
9649
  declare module "babylonjs-loaders/SPLAT/gaussianSplattingStream" {
9620
- import { GaussianSplattingMesh } from "babylonjs/Meshes/GaussianSplatting/gaussianSplattingMesh";
9650
+ import { GaussianSplattingMesh, IGaussianSplattingLodBudgetParticipant } from "babylonjs/Meshes/GaussianSplatting/gaussianSplattingMesh";
9621
9651
  import { GaussianSplattingPartProxyMesh } from "babylonjs/Meshes/GaussianSplatting/gaussianSplattingPartProxyMesh";
9622
9652
  import { Scene } from "babylonjs/scene";
9623
9653
  import { Nullable } from "babylonjs/types";
@@ -9650,6 +9680,9 @@ interface ISOGLODNode {
9650
9680
  activeLod?: number;
9651
9681
  /** Distance-based ideal LOD level for this node, recomputed per frame. */
9652
9682
  optimalLod?: number;
9683
+ /** Projected screen size (pixels) of the node's AABB — the max across active cameras. Larger nodes keep finer
9684
+ * detail under the splat budget. Only computed while the budget is enabled. */
9685
+ pixelSize?: number;
9653
9686
  /** Available LOD levels for this leaf, sorted ascending (0 = finest). Set during the tree walk. */
9654
9687
  availableLevels?: number[];
9655
9688
  /** Coarsest available level (= max key), always streamed as the permanent base layer. */
@@ -9751,6 +9784,17 @@ export interface IGaussianSplattingStreamOptions {
9751
9784
  * return to it avoids a re-download. Only used when a budget enables eviction. PlayCanvas default `100`.
9752
9785
  */
9753
9786
  evictionCooldownFrames?: number;
9787
+ /**
9788
+ * Enables budget-driven LOD: caps the total rendered splats by converging a screen-space (size + distance)
9789
+ * pixel-size threshold to the budget. Selection is view-direction-independent, so it is consistent across any
9790
+ * number of active cameras (each node takes the finest level and largest projected size any camera demands).
9791
+ * A number is an explicit splat cap; `"auto"` picks a device-tiered default (desktop 2.5M / iOS 1.5M /
9792
+ * other mobile 1M; XR shares the mobile tier). **Undefined (default) disables the cap** — LOD is pure
9793
+ * distance, identical to prior behavior. Runtime-mutable via the {@link splatBudget} accessor. When this stream
9794
+ * is hosted in a compound, the compound's {@link GaussianSplattingMesh.splatBudget} (if set) overrides this and
9795
+ * apportions a shared budget across all its streams.
9796
+ */
9797
+ splatBudget?: number | "auto";
9754
9798
  /**
9755
9799
  * When set, the stream does not render itself; instead it reserves a region of this compound mesh and
9756
9800
  * decodes/sorts into it, so its splats are depth-sorted and drawn in ONE pass together with the compound's
@@ -9790,7 +9834,7 @@ export interface IGaussianSplattingStreamOptions {
9790
9834
  *
9791
9835
  * @experimental
9792
9836
  */
9793
- export class GaussianSplattingStream extends GaussianSplattingMesh {
9837
+ export class GaussianSplattingStream extends GaussianSplattingMesh implements IGaussianSplattingLodBudgetParticipant {
9794
9838
  private readonly _metadata;
9795
9839
  private readonly _rootUrl;
9796
9840
  private readonly _streamOptions;
@@ -9805,9 +9849,12 @@ export class GaussianSplattingStream extends GaussianSplattingMesh {
9805
9849
  private _lodUpdateInterval;
9806
9850
  private _lodUpdateDistance;
9807
9851
  private _maxDetailLod;
9852
+ private _lodPixelThreshold;
9853
+ private _hostBudgetAllocation;
9808
9854
  private _frustumCulling;
9809
9855
  private readonly _frustumPlanes;
9810
9856
  private readonly _cullViewProj;
9857
+ private readonly _frustumScratch;
9811
9858
  private _workBuffer;
9812
9859
  private _decodeSh;
9813
9860
  private _streamShDegree;
@@ -9838,7 +9885,8 @@ export class GaussianSplattingStream extends GaussianSplattingMesh {
9838
9885
  private _lodObserver;
9839
9886
  private _baseLayerReady;
9840
9887
  private _framesSinceLodUpdate;
9841
- private readonly _lastLodCamPos;
9888
+ private readonly _lastLodCamPositions;
9889
+ private _lastLodSignature;
9842
9890
  private _forceLodUpdate;
9843
9891
  private readonly _boundsMin;
9844
9892
  private readonly _boundsMax;
@@ -9937,6 +9985,66 @@ export class GaussianSplattingStream extends GaussianSplattingMesh {
9937
9985
  */
9938
9986
  get maxDetailLod(): number;
9939
9987
  set maxDetailLod(value: number);
9988
+ /**
9989
+ * This stream's own budget-driven LOD cap in splats (see {@link IGaussianSplattingStreamOptions.splatBudget}).
9990
+ * `0` disables the budget (pure distance LOD). Setting it caps the rendered splat count, taking effect on the
9991
+ * next frame. When this stream is hosted in a compound whose own budget is set, that shared budget overrides
9992
+ * this value — read the actual runtime cap from {@link effectiveSplatBudget}, not this getter (which always
9993
+ * reports the configured own cap).
9994
+ * @experimental
9995
+ */
9996
+ get splatBudget(): number;
9997
+ set splatBudget(value: number);
9998
+ /**
9999
+ * The splat cap actually in force this frame: a hosting compound's apportioned allocation when this stream is
10000
+ * coordinated, otherwise this stream's own {@link splatBudget}, clamped to what can be kept resident. `0` means
10001
+ * no cap (pure distance LOD). Unlike {@link splatBudget}, this reflects the compound override, so it is the value
10002
+ * to display or reason about at runtime.
10003
+ * @experimental
10004
+ */
10005
+ get effectiveSplatBudget(): number;
10006
+ /**
10007
+ * Resolves the raw {@link splatBudget} option to a concrete cap: `undefined` ⇒ 0 (disabled), `"auto"` ⇒ a
10008
+ * device-tiered default, a positive number ⇒ itself (floored).
10009
+ * @param option the raw option value
10010
+ * @returns the resolved splat cap (0 = disabled)
10011
+ */
10012
+ private _resolveSplatBudget;
10013
+ /**
10014
+ * Device-tiered default splat budget for {@link splatBudget} `"auto"`: desktop 2.5M, iOS 1.5M, other mobile
10015
+ * (incl. Android/XR) 1M. XR is folded into the mobile tier (no reliable at-construction detection).
10016
+ * @returns the default splat cap for this device
10017
+ */
10018
+ private _computeDefaultSplatBudget;
10019
+ /**
10020
+ * The splat budget this stream converges against this frame: the compound's apportioned allocation when hosted
10021
+ * and coordinated, else this stream's own resolved budget. Clamped to the resident budget so the stream never
10022
+ * targets more splats than can be kept resident. `0` means the budget is disabled.
10023
+ * @returns the effective splat cap (0 = disabled)
10024
+ */
10025
+ private _effectiveSplatBudget;
10026
+ /**
10027
+ * Whether budget-driven LOD is active this frame.
10028
+ * @returns true when a positive effective budget is in force
10029
+ */
10030
+ private _splatBudgetEnabled;
10031
+ /**
10032
+ * {@link IGaussianSplattingLodBudgetParticipant}: the splats this stream would render at full (distance-optimal)
10033
+ * detail — its demand on a host compound's shared budget. Computed from the current per-node distance-optimal
10034
+ * levels (no pixel threshold), so it does not depend on the allocation it is helping to compute.
10035
+ * @returns the full-detail rendered splat count (0 before the base layer is ready)
10036
+ */
10037
+ getBudgetDemand(): number;
10038
+ /**
10039
+ * {@link IGaussianSplattingLodBudgetParticipant}: sets the compound's apportioned share of the shared budget.
10040
+ * `null` releases coordination (revert to this stream's own {@link splatBudget}); a number (incl. 0, meaning
10041
+ * "coordinated at the coarsest level") drives the pixel-threshold convergence. Any change to the (already integer)
10042
+ * allocation forces a next-frame re-eval: a decrease may put the current selection over the new cap, and even a
10043
+ * small increase can unlock a finer level that a stationary camera would otherwise never re-evaluate to. The
10044
+ * apportioned demand is allocation-independent, so this settles in one step and does not churn frame to frame.
10045
+ * @param splats the apportioned allocation, or null to release coordination
10046
+ */
10047
+ setBudgetAllocation(splats: Nullable<number>): void;
9940
10048
  /**
9941
10049
  * Coarsest LOD level index in the scene (number of LOD levels minus one). Useful as the upper bound
9942
10050
  * for {@link maxDetailLod}.
@@ -9980,10 +10088,17 @@ export class GaussianSplattingStream extends GaussianSplattingMesh {
9980
10088
  */
9981
10089
  private _getEffectiveWorldMatrix;
9982
10090
  /**
9983
- * Re-evaluates the optimal LOD for every node based on the camera position. The result is stored in
9984
- * each node's `optimalLod`. Rendering is unaffected; this currently drives only diagnostics and the
9985
- * debug wireframe display.
9986
- * @param camera camera to evaluate against (defaults to the scene's active camera)
10091
+ * The cameras the LOD should serve: `scene.activeCameras` when set (split-view / multi-view), else the single
10092
+ * `scene.activeCamera`. Mirrors the sort path's multi-camera handling.
10093
+ * @returns the non-null active cameras (may be empty)
10094
+ */
10095
+ private _getActiveLodCameras;
10096
+ /**
10097
+ * Re-evaluates the optimal LOD for every node from the active cameras. Each node takes the finest level and the
10098
+ * largest projected pixel size any active camera demands (so every pane of a split view is served), and the
10099
+ * frustum bias uses the union of the frusta. Selection is view-direction-independent, so single- and multi-camera
10100
+ * rendering are consistent. The results are stored in each node's `optimalLod` / `pixelSize`.
10101
+ * @param camera when provided, evaluate against just this camera; otherwise use the active-camera set
9987
10102
  */
9988
10103
  evaluateOptimalLods(camera?: Nullable<Camera>): void;
9989
10104
  /**
@@ -10167,10 +10282,61 @@ export class GaussianSplattingStream extends GaussianSplattingMesh {
10167
10282
  */
10168
10283
  private _cappedLevelForNode;
10169
10284
  /**
10170
- * Computes each node's {@link ISOGLODNode.targetLevel}: the distance-based optimal level snapped to an
10171
- * available level, capped so no node renders finer (more detailed) than {@link maxDetailLod}.
10285
+ * Computes each node's {@link ISOGLODNode.targetLevel}. With the splat budget disabled this is the
10286
+ * distance-optimal level snapped to an available level (capped by {@link maxDetailLod}) — unchanged prior
10287
+ * behavior. With the budget enabled it converges a global pixel-size threshold to the budget and coarsens each
10288
+ * node from its distance-optimal ceiling by how far its projected size falls below that threshold.
10172
10289
  */
10173
10290
  private _computeTargetLevels;
10291
+ /**
10292
+ * Splat count of the always-rendered environment layer (0 when none), coerced to a finite non-negative integer.
10293
+ * Counted as a fixed cost in both the budget demand and the leaf convergence so it is never over-drawn.
10294
+ * @returns the environment's rendered splat count
10295
+ */
10296
+ private _environmentSplatCount;
10297
+ /**
10298
+ * The level a node renders at for a given pixel-size threshold `t`: its distance-optimal ceiling, coarsened by
10299
+ * `round(log_mult(t / pixelSize))` geometric steps when its projected size is below `t`. Snapped to
10300
+ * an available level honoring {@link maxDetailLod}.
10301
+ * @param node leaf node
10302
+ * @param t pixel-size threshold (pixels)
10303
+ * @returns the chosen available LOD level
10304
+ */
10305
+ private _budgetedLevel;
10306
+ /**
10307
+ * Splat count of a node's file at a given (already snapped) available level.
10308
+ * @param node leaf node
10309
+ * @param level available LOD level
10310
+ * @returns the level's splat count (0 if absent)
10311
+ */
10312
+ private _countAtLevel;
10313
+ /**
10314
+ * Sum of every leaf's rendered splat count at pixel-size threshold `t`.
10315
+ * @param t pixel-size threshold (pixels)
10316
+ * @returns total rendered splats
10317
+ */
10318
+ private _totalSplatsAtThreshold;
10319
+ /**
10320
+ * Converges the global pixel-size threshold {@link _lodPixelThreshold} to the largest detail whose total rendered
10321
+ * splats is still within `budget` (the pixel-scale cut, floored at the true sub-pixel limit of 1 px). The
10322
+ * total is monotonically non-increasing in the threshold, so a bisection on `[floorT, ceilT]` — where `ceilT`
10323
+ * coarsens every node to its coarsest available level — gives a GUARANTEED result at or under the cap. When even
10324
+ * that coarsest state exceeds the budget (the budget is below the pinned minimum detail), the threshold is set to
10325
+ * `ceilT` so every node renders at minimum detail — the best achievable; the cap is then unavoidable. O(leaves)
10326
+ * per iteration; no decode.
10327
+ * @param budget target maximum rendered splat count
10328
+ */
10329
+ private _convergePixelThreshold;
10330
+ /**
10331
+ * The finest already-decoded level of a node that is at least as coarse as `level` (index >= level). Used to
10332
+ * enforce the budget cap immediately without waiting for a download. Returns `null` when no such level is resident
10333
+ * — including when eviction has freed the base layer — so the caller keeps the currently-visible (resident) file
10334
+ * rather than switching to a non-resident one, which would render a hole.
10335
+ * @param node leaf node
10336
+ * @param level the minimum coarseness (level index) required
10337
+ * @returns a resident level index >= level, or null when none is resident
10338
+ */
10339
+ private _residentLevelAtLeast;
10174
10340
  /**
10175
10341
  * Applies each node's {@link ISOGLODNode.targetLevel}: switches a node to its target level when that
10176
10342
  * level's file is already decoded, otherwise records a pending download request for the file and leaves
@@ -10224,17 +10390,28 @@ export class GaussianSplattingStream extends GaussianSplattingMesh {
10224
10390
  */
10225
10391
  private _onLodFrame;
10226
10392
  /**
10227
- * Updates each leaf node's {@link ISOGLODNode.inFrustum} flag from a per-node frustum test against the
10228
- * active camera. When {@link frustumCulling} is disabled (or there is no camera) every node is marked
10229
- * in-frustum. Bounds are static (from the LOD tree), so flags are valid for all nodes regardless of
10230
- * decode state. Returns true when any node's in-frustum state changed (so the LOD bias must be re-applied).
10393
+ * A cheap signature of the discrete inputs (besides camera translation, tracked separately) that affect projected
10394
+ * pixel size: each camera's identity, FOV, FOV mode and viewport, plus the engine render size and the effective
10395
+ * world matrix revision. A change invalidates the throttled budget LOD so a colocated camera swap / viewport
10396
+ * resize / FOV change — or the stream (or its hosted proxy) being moved or scaled while still in frustum, which
10397
+ * shifts every node's distance and projected size — can't leave it stale.
10398
+ * @param cameras the active cameras
10399
+ * @returns the signature string
10400
+ */
10401
+ private _computeLodSignature;
10402
+ /**
10403
+ * Updates each leaf node's {@link ISOGLODNode.inFrustum} flag: a node is in-frustum if it is inside ANY
10404
+ * active camera's frustum (the union). When {@link frustumCulling} is disabled (or there are no cameras)
10405
+ * every node is marked in-frustum. Bounds are static (from the LOD tree), so flags are valid for all nodes
10406
+ * regardless of decode state. Returns true when any node's in-frustum state changed (so the LOD bias must be re-applied).
10231
10407
  * @returns whether any node's in-frustum state changed
10232
10408
  */
10233
10409
  private _updateNodeFrustum;
10234
10410
  /**
10235
- * Reads the splat count from SOG metadata.
10411
+ * Reads the splat count from SOG metadata, coerced to a finite non-negative integer (metadata is untrusted, so
10412
+ * `count` / `shape[0]` may be a string or malformed — a non-numeric value must not leak into count arithmetic).
10236
10413
  * @param data SOG metadata
10237
- * @returns the splat count
10414
+ * @returns the splat count (0 when absent/invalid)
10238
10415
  */
10239
10416
  private static _GetSplatCount;
10240
10417
  /**
@@ -10897,6 +11074,12 @@ import { Vector2 } from "babylonjs/Maths/math.vector";
10897
11074
  * Options for loading OBJ/MTL files
10898
11075
  */
10899
11076
  export type OBJLoadingOptions = {
11077
+ /**
11078
+ * Defines the character encoding used to decode OBJ and MTL files.
11079
+ * Use "auto" to detect UTF-8/UTF-16 and fall back to GB18030, or provide an encoding label supported by TextDecoder.
11080
+ * Defaults to "auto".
11081
+ */
11082
+ encoding?: string;
10900
11083
  /**
10901
11084
  * Defines if UVs are optimized by default during load.
10902
11085
  */
@@ -10966,6 +11149,10 @@ import { OBJLoadingOptions } from "babylonjs-loaders/OBJ/objLoadingOptions";
10966
11149
  * This is a babylon scene loader plugin.
10967
11150
  */
10968
11151
  export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
11152
+ /**
11153
+ * Defines the character encoding used to decode OBJ and MTL files.
11154
+ */
11155
+ static ENCODING: string;
10969
11156
  /**
10970
11157
  * Defines if UVs are optimized by default during load.
10971
11158
  */
@@ -11017,7 +11204,11 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
11017
11204
  /**
11018
11205
  * Defines the extension the plugin is able to load.
11019
11206
  */
11020
- readonly extensions: ".obj";
11207
+ readonly extensions: {
11208
+ readonly ".obj": {
11209
+ readonly isBinary: true;
11210
+ };
11211
+ };
11021
11212
  private _assetContainer;
11022
11213
  private _loadingOptions;
11023
11214
  /**
@@ -11039,6 +11230,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
11039
11230
  * @param onFailure
11040
11231
  */
11041
11232
  private _loadMTL;
11233
+ private _decode;
11042
11234
  /** @internal */
11043
11235
  createPlugin(options: SceneLoaderPluginOptions): ISceneLoaderPluginAsync | ISceneLoaderPlugin;
11044
11236
  /**
@@ -11062,7 +11254,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
11062
11254
  * @param rootUrl root url to load from
11063
11255
  * @returns a promise which completes when objects have been loaded to the scene
11064
11256
  */
11065
- loadAsync(scene: Scene, data: string, rootUrl: string): Promise<void>;
11257
+ loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string): Promise<void>;
11066
11258
  /**
11067
11259
  * Load into an asset container.
11068
11260
  * @param scene The scene to load into
@@ -11070,7 +11262,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
11070
11262
  * @param rootUrl The root url for scene and resources
11071
11263
  * @returns The loaded asset container
11072
11264
  */
11073
- loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string): Promise<AssetContainer>;
11265
+ loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string): Promise<AssetContainer>;
11074
11266
  /**
11075
11267
  * Read the OBJ file and create an Array of meshes.
11076
11268
  * Each mesh contains all information given by the OBJ and the MTL file.
@@ -11093,7 +11285,11 @@ export function RegisterOBJFileLoader(): void;
11093
11285
  declare module "babylonjs-loaders/OBJ/objFileLoader.metadata" {
11094
11286
  export const OBJFileLoaderMetadata: {
11095
11287
  readonly name: "obj";
11096
- readonly extensions: ".obj";
11288
+ readonly extensions: {
11289
+ readonly ".obj": {
11290
+ readonly isBinary: true;
11291
+ };
11292
+ };
11097
11293
  };
11098
11294
 
11099
11295
  }
@@ -15334,7 +15530,8 @@ declare namespace BABYLON {
15334
15530
 
15335
15531
  }
15336
15532
  declare namespace BABYLON.GLTF2 {
15337
- /** @internal */
15533
+ /** This file must only contain pure code and pure imports */
15534
+ /** @internal */
15338
15535
  export type GetValueFn = (target: any, source: Float32Array, offset: number, scale: number) => any;
15339
15536
  /** @internal */
15340
15537
  export function getVector3(_target: any, source: Float32Array, offset: number, scale: number): Vector3;
@@ -15372,7 +15569,20 @@ declare namespace BABYLON.GLTF2 {
15372
15569
  babylonAnimation: Animation;
15373
15570
  }[];
15374
15571
  }
15572
+ /**
15573
+ * Registers the core glTF animation interpolation mappings.
15574
+ */
15575
+ export function RegisterGLTFLoaderAnimation(): void;
15576
+
15577
+
15578
+
15579
+ }
15580
+ declare namespace BABYLON {
15581
+
15375
15582
 
15583
+ }
15584
+ declare namespace BABYLON.GLTF2 {
15585
+
15376
15586
 
15377
15587
 
15378
15588
  }
@@ -17327,6 +17537,11 @@ declare namespace BABYLON.GLTF2.Loader.Extensions {
17327
17537
  onReady(): void;
17328
17538
  dispose(): void;
17329
17539
  }
17540
+ /**
17541
+ * @internal
17542
+ * Registers KHR_node_visibility runtime dependencies without changing the extension registry.
17543
+ */
17544
+ export function _RegisterKHRNodeVisibilityRuntime(): void;
17330
17545
  /**
17331
17546
  * Registers the KHR_node_visibility glTF loader extension.
17332
17547
  * Safe to call multiple times; only the first call has an effect.
@@ -17388,6 +17603,11 @@ declare namespace BABYLON.GLTF2.Loader.Extensions {
17388
17603
  onReady(): Promise<void>;
17389
17604
  dispose(): void;
17390
17605
  }
17606
+ /**
17607
+ * @internal
17608
+ * Registers KHR_node_selectability runtime dependencies without changing the extension registry.
17609
+ */
17610
+ export function _RegisterKHRNodeSelectabilityRuntime(): void;
17391
17611
  /**
17392
17612
  * Registers the KHR_node_selectability glTF loader extension.
17393
17613
  * Safe to call multiple times; only the first call has an effect.
@@ -17450,6 +17670,11 @@ declare namespace BABYLON.GLTF2.Loader.Extensions {
17450
17670
  onReady(): Promise<void>;
17451
17671
  dispose(): void;
17452
17672
  }
17673
+ /**
17674
+ * @internal
17675
+ * Registers KHR_node_hoverability runtime dependencies without changing the extension registry.
17676
+ */
17677
+ export function _RegisterKHRNodeHoverabilityRuntime(): void;
17453
17678
  /**
17454
17679
  * Registers the KHR_node_hoverability glTF loader extension.
17455
17680
  * Safe to call multiple times; only the first call has an effect.
@@ -19006,6 +19231,11 @@ declare namespace BABYLON.GLTF2.Loader.Extensions {
19006
19231
  * populates the object model with the interactivity extension
19007
19232
  */
19008
19233
  export function _AddInteractivityObjectModel(scene: Scene): void;
19234
+ /**
19235
+ * @internal
19236
+ * Registers KHR_interactivity runtime dependencies without changing the extension registry.
19237
+ */
19238
+ export function _RegisterKHRInteractivityRuntime(): void;
19009
19239
  /**
19010
19240
  * Registers the KHR_interactivity glTF loader extension.
19011
19241
  * Safe to call multiple times; only the first call has an effect.
@@ -22061,6 +22291,9 @@ declare namespace BABYLON {
22061
22291
  activeLod?: number;
22062
22292
  /** Distance-based ideal LOD level for this node, recomputed per frame. */
22063
22293
  optimalLod?: number;
22294
+ /** Projected screen size (pixels) of the node's AABB — the max across active cameras. Larger nodes keep finer
22295
+ * detail under the splat budget. Only computed while the budget is enabled. */
22296
+ pixelSize?: number;
22064
22297
  /** Available LOD levels for this leaf, sorted ascending (0 = finest). Set during the tree walk. */
22065
22298
  availableLevels?: number[];
22066
22299
  /** Coarsest available level (= max key), always streamed as the permanent base layer. */
@@ -22162,6 +22395,17 @@ declare namespace BABYLON {
22162
22395
  * return to it avoids a re-download. Only used when a budget enables eviction. PlayCanvas default `100`.
22163
22396
  */
22164
22397
  evictionCooldownFrames?: number;
22398
+ /**
22399
+ * Enables budget-driven LOD: caps the total rendered splats by converging a screen-space (size + distance)
22400
+ * pixel-size threshold to the budget. Selection is view-direction-independent, so it is consistent across any
22401
+ * number of active cameras (each node takes the finest level and largest projected size any camera demands).
22402
+ * A number is an explicit splat cap; `"auto"` picks a device-tiered default (desktop 2.5M / iOS 1.5M /
22403
+ * other mobile 1M; XR shares the mobile tier). **Undefined (default) disables the cap** — LOD is pure
22404
+ * distance, identical to prior behavior. Runtime-mutable via the {@link splatBudget} accessor. When this stream
22405
+ * is hosted in a compound, the compound's {@link GaussianSplattingMesh.splatBudget} (if set) overrides this and
22406
+ * apportions a shared budget across all its streams.
22407
+ */
22408
+ splatBudget?: number | "auto";
22165
22409
  /**
22166
22410
  * When set, the stream does not render itself; instead it reserves a region of this compound mesh and
22167
22411
  * decodes/sorts into it, so its splats are depth-sorted and drawn in ONE pass together with the compound's
@@ -22201,7 +22445,7 @@ declare namespace BABYLON {
22201
22445
  *
22202
22446
  * @experimental
22203
22447
  */
22204
- export class GaussianSplattingStream extends GaussianSplattingMesh {
22448
+ export class GaussianSplattingStream extends GaussianSplattingMesh implements IGaussianSplattingLodBudgetParticipant {
22205
22449
  private readonly _metadata;
22206
22450
  private readonly _rootUrl;
22207
22451
  private readonly _streamOptions;
@@ -22216,9 +22460,12 @@ declare namespace BABYLON {
22216
22460
  private _lodUpdateInterval;
22217
22461
  private _lodUpdateDistance;
22218
22462
  private _maxDetailLod;
22463
+ private _lodPixelThreshold;
22464
+ private _hostBudgetAllocation;
22219
22465
  private _frustumCulling;
22220
22466
  private readonly _frustumPlanes;
22221
22467
  private readonly _cullViewProj;
22468
+ private readonly _frustumScratch;
22222
22469
  private _workBuffer;
22223
22470
  private _decodeSh;
22224
22471
  private _streamShDegree;
@@ -22249,7 +22496,8 @@ declare namespace BABYLON {
22249
22496
  private _lodObserver;
22250
22497
  private _baseLayerReady;
22251
22498
  private _framesSinceLodUpdate;
22252
- private readonly _lastLodCamPos;
22499
+ private readonly _lastLodCamPositions;
22500
+ private _lastLodSignature;
22253
22501
  private _forceLodUpdate;
22254
22502
  private readonly _boundsMin;
22255
22503
  private readonly _boundsMax;
@@ -22348,6 +22596,66 @@ declare namespace BABYLON {
22348
22596
  */
22349
22597
  get maxDetailLod(): number;
22350
22598
  set maxDetailLod(value: number);
22599
+ /**
22600
+ * This stream's own budget-driven LOD cap in splats (see {@link IGaussianSplattingStreamOptions.splatBudget}).
22601
+ * `0` disables the budget (pure distance LOD). Setting it caps the rendered splat count, taking effect on the
22602
+ * next frame. When this stream is hosted in a compound whose own budget is set, that shared budget overrides
22603
+ * this value — read the actual runtime cap from {@link effectiveSplatBudget}, not this getter (which always
22604
+ * reports the configured own cap).
22605
+ * @experimental
22606
+ */
22607
+ get splatBudget(): number;
22608
+ set splatBudget(value: number);
22609
+ /**
22610
+ * The splat cap actually in force this frame: a hosting compound's apportioned allocation when this stream is
22611
+ * coordinated, otherwise this stream's own {@link splatBudget}, clamped to what can be kept resident. `0` means
22612
+ * no cap (pure distance LOD). Unlike {@link splatBudget}, this reflects the compound override, so it is the value
22613
+ * to display or reason about at runtime.
22614
+ * @experimental
22615
+ */
22616
+ get effectiveSplatBudget(): number;
22617
+ /**
22618
+ * Resolves the raw {@link splatBudget} option to a concrete cap: `undefined` ⇒ 0 (disabled), `"auto"` ⇒ a
22619
+ * device-tiered default, a positive number ⇒ itself (floored).
22620
+ * @param option the raw option value
22621
+ * @returns the resolved splat cap (0 = disabled)
22622
+ */
22623
+ private _resolveSplatBudget;
22624
+ /**
22625
+ * Device-tiered default splat budget for {@link splatBudget} `"auto"`: desktop 2.5M, iOS 1.5M, other mobile
22626
+ * (incl. Android/XR) 1M. XR is folded into the mobile tier (no reliable at-construction detection).
22627
+ * @returns the default splat cap for this device
22628
+ */
22629
+ private _computeDefaultSplatBudget;
22630
+ /**
22631
+ * The splat budget this stream converges against this frame: the compound's apportioned allocation when hosted
22632
+ * and coordinated, else this stream's own resolved budget. Clamped to the resident budget so the stream never
22633
+ * targets more splats than can be kept resident. `0` means the budget is disabled.
22634
+ * @returns the effective splat cap (0 = disabled)
22635
+ */
22636
+ private _effectiveSplatBudget;
22637
+ /**
22638
+ * Whether budget-driven LOD is active this frame.
22639
+ * @returns true when a positive effective budget is in force
22640
+ */
22641
+ private _splatBudgetEnabled;
22642
+ /**
22643
+ * {@link IGaussianSplattingLodBudgetParticipant}: the splats this stream would render at full (distance-optimal)
22644
+ * detail — its demand on a host compound's shared budget. Computed from the current per-node distance-optimal
22645
+ * levels (no pixel threshold), so it does not depend on the allocation it is helping to compute.
22646
+ * @returns the full-detail rendered splat count (0 before the base layer is ready)
22647
+ */
22648
+ getBudgetDemand(): number;
22649
+ /**
22650
+ * {@link IGaussianSplattingLodBudgetParticipant}: sets the compound's apportioned share of the shared budget.
22651
+ * `null` releases coordination (revert to this stream's own {@link splatBudget}); a number (incl. 0, meaning
22652
+ * "coordinated at the coarsest level") drives the pixel-threshold convergence. Any change to the (already integer)
22653
+ * allocation forces a next-frame re-eval: a decrease may put the current selection over the new cap, and even a
22654
+ * small increase can unlock a finer level that a stationary camera would otherwise never re-evaluate to. The
22655
+ * apportioned demand is allocation-independent, so this settles in one step and does not churn frame to frame.
22656
+ * @param splats the apportioned allocation, or null to release coordination
22657
+ */
22658
+ setBudgetAllocation(splats: Nullable<number>): void;
22351
22659
  /**
22352
22660
  * Coarsest LOD level index in the scene (number of LOD levels minus one). Useful as the upper bound
22353
22661
  * for {@link maxDetailLod}.
@@ -22391,10 +22699,17 @@ declare namespace BABYLON {
22391
22699
  */
22392
22700
  private _getEffectiveWorldMatrix;
22393
22701
  /**
22394
- * Re-evaluates the optimal LOD for every node based on the camera position. The result is stored in
22395
- * each node's `optimalLod`. Rendering is unaffected; this currently drives only diagnostics and the
22396
- * debug wireframe display.
22397
- * @param camera camera to evaluate against (defaults to the scene's active camera)
22702
+ * The cameras the LOD should serve: `scene.activeCameras` when set (split-view / multi-view), else the single
22703
+ * `scene.activeCamera`. Mirrors the sort path's multi-camera handling.
22704
+ * @returns the non-null active cameras (may be empty)
22705
+ */
22706
+ private _getActiveLodCameras;
22707
+ /**
22708
+ * Re-evaluates the optimal LOD for every node from the active cameras. Each node takes the finest level and the
22709
+ * largest projected pixel size any active camera demands (so every pane of a split view is served), and the
22710
+ * frustum bias uses the union of the frusta. Selection is view-direction-independent, so single- and multi-camera
22711
+ * rendering are consistent. The results are stored in each node's `optimalLod` / `pixelSize`.
22712
+ * @param camera when provided, evaluate against just this camera; otherwise use the active-camera set
22398
22713
  */
22399
22714
  evaluateOptimalLods(camera?: Nullable<Camera>): void;
22400
22715
  /**
@@ -22578,10 +22893,61 @@ declare namespace BABYLON {
22578
22893
  */
22579
22894
  private _cappedLevelForNode;
22580
22895
  /**
22581
- * Computes each node's {@link ISOGLODNode.targetLevel}: the distance-based optimal level snapped to an
22582
- * available level, capped so no node renders finer (more detailed) than {@link maxDetailLod}.
22896
+ * Computes each node's {@link ISOGLODNode.targetLevel}. With the splat budget disabled this is the
22897
+ * distance-optimal level snapped to an available level (capped by {@link maxDetailLod}) — unchanged prior
22898
+ * behavior. With the budget enabled it converges a global pixel-size threshold to the budget and coarsens each
22899
+ * node from its distance-optimal ceiling by how far its projected size falls below that threshold.
22583
22900
  */
22584
22901
  private _computeTargetLevels;
22902
+ /**
22903
+ * Splat count of the always-rendered environment layer (0 when none), coerced to a finite non-negative integer.
22904
+ * Counted as a fixed cost in both the budget demand and the leaf convergence so it is never over-drawn.
22905
+ * @returns the environment's rendered splat count
22906
+ */
22907
+ private _environmentSplatCount;
22908
+ /**
22909
+ * The level a node renders at for a given pixel-size threshold `t`: its distance-optimal ceiling, coarsened by
22910
+ * `round(log_mult(t / pixelSize))` geometric steps when its projected size is below `t`. Snapped to
22911
+ * an available level honoring {@link maxDetailLod}.
22912
+ * @param node leaf node
22913
+ * @param t pixel-size threshold (pixels)
22914
+ * @returns the chosen available LOD level
22915
+ */
22916
+ private _budgetedLevel;
22917
+ /**
22918
+ * Splat count of a node's file at a given (already snapped) available level.
22919
+ * @param node leaf node
22920
+ * @param level available LOD level
22921
+ * @returns the level's splat count (0 if absent)
22922
+ */
22923
+ private _countAtLevel;
22924
+ /**
22925
+ * Sum of every leaf's rendered splat count at pixel-size threshold `t`.
22926
+ * @param t pixel-size threshold (pixels)
22927
+ * @returns total rendered splats
22928
+ */
22929
+ private _totalSplatsAtThreshold;
22930
+ /**
22931
+ * Converges the global pixel-size threshold {@link _lodPixelThreshold} to the largest detail whose total rendered
22932
+ * splats is still within `budget` (the pixel-scale cut, floored at the true sub-pixel limit of 1 px). The
22933
+ * total is monotonically non-increasing in the threshold, so a bisection on `[floorT, ceilT]` — where `ceilT`
22934
+ * coarsens every node to its coarsest available level — gives a GUARANTEED result at or under the cap. When even
22935
+ * that coarsest state exceeds the budget (the budget is below the pinned minimum detail), the threshold is set to
22936
+ * `ceilT` so every node renders at minimum detail — the best achievable; the cap is then unavoidable. O(leaves)
22937
+ * per iteration; no decode.
22938
+ * @param budget target maximum rendered splat count
22939
+ */
22940
+ private _convergePixelThreshold;
22941
+ /**
22942
+ * The finest already-decoded level of a node that is at least as coarse as `level` (index >= level). Used to
22943
+ * enforce the budget cap immediately without waiting for a download. Returns `null` when no such level is resident
22944
+ * — including when eviction has freed the base layer — so the caller keeps the currently-visible (resident) file
22945
+ * rather than switching to a non-resident one, which would render a hole.
22946
+ * @param node leaf node
22947
+ * @param level the minimum coarseness (level index) required
22948
+ * @returns a resident level index >= level, or null when none is resident
22949
+ */
22950
+ private _residentLevelAtLeast;
22585
22951
  /**
22586
22952
  * Applies each node's {@link ISOGLODNode.targetLevel}: switches a node to its target level when that
22587
22953
  * level's file is already decoded, otherwise records a pending download request for the file and leaves
@@ -22635,17 +23001,28 @@ declare namespace BABYLON {
22635
23001
  */
22636
23002
  private _onLodFrame;
22637
23003
  /**
22638
- * Updates each leaf node's {@link ISOGLODNode.inFrustum} flag from a per-node frustum test against the
22639
- * active camera. When {@link frustumCulling} is disabled (or there is no camera) every node is marked
22640
- * in-frustum. Bounds are static (from the LOD tree), so flags are valid for all nodes regardless of
22641
- * decode state. Returns true when any node's in-frustum state changed (so the LOD bias must be re-applied).
23004
+ * A cheap signature of the discrete inputs (besides camera translation, tracked separately) that affect projected
23005
+ * pixel size: each camera's identity, FOV, FOV mode and viewport, plus the engine render size and the effective
23006
+ * world matrix revision. A change invalidates the throttled budget LOD so a colocated camera swap / viewport
23007
+ * resize / FOV change — or the stream (or its hosted proxy) being moved or scaled while still in frustum, which
23008
+ * shifts every node's distance and projected size — can't leave it stale.
23009
+ * @param cameras the active cameras
23010
+ * @returns the signature string
23011
+ */
23012
+ private _computeLodSignature;
23013
+ /**
23014
+ * Updates each leaf node's {@link ISOGLODNode.inFrustum} flag: a node is in-frustum if it is inside ANY
23015
+ * active camera's frustum (the union). When {@link frustumCulling} is disabled (or there are no cameras)
23016
+ * every node is marked in-frustum. Bounds are static (from the LOD tree), so flags are valid for all nodes
23017
+ * regardless of decode state. Returns true when any node's in-frustum state changed (so the LOD bias must be re-applied).
22642
23018
  * @returns whether any node's in-frustum state changed
22643
23019
  */
22644
23020
  private _updateNodeFrustum;
22645
23021
  /**
22646
- * Reads the splat count from SOG metadata.
23022
+ * Reads the splat count from SOG metadata, coerced to a finite non-negative integer (metadata is untrusted, so
23023
+ * `count` / `shape[0]` may be a string or malformed — a non-numeric value must not leak into count arithmetic).
22647
23024
  * @param data SOG metadata
22648
- * @returns the splat count
23025
+ * @returns the splat count (0 when absent/invalid)
22649
23026
  */
22650
23027
  private static _GetSplatCount;
22651
23028
  /**
@@ -23289,6 +23666,12 @@ declare namespace BABYLON {
23289
23666
  * Options for loading OBJ/MTL files
23290
23667
  */
23291
23668
  export type OBJLoadingOptions = {
23669
+ /**
23670
+ * Defines the character encoding used to decode OBJ and MTL files.
23671
+ * Use "auto" to detect UTF-8/UTF-16 and fall back to GB18030, or provide an encoding label supported by TextDecoder.
23672
+ * Defaults to "auto".
23673
+ */
23674
+ encoding?: string;
23292
23675
  /**
23293
23676
  * Defines if UVs are optimized by default during load.
23294
23677
  */
@@ -23346,6 +23729,10 @@ declare namespace BABYLON {
23346
23729
  * This is a babylon scene loader plugin.
23347
23730
  */
23348
23731
  export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
23732
+ /**
23733
+ * Defines the character encoding used to decode OBJ and MTL files.
23734
+ */
23735
+ static ENCODING: string;
23349
23736
  /**
23350
23737
  * Defines if UVs are optimized by default during load.
23351
23738
  */
@@ -23397,7 +23784,11 @@ declare namespace BABYLON {
23397
23784
  /**
23398
23785
  * Defines the extension the plugin is able to load.
23399
23786
  */
23400
- readonly extensions: ".obj";
23787
+ readonly extensions: {
23788
+ readonly ".obj": {
23789
+ readonly isBinary: true;
23790
+ };
23791
+ };
23401
23792
  private _assetContainer;
23402
23793
  private _loadingOptions;
23403
23794
  /**
@@ -23419,6 +23810,7 @@ declare namespace BABYLON {
23419
23810
  * @param onFailure
23420
23811
  */
23421
23812
  private _loadMTL;
23813
+ private _decode;
23422
23814
  /** @internal */
23423
23815
  createPlugin(options: SceneLoaderPluginOptions): ISceneLoaderPluginAsync | ISceneLoaderPlugin;
23424
23816
  /**
@@ -23442,7 +23834,7 @@ declare namespace BABYLON {
23442
23834
  * @param rootUrl root url to load from
23443
23835
  * @returns a promise which completes when objects have been loaded to the scene
23444
23836
  */
23445
- loadAsync(scene: Scene, data: string, rootUrl: string): Promise<void>;
23837
+ loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string): Promise<void>;
23446
23838
  /**
23447
23839
  * Load into an asset container.
23448
23840
  * @param scene The scene to load into
@@ -23450,7 +23842,7 @@ declare namespace BABYLON {
23450
23842
  * @param rootUrl The root url for scene and resources
23451
23843
  * @returns The loaded asset container
23452
23844
  */
23453
- loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string): Promise<AssetContainer>;
23845
+ loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string): Promise<AssetContainer>;
23454
23846
  /**
23455
23847
  * Read the OBJ file and create an Array of meshes.
23456
23848
  * Each mesh contains all information given by the OBJ and the MTL file.
@@ -23472,7 +23864,11 @@ declare namespace BABYLON {
23472
23864
 
23473
23865
  export var OBJFileLoaderMetadata: {
23474
23866
  readonly name: "obj";
23475
- readonly extensions: ".obj";
23867
+ readonly extensions: {
23868
+ readonly ".obj": {
23869
+ readonly isBinary: true;
23870
+ };
23871
+ };
23476
23872
  };
23477
23873
 
23478
23874