| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import {BaseEvent, EventDispatcher, WebGLRenderTarget} from 'three'
- import {IMaterial, IObject3D, ITexture} from '../core'
- import {BlobExt, ExportFileOptions, IAssetExporter, IExporter, IExportParser} from './IExporter'
- import {EXRExporter2, SimpleJSONExporter, SimpleTextExporter} from './export'
- import {IRenderTarget} from '../rendering'
-
- /**
- * 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<BaseEvent, 'exporterCreate' | 'exportFile'> implements IAssetExporter {
- readonly exporters: IExporter[] = [
- {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']},
- ]
-
- addExporter(...exporters: IExporter[]) {
- for (const exporter of exporters) {
- if (this.exporters.includes(exporter)) {
- console.warn('Exporter already added', exporter)
- return
- }
- this.exporters.push(exporter)
- }
- }
- removeExporter(...exporters: IExporter[]) {
- for (const exporter of exporters) {
- const i = this.exporters.indexOf(exporter)
- if (i >= 0) this.exporters.splice(i, 1)
- }
- }
-
- getExporter(...ext: string[]): IExporter|undefined {
- return this.exporters.find(e=>e.ext.some(e1=>ext.includes(e1)))
- }
-
- constructor() {
- super()
- }
-
- public async exportObject(obj?: IObject3D|IMaterial|ITexture|IRenderTarget, options: ExportFileOptions = {}): Promise<BlobExt|undefined> {
- if (!obj?.assetType) {
- console.error('Object has no asset type')
- return undefined
- }
- const excluded: IObject3D[] = []
- 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: any)=>o.visible = true)
- }
- if ((obj as any)?.userData?.rootSceneModelRoot && options.viewerConfig === false) {
- delete (obj as any)!.userData!.__exportViewerConfig
- }
- return blob
- }
-
- // export to blob
- private async _exportFile(obj: IObject3D|IMaterial|ITexture|IRenderTarget, options: ExportFileOptions = {}): Promise<BlobExt|undefined> {
- // if ((file as any)?.__imported) return (file as any).__imported // todo: cache exports?
-
- let res: BlobExt
- 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}) as BlobExt
- 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
- }
-
- private _createParser(ext: string): IExportParser {
- 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
- }
- private _cachedParsers: {parser: IExportParser, ext: string[]}[] = []
- private _getParser(ext: string): IExportParser {
- return this._cachedParsers.find(e => e.ext.includes(ext))?.parser ?? this._createParser(ext)
- }
-
- public async processBeforeExport(obj: IObject3D|IMaterial|ITexture|IRenderTarget, options: ExportFileOptions = {}): Promise<{obj:any, ext:string, typeExt?:string, blob?: BlobExt}|undefined> {
- // 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 as IMaterial).toJSON(), ext: (obj as IMaterial).constructor?.TypeSlug || 'json', typeExt: 'json'}
- case 'texture':
- return options.exportExt ? {obj, ext: options.exportExt} : {obj: (obj as ITexture).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 as WebGLRenderTarget,
- (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(): void {
- // todo
- }
-
-
- }
|