playcanvas
Version:
PlayCanvas WebGL game engine
199 lines (196 loc) • 5.55 kB
JavaScript
import { Asset } from '../asset/asset.js';
import { Http, http } from '../../platform/net/http.js';
import { GSplatResource } from '../../scene/gsplat/gsplat-resource.js';
import { GSplatSogsData } from '../../scene/gsplat/gsplat-sogs-data.js';
import { GSplatSogsResource } from '../../scene/gsplat/gsplat-sogs-resource.js';
const combineProgress = (target, assets)=>{
const map = new Map();
const fire = ()=>{
let loaded = 0;
let total = 0;
map.forEach((value)=>{
loaded += value.loaded;
total += value.total;
});
target.fire('progress', loaded, total);
};
assets.forEach((asset)=>{
const progress = (loaded, total)=>{
map.set(asset, {
loaded,
total
});
fire();
};
const done = ()=>{
asset.off('progress', progress);
asset.off('load', done);
asset.off('error', done);
};
asset.on('progress', progress);
asset.on('load', done);
asset.on('error', done);
});
};
const upgradeMeta = (meta)=>{
const result = {
version: 1,
count: meta.means.shape[0],
means: {
mins: meta.means.mins,
maxs: meta.means.maxs,
files: meta.means.files
},
scales: {
mins: meta.scales.mins,
maxs: meta.scales.maxs,
files: meta.scales.files
},
quats: {
files: meta.quats.files
},
sh0: {
mins: meta.sh0.mins,
maxs: meta.sh0.maxs,
files: meta.sh0.files
}
};
if (meta.shN) {
result.shN = {
mins: meta.shN.mins,
maxs: meta.shN.maxs,
files: meta.shN.files
};
}
return result;
};
class SogsParser {
constructor(app, maxRetries){
this.app = app;
this.maxRetries = maxRetries;
}
_shouldAbort(asset, unloaded) {
if (unloaded || !this.app.assets.get(asset.id)) return true;
if (!this.app?.graphicsDevice || this.app.graphicsDevice._destroyed) return true;
return false;
}
async loadTextures(url, callback, asset, meta) {
if (meta.version !== 2) {
meta = upgradeMeta(meta);
}
const { assets } = this.app;
const subs = [
'means',
'quats',
'scales',
'sh0',
'shN'
];
const textures = {};
const promises = [];
subs.forEach((sub)=>{
const files = meta[sub]?.files ?? [];
textures[sub] = files.map((filename)=>{
const texture = new Asset(filename, 'texture', {
url: asset.options?.mapUrl?.(filename) ?? new URL(filename, new URL(url.load, window.location.href).toString()).toString(),
filename
}, {
mipmaps: false
}, {
crossOrigin: 'anonymous'
});
const promise = new Promise((resolve, reject)=>{
texture.on('load', ()=>resolve(null));
texture.on('error', (err)=>reject(err));
});
assets.add(texture);
promises.push(promise);
return texture;
});
});
const textureAssets = subs.map((sub)=>textures[sub]).flat();
let unloaded = false;
asset.once('unload', ()=>{
unloaded = true;
textureAssets.forEach((t)=>{
assets.remove(t);
t.unload();
});
});
combineProgress(asset, textureAssets);
textureAssets.forEach((t)=>assets.load(t));
await Promise.allSettled(promises);
if (this._shouldAbort(asset, unloaded)) {
textureAssets.forEach((t)=>{
assets.remove(t);
t.unload();
});
callback(null, null);
return;
}
const data = new GSplatSogsData();
data.url = url.original;
data.meta = meta;
data.numSplats = meta.count;
data.means_l = textures.means[0].resource;
data.means_u = textures.means[1].resource;
data.quats = textures.quats[0].resource;
data.scales = textures.scales[0].resource;
data.sh0 = textures.sh0[0].resource;
data.sh_centroids = textures.shN?.[0]?.resource;
data.sh_labels = textures.shN?.[1]?.resource;
data.shBands = GSplatSogsData.calcBands(data.sh_centroids?.width);
const decompress = asset.data?.decompress;
const minimalMemory = asset.options?.minimalMemory ?? false;
data.minimalMemory = minimalMemory;
if (!decompress) {
if (this._shouldAbort(asset, unloaded)) {
data.destroy();
callback(null, null);
return;
}
await data.prepareGpuData();
}
if (this._shouldAbort(asset, unloaded)) {
data.destroy();
callback(null, null);
return;
}
const resource = decompress ? new GSplatResource(this.app.graphicsDevice, await data.decompress()) : new GSplatSogsResource(this.app.graphicsDevice, data);
if (this._shouldAbort(asset, unloaded)) {
resource.destroy();
callback(null, null);
return;
}
callback(null, resource);
}
load(url, callback, asset) {
if (asset.data?.means) {
this.loadTextures(url, callback, asset, asset.data);
} else {
if (typeof url === 'string') {
url = {
load: url,
original: url
};
}
const options = {
retry: this.maxRetries > 0,
maxRetries: this.maxRetries,
responseType: Http.ResponseType.JSON
};
http.get(url.load, options, (err, meta)=>{
if (this._shouldAbort(asset, false)) {
callback(null, null);
return;
}
if (!err) {
this.loadTextures(url, callback, asset, meta);
} else {
callback(`Error loading gsplat meta: ${url.original} [${err}]`);
}
});
}
}
}
export { SogsParser };