playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
177 lines (174 loc) • 5.03 kB
JavaScript
import { GSplatOctreeNode } from './gsplat-octree-node.js';
import { path } from '../../core/path.js';
const _toDelete = [];
class GSplatOctree {
destroy() {
this.destroyed = true;
this.fileResources.clear();
this.cooldowns.clear();
this.assetLoader?.destroy();
this.assetLoader = null;
this.environmentResource = null;
}
_traceLodCounts() {}
_extractLeafNodes(node, leafNodes) {
if (node.lods) {
leafNodes.push({
lods: node.lods,
bound: node.bound
});
} else if (node.children) {
for (const child of node.children){
this._extractLeafNodes(child, leafNodes);
}
}
}
getFileResource(fileIndex) {
return this.fileResources.get(fileIndex);
}
incRefCount(fileIndex) {
const count = this.fileRefCounts[fileIndex] + 1;
this.fileRefCounts[fileIndex] = count;
this.cooldowns.delete(fileIndex);
}
decRefCount(fileIndex, cooldownTicks) {
const count = this.fileRefCounts[fileIndex] - 1;
this.fileRefCounts[fileIndex] = count;
if (count === 0) {
if (cooldownTicks === 0) {
this.unloadResource(fileIndex);
} else {
this.cooldowns.set(fileIndex, cooldownTicks);
}
}
}
unloadResource(fileIndex) {
if (!this.assetLoader) {
return;
}
const fullUrl = this.files[fileIndex].url;
this.assetLoader.unload(fullUrl);
if (this.fileResources.has(fileIndex)) {
this.fileResources.delete(fileIndex);
this._traceLodCounts();
}
}
updateCooldownTick(cooldownTicks) {
this.cooldownTicks = cooldownTicks;
if (this.cooldowns.size > 0) {
this.cooldowns.forEach((remaining, fileIndex)=>{
if (remaining <= 1) {
if (this.fileRefCounts[fileIndex] === 0) {
this.unloadResource(fileIndex);
}
_toDelete.push(fileIndex);
} else {
this.cooldowns.set(fileIndex, remaining - 1);
}
});
_toDelete.forEach((idx)=>this.cooldowns.delete(idx));
_toDelete.length = 0;
}
}
ensureFileResource(fileIndex) {
if (this.fileResources.has(fileIndex)) {
return;
}
const fullUrl = this.files[fileIndex].url;
const res = this.assetLoader?.getResource(fullUrl);
if (res) {
this.fileResources.set(fileIndex, res);
if (this.fileRefCounts[fileIndex] === 0) {
this.cooldowns.set(fileIndex, this.cooldownTicks);
}
this._traceLodCounts();
return;
}
this.assetLoader?.load(fullUrl);
}
incEnvironmentRefCount() {
this.environmentRefCount++;
}
decEnvironmentRefCount() {
this.environmentRefCount--;
if (this.environmentRefCount === 0) {
this.unloadEnvironmentResource();
}
}
ensureEnvironmentResource() {
if (!this.assetLoader) {
return;
}
if (!this.environmentUrl) {
return;
}
if (this.environmentResource) {
return;
}
const res = this.assetLoader.getResource(this.environmentUrl);
if (res) {
this.environmentResource = res;
if (this.environmentRefCount === 0) {
this.unloadEnvironmentResource();
}
return;
}
this.assetLoader.load(this.environmentUrl);
}
unloadEnvironmentResource() {
if (!this.assetLoader) {
return;
}
if (this.environmentResource && this.environmentUrl) {
this.assetLoader.unload(this.environmentUrl);
this.environmentResource = null;
}
}
constructor(assetFileUrl, data){
this.fileResources = new Map();
this.cooldowns = new Map();
this.environmentUrl = null;
this.environmentResource = null;
this.environmentRefCount = 0;
this.assetLoader = null;
this.destroyed = false;
this.cooldownTicks = 100;
this.lodLevels = data.lodLevels;
this.assetFileUrl = assetFileUrl;
const baseDir = path.getDirectory(assetFileUrl);
this.files = data.filenames.map((url)=>({
url: path.isRelativePath(url) ? path.join(baseDir, url) : url,
lodLevel: -1
}));
this.fileRefCounts = new Int32Array(this.files.length);
if (data.environment) {
this.environmentUrl = path.isRelativePath(data.environment) ? path.join(baseDir, data.environment) : data.environment;
}
const leafNodes = [];
this._extractLeafNodes(data.tree, leafNodes);
this.nodes = leafNodes.map((nodeData)=>{
const lods = [];
for(let i = 0; i < this.lodLevels; i++){
const lodData = nodeData.lods[i.toString()];
if (lodData) {
lods.push({
file: this.files[lodData.file].url || '',
fileIndex: lodData.file,
offset: lodData.offset || 0,
count: lodData.count || 0
});
this.files[lodData.file].lodLevel = i;
} else {
lods.push({
file: '',
fileIndex: -1,
offset: 0,
count: 0
});
}
}
return new GSplatOctreeNode(lods, nodeData.bound);
});
}
}
export { GSplatOctree };