playcanvas
Version:
PlayCanvas WebGL game engine
112 lines (109 loc) • 3.59 kB
JavaScript
import { Debug } from '../../core/debug.js';
/**
* @import { GSplatInfo } from './gsplat-info.js'
* @import { GSplatOctree } from './gsplat-octree.js'
*/ class GSplatWorldState {
constructor(device, version, splats){
/**
* The version of the world state.
*
* @type {number}
*/ this.version = 0;
/**
* Whether the sort parameters have been set on the sorter.
*
* @type {boolean}
*/ this.sortParametersSet = false;
/**
* Whether the world state has been sorted before.
*
* @type {boolean}
*/ this.sortedBefore = false;
/**
* An array of all splats managed by this world state.
*
* @type {GSplatInfo[]}
*/ this.splats = [];
/**
* The texture size of work buffer.
*
* @type {number}
*/ this.textureSize = 0;
/**
* Total number of pixels actually used in the texture (excluding unused regions).
* This is the count that should be sent to the sort worker and renderer.
*
* @type {number}
*/ this.totalUsedPixels = 0;
/**
* Files to decrement when this state becomes active.
* Array of tuples: [octree, fileIndex]
* @type {Array<[GSplatOctree, number]>}
*/ this.pendingReleases = [];
this.version = version;
this.splats = splats;
this.estimateTextureSize(this.splats, device.maxTextureSize);
this.assignLines(this.splats, this.textureSize);
}
/**
* Estimates the square texture size that can store all splats, using binary search to find the
* smallest size that fits.
*
* @param {GSplatInfo[]} splats - The splats to allocate space for.
* @param {number} maxSize - Max texture size (width and height).
* @returns {boolean} - True if the texture size was found.
*/ estimateTextureSize(splats, maxSize) {
const fits = (size)=>{
let rows = 0;
for (const splat of splats){
rows += Math.ceil(splat.activeSplats / size);
if (rows > size) return false;
}
return true;
};
let low = 1;
let high = maxSize;
let bestSize = null;
while(low <= high){
const mid = Math.floor((low + high) / 2);
if (fits(mid)) {
bestSize = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
if (bestSize === null) {
this.textureSize = 0;
Debug.error('estimateTextureSize: failed to find a valid texture size');
return false;
}
this.textureSize = bestSize;
return true;
}
destroy() {
this.splats.forEach((splat)=>splat.destroy());
this.splats.length = 0;
}
/**
* Assigns lines to each splat based on the texture size.
*
* @param {GSplatInfo[]} splats - The splats to assign lines to.
* @param {number} size - The texture size.
*/ assignLines(splats, size) {
if (splats.length === 0) {
this.totalUsedPixels = 0;
return;
}
let start = 0;
for (const splat of splats){
const activeSplats = splat.activeSplats;
const numLines = Math.ceil(activeSplats / size);
splat.setLines(start, numLines, size, activeSplats);
start += numLines;
}
// Calculate total used pixels (lines used × texture width)
this.totalUsedPixels = start * size;
}
}
export { GSplatWorldState };