| @@ -6,14 +6,14 @@ async function init() { | |||
| viewer.addPluginSync(Rhino3dmLoadPlugin) | |||
| // load obj + mtl | |||
| // load a 3dm file | |||
| const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/3dm/Rhino_Logo.3dm', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| // export to glb | |||
| const blob = await viewer.assetManager.exporter.exportObject(result) | |||
| const blob = await viewer.export(result) | |||
| // const blob = await viewer.exportScene(); // its possible to export the whole scene also | |||
| if (!blob || blob.ext !== 'glb') { | |||
| @@ -2,7 +2,7 @@ | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>HDR Load</title> | |||
| <title>EXR Load</title> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -17,13 +17,13 @@ async function init() { | |||
| } | |||
| const mesh = helmet.getObjectByName('node_damagedHelmet_-6514')! | |||
| // const blob = await viewer.assetManager.exporter.exportObject(helmetObject, {exportExt: 'glb'}) | |||
| // const blob = await viewer.export(helmetObject, {exportExt: 'glb'}) | |||
| // const blob = await viewer.exportScene({viewerConfig: false}) // export scene without viewer config | |||
| // const blob = await viewer.exportScene() // export scene with viewer config and default settings. | |||
| createSimpleButtons({ | |||
| ['Download Helmet Object GLB']: async() => { | |||
| const blob = await viewer.assetManager.exporter.exportObject(mesh, { | |||
| const blob = await viewer.export(mesh, { | |||
| exportExt: 'glb', | |||
| embedUrlImages: true, // embed images in glb even when url is available. | |||
| }) | |||
| @@ -34,7 +34,7 @@ async function init() { | |||
| downloadBlob(blob, 'helmet.' + blob.ext) | |||
| }, | |||
| ['Download Helmet Material']: async() => { | |||
| const blob = await viewer.assetManager.exporter.exportObject(<IMaterial>mesh.material) | |||
| const blob = await viewer.export(<IMaterial>mesh.material) | |||
| if (!blob) { | |||
| alert('Unable to export helmet material') | |||
| return | |||
| @@ -0,0 +1,34 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>HDR To EXR</title> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,44 @@ | |||
| import {_testFinish, downloadBlob, IAsset, ITexture, ThreeViewer} from 'threepipe' | |||
| const viewer = new ThreeViewer({canvas: document.getElementById('mcanvas') as HTMLCanvasElement}) | |||
| async function init() { | |||
| // import a hdr file | |||
| const dataTexture = await viewer.import<ITexture>('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| if (!dataTexture) { | |||
| console.error('Unable to import texture') | |||
| return | |||
| } | |||
| // export as exr | |||
| const blob = await viewer.export(dataTexture, {exportExt: 'exr'}) | |||
| if (!blob || blob.ext !== 'exr') { | |||
| console.error('Unable to export texture', blob) | |||
| return | |||
| } | |||
| // load the exr as environment map | |||
| const map = await viewer.setEnvironmentMap({ | |||
| path: 'file.exr', | |||
| file: blob, | |||
| } as IAsset) | |||
| if (!map) { | |||
| console.error('Unable to load exr') | |||
| return | |||
| } | |||
| viewer.scene.background = map | |||
| // add download button | |||
| const downloadButton = document.createElement('button') | |||
| downloadButton.innerText = 'Download .exr' | |||
| downloadButton.style.position = 'absolute' | |||
| downloadButton.style.bottom = '3rem' | |||
| downloadButton.style.right = '3rem' | |||
| downloadButton.style.zIndex = '10000' | |||
| downloadButton.onclick = () => downloadBlob(blob, 'file.exr') | |||
| document.body.appendChild(downloadButton) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -249,6 +249,7 @@ | |||
| <li><a href="./parallel-asset-import/">Parallel Asset Import </a></li> | |||
| <li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li> | |||
| <li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li> | |||
| <li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li> | |||
| </ul> | |||
| <h2 class="category">Tests</h2> | |||
| <ul> | |||
| @@ -13,7 +13,7 @@ async function init() { | |||
| // todo wait for images to load | |||
| // export to glb | |||
| const blob = await viewer.assetManager.exporter.exportObject(result) | |||
| const blob = await viewer.export(result) | |||
| // const blob = await viewer.exportScene(); // its possible to export the whole scene also | |||
| if (!blob || blob.ext !== 'glb') { | |||
| @@ -22,10 +22,9 @@ async function init() { | |||
| sphere.position.setX(2) | |||
| await viewer.addSceneObject(sphere) | |||
| const matBlob = await viewer.assetManager.exporter.exportObject(material) | |||
| if (!matBlob) { | |||
| return | |||
| } | |||
| const matBlob = await viewer.export(material) | |||
| if (!matBlob) return | |||
| const material2 = await viewer.assetManager.importer.importSingle<IMaterial>({file: matBlob, path: 'mat.' + matBlob.ext}) | |||
| if (!material2) { | |||
| return | |||
| @@ -39,7 +38,7 @@ async function init() { | |||
| createSimpleButtons({ | |||
| ['Download PMAT']: async() => { | |||
| const blob = await viewer.assetManager.exporter.exportObject(material) | |||
| const blob = await viewer.export(material) | |||
| if (!blob) { | |||
| alert('Unable to export material') | |||
| return | |||
| @@ -1,6 +1,5 @@ | |||
| import {BaseEvent, EventDispatcher, WebGLRenderTarget} from 'three' | |||
| import {IMaterial, IObject3D, ITexture} from '../core' | |||
| import {AnyOptions} from 'ts-browser-helpers' | |||
| import {BlobExt, ExportFileOptions, IAssetExporter, IExporter, IExportParser} from './IExporter' | |||
| import {EXRExporter2, SimpleJSONExporter, SimpleTextExporter} from './export' | |||
| import {IRenderTarget} from '../rendering' | |||
| @@ -111,7 +110,7 @@ export class AssetExporter extends EventDispatcher<BaseEvent, 'exporterCreate' | | |||
| return this._cachedParsers.find(e => e.ext.includes(ext))?.parser ?? this._createParser(ext) | |||
| } | |||
| public async processBeforeExport(obj: IObject3D|IMaterial|ITexture|IRenderTarget, _: AnyOptions = {}): Promise<{obj:any, ext:string, typeExt?:string, blob?: BlobExt}|undefined> { | |||
| 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) { | |||
| @@ -124,7 +123,7 @@ export class AssetExporter extends EventDispatcher<BaseEvent, 'exporterCreate' | | |||
| case 'material': | |||
| return {obj: (obj as IMaterial).toJSON(), ext: (obj as IMaterial).constructor?.TypeSlug || 'json', typeExt: 'json'} | |||
| case 'texture': | |||
| return {obj: (obj as ITexture).toJSON(), ext: 'json'} | |||
| 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'} | |||
| @@ -1,13 +1,17 @@ | |||
| import {WebGLRenderTarget} from 'three' | |||
| import {DataTexture, WebGLRenderTarget} from 'three' | |||
| import {EXRExporter, EXRExporterParseOptions} from 'three/examples/jsm/exporters/EXRExporter.js' | |||
| import {IExportParser} from '../IExporter' | |||
| import {IRenderTarget} from '../../rendering' | |||
| export class EXRExporter2 extends EXRExporter implements IExportParser { | |||
| async parseAsync(obj: IRenderTarget, options: EXRExporterParseOptions): Promise<Blob> { | |||
| if (!obj.renderManager) throw new Error('No renderManager on renderTarget') | |||
| if (obj.isWebGLMultipleRenderTargets) throw new Error('WebGLMultipleRenderTargets not supported') | |||
| const res = this.parse(obj.renderManager.webglRenderer, obj as any as WebGLRenderTarget, options) | |||
| async parseAsync(obj: IRenderTarget|DataTexture, options: EXRExporterParseOptions): Promise<Blob> { | |||
| const target = <IRenderTarget>obj | |||
| if (target.isWebGLRenderTarget && !target.renderManager) throw new Error('No renderManager on renderTarget') | |||
| if (!target.isWebGLRenderTarget && !(<DataTexture>obj).isDataTexture) throw new Error('Invalid object type') | |||
| if (target.isWebGLMultipleRenderTargets) throw new Error('WebGLMultipleRenderTargets not supported') | |||
| const res = target.isWebGLRenderTarget ? | |||
| this.parse(target.renderManager!.webglRenderer, <WebGLRenderTarget>target, options) : | |||
| this.parse(undefined, <DataTexture>obj, options) | |||
| return new Blob([res], {type: 'image/x-exr'}) | |||
| } | |||
| } | |||
| @@ -14,7 +14,7 @@ import { | |||
| Vector4, | |||
| } from 'three' | |||
| import type {AssetImporter, AssetManager, MaterialManager} from '../assetmanager' | |||
| import {IAssetImporter} from '../assetmanager' | |||
| import {BlobExt, IAssetImporter} from '../assetmanager' | |||
| import {ThreeViewer} from '../viewer' | |||
| import {ITexture} from '../core' | |||
| import {IRenderTarget, RenderManager} from '../rendering' | |||
| @@ -771,3 +771,9 @@ export function metaFromResources(resources?: Partial<SerializationResourcesType | |||
| }, // clear context even if its present in resources | |||
| } | |||
| } | |||
| export function jsonToBlob(json: any): BlobExt { | |||
| const b = new Blob([JSON.stringify(json)], {type: 'application/json'}) as BlobExt | |||
| b.ext = 'json' | |||
| return b | |||
| } | |||
| @@ -4,6 +4,7 @@ import {TViewerScreenShader} from '../postprocessing' | |||
| import { | |||
| AddObjectOptions, | |||
| IAnimationLoopEvent, | |||
| IMaterial, | |||
| IObject3D, | |||
| IObjectProcessor, | |||
| ITexture, | |||
| @@ -14,6 +15,7 @@ import {ViewerRenderManager} from './ViewerRenderManager' | |||
| import { | |||
| convertArrayBufferToStringsInMeta, | |||
| getEmptyMeta, | |||
| jsonToBlob, | |||
| metaFromResources, | |||
| MetaImporter, | |||
| metaToResources, | |||
| @@ -36,6 +38,7 @@ import {GLStatsJS, IDialogWrapper, windowDialogWrapper} from '../utils' | |||
| import {IViewerPlugin, IViewerPluginSync} from './IViewerPlugin' | |||
| import {DropzonePlugin, DropzonePluginOptions} from '../plugins/interaction/DropzonePlugin' | |||
| import {uiConfig, uiFolderContainer, UiObjectConfig} from 'uiconfig.js' | |||
| import {IRenderTarget} from '../rendering' | |||
| export type IViewerEvent = BaseEvent & { | |||
| type: 'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled' | |||
| @@ -372,6 +375,30 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return await this.assetManager.addAssetSingle<T>(obj, options) | |||
| } | |||
| /** | |||
| * Imports an object/model/material/texture/viewer-config/plugin-preset/... to the viewer scene from url or an {@link IAsset} object. | |||
| * Same as {@link AssetImporter.importSingle} | |||
| * @param obj | |||
| * @param options | |||
| */ | |||
| async import<T extends ImportResult = ImportResult>(obj: string | IAsset | null, options?: ImportAddOptions) { | |||
| if (!obj) return | |||
| return await this.assetManager.importer.importSingle<T>(obj, options) | |||
| } | |||
| /** | |||
| * Exports an object/mesh/material/texture/render-target/plugin-preset/viewer to a blob. | |||
| * If no object is given, a glb is exported with the current viewer state. | |||
| * @param obj | |||
| * @param options | |||
| */ | |||
| async export(obj?: IObject3D|IMaterial|ITexture|IRenderTarget|IViewerPlugin|(typeof this), options?: ExportFileOptions) { | |||
| if (!obj) obj = this._scene // this will export the glb with the scene and viewer config | |||
| if ((<typeof this>obj).type === this.type) return jsonToBlob((<typeof this>obj).exportConfig()) | |||
| if ((<IViewerPlugin>obj).constructor?.PluginType) return jsonToBlob(this.exportPluginConfig(<IViewerPlugin>obj)) | |||
| return await this.assetManager.exporter.exportObject(<IObject3D|IMaterial|ITexture|IRenderTarget>obj, options) | |||
| } | |||
| /** | |||
| * Set the environment map of the scene from url or an {@link IAsset} object. | |||
| * @param map | |||