Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/framework/components/gsplat/gsplat-asset-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ class GSplatAssetLoader extends GSplatAssetLoaderBase {
this._registry = registry;
}

get isLoading() {
return this._currentlyLoading.size > 0 || this._loadQueue.length > 0;
}

/**
* Initiates loading of a gsplat asset. This is a fire-and-forget operation that starts
* the loading process. Use getResource() later to check if the asset has finished loading.
Expand Down
23 changes: 17 additions & 6 deletions src/framework/components/gsplat/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,35 @@ class GSplatComponentSystem extends ComponentSystem {
/**
* Fired every frame for each camera and layer combination rendering GSplats in unified mode.
* The handler is passed the {@link CameraComponent}, the {@link Layer}, a boolean indicating
* if the current frame has up-to-date sorting, and a boolean indicating if resources are loading.
* if the current frame has up-to-date sorting, and a number indicating how many resources are
* loading.
*
* The `ready` parameter indicates whether the current frame reflects all recent changes (camera
* movement, splat transforms, lod updates, etc.) with the latest sorting applied. The `loading`
* parameter indicates if octree LOD resources are still being loaded.
* movement, splat transforms, lod updates, etc.) with the latest sorting applied. The `loadingCount`
* parameter reports the total number of octree LOD resources currently loading or queued to load.
*
* This event is useful for video capture or other workflows that need to wait for frames
* to be fully ready. Only capture frames and move camera to next position when both
* `ready === true` and `loading === false`.
* `ready === true` and `loadingCount === 0`. Note that `loadingCount` can be used as a boolean
* in conditionals (0 is falsy, non-zero is truthy) for backward compatibility.
*
* @event
* @example
* app.systems.gsplat.on('frame:ready', (camera, layer, ready, loading) => {
* if (ready && !loading) {
* // Wait for frame to be ready before capturing
* app.systems.gsplat.on('frame:ready', (camera, layer, ready, loadingCount) => {
* if (ready && !loadingCount) {
* console.log(`Frame ready to capture for camera ${camera.entity.name}`);
* // Capture frame here
* }
* });
* @example
* // Track loading progress (0..1)
* let maxLoadingCount = 0;
* app.systems.gsplat.on('frame:ready', (camera, layer, ready, loadingCount) => {
* maxLoadingCount = Math.max(maxLoadingCount, loadingCount);
* const progress = maxLoadingCount > 0 ? (maxLoadingCount - loadingCount) / maxLoadingCount : 1;
* console.log(`Loading progress: ${(progress * 100).toFixed(1)}%`);
* });
*/
static EVENT_FRAMEREADY = 'frame:ready';

Expand Down
11 changes: 0 additions & 11 deletions src/scene/gsplat-unified/gsplat-asset-loader-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,6 @@ class GSplatAssetLoaderBase {
getResource(url) {
Debug.error('GSplatAssetLoaderBase#getResource: Not implemented');
}

/**
* Returns true if any resources are currently loading or queued to load.
*
* @type {boolean}
* @abstract
*/
get isLoading() {
Debug.error('GSplatAssetLoaderBase#isLoading: Not implemented');
return false;
}
}

export { GSplatAssetLoaderBase };
12 changes: 5 additions & 7 deletions src/scene/gsplat-unified/gsplat-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,15 +506,13 @@ class GSplatManager {
fireFrameReadyEvent() {
const ready = this.sortedVersion === this.lastWorldStateVersion;

// Check loader queue and octree instances' pending loads
let loading = this.director.assetLoader.isLoading;
if (!loading) {
for (const [, inst] of this.octreeInstances) {
loading ||= inst.hasPendingLoads;
}
// Count total pending loads from octree instances (including environment)
let loadingCount = 0;
for (const [, inst] of this.octreeInstances) {
loadingCount += inst.pendingLoadCount;
}

this.director.eventHandler.fire('frame:ready', this.cameraNode.camera, this.renderer.layer, ready, loading);
this.director.eventHandler.fire('frame:ready', this.cameraNode.camera, this.renderer.layer, ready, loadingCount);
}

update() {
Expand Down
15 changes: 11 additions & 4 deletions src/scene/gsplat-unified/gsplat-octree-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,19 @@ class GSplatOctreeInstance {
pendingVisibleAdds = new Map();

/**
* Returns true if this instance has resources pending load or prefetch.
* Returns the count of resources pending load or prefetch, including environment if loading.
*
* @type {boolean}
* @type {number}
*/
get hasPendingLoads() {
return this.pending.size > 0 || this.prefetchPending.size > 0;
get pendingLoadCount() {
let count = this.pending.size + this.prefetchPending.size;

// Add environment if it's configured but not yet loaded
if (this.octree.environmentUrl && !this.environmentPlacement) {
count++;
}

return count;
}

/**
Expand Down