threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
145 lines • 6.01 kB
JavaScript
import { EventDispatcher } from 'three';
import { EXRExporter2, SimpleJSONExporter, SimpleTextExporter } from './export';
/**
* Asset Exporter
*
* Utility class to export objects, materials, textures, render targets, etc.
* Used in {@link AssetManager} to export assets.
* @category Asset Manager
*/
export class AssetExporter extends EventDispatcher {
addExporter(...exporters) {
for (const exporter of exporters) {
if (this.exporters.includes(exporter)) {
console.warn('Exporter already added', exporter);
return;
}
this.exporters.push(exporter);
}
}
removeExporter(...exporters) {
for (const exporter of exporters) {
const i = this.exporters.indexOf(exporter);
if (i >= 0)
this.exporters.splice(i, 1);
}
}
getExporter(...ext) {
return this.exporters.find(e => e.ext.some(e1 => ext.includes(e1)));
}
constructor() {
super();
this.exporters = [
{ ctor: () => new SimpleJSONExporter(), ext: ['json'] },
{ ctor: () => new SimpleTextExporter(), ext: ['txt', 'text'] },
{ ctor: () => new EXRExporter2(), ext: ['exr'] },
// {ctor: ()=>new EXRExporter2(), ext: ['png', 'jpeg', 'webp']}, // todo
// {ctor: ()=>new GLTFDracoExporter(), ext: ['gltf', 'glb']},
];
this._cachedParsers = [];
}
async exportObject(obj, options = {}) {
if (!obj?.assetType) {
console.error('Object has no asset type');
return undefined;
}
const excluded = [];
if (obj.assetType === 'model') {
obj.traverse((o) => {
if (o.userData.excludeFromExport && o.visible) {
o.visible = false;
excluded.push(o);
}
});
}
const blob = await this._exportFile(obj, options);
if (obj.assetType === 'model') {
excluded.forEach((o) => o.visible = true);
}
if (obj?.userData?.rootSceneModelRoot && options.viewerConfig === false) {
delete obj.userData.__exportViewerConfig;
}
return blob;
}
// export to blob
async _exportFile(obj, options = {}) {
// if ((file as any)?.__imported) return (file as any).__imported // todo: cache exports?
let res;
try {
this.dispatchEvent({ type: 'exportFile', obj, state: 'processing', exportOptions: options });
const processed = await this.processBeforeExport(obj, options);
const ext = options.exportExt || processed?.typeExt || processed?.ext;
if (!processed || !ext) {
console.error(processed, options, obj);
throw new Error(`Unable to preprocess before export ${ext}`);
}
if (processed.blob)
res = processed.blob;
else {
const parser = this._getParser(ext);
this.dispatchEvent({ type: 'exportFile', obj, state: 'exporting' });
res = await parser.parseAsync(processed.obj, { exportExt: processed.ext ?? ext, ...options });
res.ext = processed.ext;
}
this.dispatchEvent({ type: 'exportFile', obj, state: 'done' });
}
catch (e) {
console.error('AssetExporter: Unable to Export file', obj);
// console.error(e)
this.dispatchEvent({ type: 'exportFile', obj, state: 'error', error: e });
throw e;
return undefined;
}
// if (file) (file as any).__imported = res
return res;
}
_createParser(ext) {
const exporter = this.exporters.find(e => e.ext.includes(ext));
if (!exporter)
throw new Error(`No exporter found for extension ${ext}`);
const parser = exporter?.ctor(this, exporter);
if (!parser)
throw new Error(`Unable to create parser for extension ${ext}`);
this._cachedParsers.push({ ext: exporter.ext, parser });
this.dispatchEvent({ type: 'exporterCreate', exporter, parser });
return parser;
}
_getParser(ext) {
return this._cachedParsers.find(e => e.ext.includes(ext))?.parser ?? this._createParser(ext);
}
async processBeforeExport(obj, options = {}) {
// if (obj.assetExporterProcessed && !options.forceExporterReprocess) return obj //todo;;;
switch (obj.assetType) {
case 'light':
console.error('AssetExporter: light export not implemented');
return undefined;
case 'model':
return { obj, ext: 'glb' };
// return {obj, ext: 'gltf'}
case 'material':
return { obj: obj.toJSON(), ext: obj.constructor?.TypeSlug || 'json', typeExt: 'json' };
case 'texture':
return options.exportExt ? { obj, ext: options.exportExt } : { obj: obj.toJSON(), ext: 'json' };
case 'renderTarget':
if (obj.isWebGLMultipleRenderTargets)
console.error('AssetExporter: WebGLMultipleRenderTargets export not supported');
else if (!obj.renderManager)
return { obj, ext: 'exr' };
else {
const blob = obj.renderManager.exportRenderTarget(obj, (options.exportExt || '' !== '') && options.exportExt !== 'auto' ?
options.exportExt === 'exr' ? 'image/x-exr' : 'image/' + options.exportExt : 'auto');
return {
obj, ext: blob.ext, blob,
};
}
break;
default:
console.error('AssetExporter: unknown asset type', obj.assetType);
}
return undefined;
}
dispose() {
// todo
}
}
//# sourceMappingURL=AssetExporter.js.map