Skip to content
Open
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
1 change: 0 additions & 1 deletion dist/assets/oldWorker-Ct-p1xj7.js.map

This file was deleted.

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/assets/worker-C17a9L0B.js.map

This file was deleted.

Large diffs are not rendered by default.

222 changes: 158 additions & 64 deletions dist/spark.cjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/spark.cjs.js.map

Large diffs are not rendered by default.

222 changes: 158 additions & 64 deletions dist/spark.module.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/spark.module.js.map

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions dist/types/ILodScheduler.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface ILodScheduler {
schedule(task: () => Promise<void>): Promise<boolean>;
dispose(): void;
}
/**
* Default scheduler that preserves Spark's historical LOD behavior:
* if a traversal job is already in flight, newer requests are skipped.
*/
export declare class DropIfBusyLodScheduler implements ILodScheduler {
private running;
schedule(task: () => Promise<void>): Promise<boolean>;
dispose(): void;
}
39 changes: 39 additions & 0 deletions dist/types/ILodTraverser.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export interface LodInstance {
instanceId: string;
lodId: number;
rootPage?: number;
viewToObjectCols: number[];
lodScale: number;
behindFoveate: number;
coneFov0: number;
coneFov: number;
coneFoveate: number;
}
export interface LodTraverseResult {
keyIndices: Record<string, {
lodId: number;
numSplats: number;
indices: Uint32Array;
}>;
chunks: [number, number][];
pixelLimit?: number;
}
export interface ILodTraverser {
init(): Promise<boolean>;
newTree(capacity: number): Promise<number>;
uploadTree(lodId: number, treeData: Uint32Array, numSplats: number): Promise<number>;
newSharedTree(lodId: number): Promise<number>;
updateTrees(ranges: {
lodId: number;
pageBase: number;
chunkBase: number;
count: number;
lodTreeData?: Uint32Array;
}[]): Promise<void>;
traverse(instances: Record<string, LodInstance>, maxSplats: number, pixelScaleLimit: number, lastPixelLimit?: number): Promise<LodTraverseResult>;
removeTree(lodId: number): Promise<void>;
getLodTreeLevel(lodId: number, level: number): Promise<{
indices: Uint32Array;
}>;
dispose(): void;
}
9 changes: 9 additions & 0 deletions dist/types/ISortProvider.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface ISortProvider {
init(): Promise<boolean>;
preallocate?(maxSplats: number): void;
sort(readback: Uint32Array, numSplats: number, ordering: Uint32Array): Promise<{
ordering: Uint32Array;
activeSplats: number;
}>;
dispose(): void;
}
19 changes: 15 additions & 4 deletions dist/types/SparkRenderer.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ExtSplats, PackedSplats, PagedSplats, SplatMesh, SplatPager } from '.';
import { ILodScheduler } from './ILodScheduler';
import { ILodTraverser } from './ILodTraverser';
import { ISortProvider } from './ISortProvider';
import { SplatAccumulator } from './SplatAccumulator';
import { SplatWorker } from './SplatWorker';
import * as THREE from "three";
export interface SparkRendererOptions {
/**
Expand Down Expand Up @@ -199,6 +201,15 @@ export interface SparkRendererOptions {
behindFoveate?: number;
lodRaycast?: number;
lodRaycastIntervalMs?: number;
/** Injected LOD traverser implementation. When provided, SparkRenderer
* uses this for LOD tree traversal instead of the built-in WASM worker. */
lodTraverser?: ILodTraverser;
/** Injected LOD scheduler implementation. When provided, SparkRenderer
* uses this to decide how overlapping LOD update requests are handled. */
lodScheduler?: ILodScheduler;
/** Injected sort provider implementation. When provided, SparkRenderer
* uses this for depth sorting instead of the built-in WASM worker. */
sortProvider?: ISortProvider;
target?: {
/**
* Width of the render target in pixels.
Expand Down Expand Up @@ -271,7 +282,6 @@ export declare class SparkRenderer extends THREE.Mesh {
sorting: boolean;
sortDirty: boolean;
lastSortTime: number;
sortWorker: SplatWorker | null;
sortTimeoutId: number;
sortedCenter: THREE.Vector3;
sortedDir: THREE.Vector3;
Expand All @@ -292,7 +302,9 @@ export declare class SparkRenderer extends THREE.Mesh {
lodRaycast?: number;
lodRaycastIntervalMs: number;
lastLodRaycastTime: number;
lodWorker: SplatWorker | null;
lodTraverser: ILodTraverser;
lodScheduler: ILodScheduler;
sortProvider: ISortProvider;
lodMeshes: {
mesh: SplatMesh;
version: number;
Expand Down Expand Up @@ -450,7 +462,6 @@ export declare class SparkRenderer extends THREE.Mesh {
}): Promise<void>;
private updateInternal;
private driveSort;
private ensureLodWorker;
defaultSplatTarget(): 500000 | 750000 | 1000000 | 1500000 | 2500000;
private driveLod;
private initLodTree;
Expand Down
25 changes: 25 additions & 0 deletions dist/types/WorkerLodTraverser.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ILodTraverser, LodInstance, LodTraverseResult } from './ILodTraverser';
/**
* Default LOD traverser that delegates to the WASM worker.
*/
export declare class WorkerLodTraverser implements ILodTraverser {
private worker;
private ensureWorker;
init(): Promise<boolean>;
newTree(capacity: number): Promise<number>;
uploadTree(_lodId: number, lodTree: Uint32Array, numSplats: number): Promise<number>;
newSharedTree(lodId: number): Promise<number>;
updateTrees(ranges: {
lodId: number;
pageBase: number;
chunkBase: number;
count: number;
lodTreeData?: Uint32Array;
}[]): Promise<void>;
traverse(instances: Record<string, LodInstance>, maxSplats: number, pixelScaleLimit: number, lastPixelLimit?: number): Promise<LodTraverseResult>;
removeTree(lodId: number): Promise<void>;
getLodTreeLevel(lodId: number, level: number): Promise<{
indices: Uint32Array;
}>;
dispose(): void;
}
14 changes: 14 additions & 0 deletions dist/types/WorkerSortProvider.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ISortProvider } from './ISortProvider';
/**
* Default sort provider that delegates to the WASM worker.
*/
export declare class WorkerSortProvider implements ISortProvider {
private worker;
private ensureWorker;
init(): Promise<boolean>;
sort(readback: Uint32Array, numSplats: number, ordering: Uint32Array): Promise<{
ordering: Uint32Array;
activeSplats: number;
}>;
dispose(): void;
}
5 changes: 5 additions & 0 deletions dist/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export { OldSparkRenderer, type OldSparkRendererOptions, } from './OldSparkRenderer';
export { OldSparkViewpoint, type OldSparkViewpointOptions, } from './OldSparkViewpoint';
export { SparkRenderer, type SparkRendererOptions, } from './SparkRenderer';
export type { ILodTraverser, LodInstance, LodTraverseResult, } from './ILodTraverser';
export { DropIfBusyLodScheduler, type ILodScheduler, } from './ILodScheduler';
export { WorkerLodTraverser } from './WorkerLodTraverser';
export type { ISortProvider } from './ISortProvider';
export { WorkerSortProvider } from './WorkerSortProvider';
export { SplatAccumulator, type GeneratorMapping } from './SplatAccumulator';
export * as dyno from './dyno';
export { RgbaArray, readRgbaArray } from './RgbaArray';
Expand Down
30 changes: 30 additions & 0 deletions src/ILodScheduler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export interface ILodScheduler {
schedule(task: () => Promise<void>): Promise<boolean>;
dispose(): void;
}

/**
* Default scheduler that preserves Spark's historical LOD behavior:
* if a traversal job is already in flight, newer requests are skipped.
*/
export class DropIfBusyLodScheduler implements ILodScheduler {
private running = false;

async schedule(task: () => Promise<void>): Promise<boolean> {
if (this.running) {
return false;
}

this.running = true;
try {
await task();
return true;
} finally {
this.running = false;
}
}

dispose(): void {
this.running = false;
}
}
52 changes: 52 additions & 0 deletions src/ILodTraverser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export interface LodInstance {
instanceId: string;
lodId: number;
rootPage?: number;
viewToObjectCols: number[];
lodScale: number;
behindFoveate: number;
coneFov0: number;
coneFov: number;
coneFoveate: number;
}

export interface LodTraverseResult {
keyIndices: Record<
string,
{ lodId: number; numSplats: number; indices: Uint32Array }
>;
chunks: [number, number][];
pixelLimit?: number;
}

export interface ILodTraverser {
init(): Promise<boolean>;
newTree(capacity: number): Promise<number>;
uploadTree(
lodId: number,
treeData: Uint32Array,
numSplats: number,
): Promise<number>;
newSharedTree(lodId: number): Promise<number>;
updateTrees(
ranges: {
lodId: number;
pageBase: number;
chunkBase: number;
count: number;
lodTreeData?: Uint32Array;
}[],
): Promise<void>;
traverse(
instances: Record<string, LodInstance>,
maxSplats: number,
pixelScaleLimit: number,
lastPixelLimit?: number,
): Promise<LodTraverseResult>;
removeTree(lodId: number): Promise<void>;
getLodTreeLevel(
lodId: number,
level: number,
): Promise<{ indices: Uint32Array }>;
dispose(): void;
}
10 changes: 10 additions & 0 deletions src/ISortProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface ISortProvider {
init(): Promise<boolean>;
preallocate?(maxSplats: number): void;
sort(
readback: Uint32Array,
numSplats: number,
ordering: Uint32Array,
): Promise<{ ordering: Uint32Array; activeSplats: number }>;
dispose(): void;
}
Loading