| converting = true | converting = true | ||||
| // export to glb | // export to glb | ||||
| const blob = await viewer.export(result, { | |||||
| fbxBlob = await assimp.exportModel('fbx', result, { | |||||
| embedUrlImages: true, | embedUrlImages: true, | ||||
| }) | }) | ||||
| // const blob = await viewer.exportScene(); // its possible to export the whole scene also | // const blob = await viewer.exportScene(); // its possible to export the whole scene also | ||||
| if (!blob || blob.ext !== 'glb') { | |||||
| alert('Unable to export scene') | |||||
| converting = false | |||||
| return | |||||
| } | |||||
| fbxBlob = assimp.convertFiles({['file.glb']: await blob.arrayBuffer()}, 'fbx') | |||||
| if (!fbxBlob) { | if (!fbxBlob) { | ||||
| alert('Failed to convert glb to fbx') | alert('Failed to convert glb to fbx') | ||||
| converting = false | converting = false |
| EnvironmentControlsPlugin, | EnvironmentControlsPlugin, | ||||
| GlobeControlsPlugin, | GlobeControlsPlugin, | ||||
| } from '@threepipe/plugin-3d-tiles-renderer' | } from '@threepipe/plugin-3d-tiles-renderer' | ||||
| import {AssimpJsPlugin} from '@threepipe/plugin-assimpjs' | |||||
| // @ts-expect-error todo fix import | // @ts-expect-error todo fix import | ||||
| import {BloomPlugin, DepthOfFieldPlugin, SSContactShadowsPlugin, SSReflectionPlugin, TemporalAAPlugin, VelocityBufferPlugin, OutlinePlugin, SSGIPlugin, AnisotropyPlugin} from '@threepipe/webgi-plugins' | import {BloomPlugin, DepthOfFieldPlugin, SSContactShadowsPlugin, SSReflectionPlugin, TemporalAAPlugin, VelocityBufferPlugin, OutlinePlugin, SSGIPlugin, AnisotropyPlugin} from '@threepipe/webgi-plugins' | ||||
| EnvironmentControlsPlugin, GlobeControlsPlugin, | EnvironmentControlsPlugin, GlobeControlsPlugin, | ||||
| B3DMLoadPlugin, I3DMLoadPlugin, PNTSLoadPlugin, CMPTLoadPlugin, | B3DMLoadPlugin, I3DMLoadPlugin, PNTSLoadPlugin, CMPTLoadPlugin, | ||||
| TilesRendererPlugin, DeepZoomImageLoadPlugin, /* SlippyMapTilesLoadPlugin,*/ | TilesRendererPlugin, DeepZoomImageLoadPlugin, /* SlippyMapTilesLoadPlugin,*/ | ||||
| new AssimpJsPlugin(false), | |||||
| ] | ] | ||||
| editorModes: Record<string, Class<IViewerPlugin<any>>[]> = { | editorModes: Record<string, Class<IViewerPlugin<any>>[]> = { | ||||
| ['Interaction']: [HierarchyUiPlugin, TransformControlsPlugin, PickingPlugin, OutlinePlugin, Object3DGeneratorPlugin, GeometryGeneratorPlugin, EditorViewWidgetPlugin, Object3DWidgetsPlugin, MeshOptSimplifyModifierPlugin], | ['Interaction']: [HierarchyUiPlugin, TransformControlsPlugin, PickingPlugin, OutlinePlugin, Object3DGeneratorPlugin, GeometryGeneratorPlugin, EditorViewWidgetPlugin, Object3DWidgetsPlugin, MeshOptSimplifyModifierPlugin], | ||||
| ['GBuffer']: [GBufferPlugin, DepthBufferPlugin, NormalBufferPlugin], | ['GBuffer']: [GBufferPlugin, DepthBufferPlugin, NormalBufferPlugin], | ||||
| ['Post-processing']: [TonemapPlugin, ProgressivePlugin, SSAOPlugin, SSReflectionPlugin, BloomPlugin, DepthOfFieldPlugin, SSGIPlugin, FrameFadePlugin, VignettePlugin, ChromaticAberrationPlugin, FilmicGrainPlugin, TemporalAAPlugin, VelocityBufferPlugin, SSContactShadowsPlugin], | ['Post-processing']: [TonemapPlugin, ProgressivePlugin, SSAOPlugin, SSReflectionPlugin, BloomPlugin, DepthOfFieldPlugin, SSGIPlugin, FrameFadePlugin, VignettePlugin, ChromaticAberrationPlugin, FilmicGrainPlugin, TemporalAAPlugin, VelocityBufferPlugin, SSContactShadowsPlugin], | ||||
| ['Export']: [AssetExporterPlugin, CanvasSnapshotPlugin, AWSClientPlugin, TransfrSharePlugin], | |||||
| ['Export']: [AssetExporterPlugin, CanvasSnapshotPlugin, AWSClientPlugin, TransfrSharePlugin, AssimpJsPlugin], | |||||
| ['Configurator']: [MaterialConfiguratorPlugin, SwitchNodePlugin, GLTFKHRMaterialVariantsPlugin], | ['Configurator']: [MaterialConfiguratorPlugin, SwitchNodePlugin, GLTFKHRMaterialVariantsPlugin], | ||||
| ['Animation']: [GLTFAnimationPlugin, CameraViewPlugin], | ['Animation']: [GLTFAnimationPlugin, CameraViewPlugin], | ||||
| ['Extras']: [HDRiGroundPlugin, Rhino3dmLoadPlugin, ClearcoatTintPlugin, FragmentClippingExtensionPlugin, NoiseBumpMaterialPlugin, AnisotropyPlugin, CustomBumpMapPlugin, VirtualCamerasPlugin, TilesRendererPlugin], | ['Extras']: [HDRiGroundPlugin, Rhino3dmLoadPlugin, ClearcoatTintPlugin, FragmentClippingExtensionPlugin, NoiseBumpMaterialPlugin, AnisotropyPlugin, CustomBumpMapPlugin, VirtualCamerasPlugin, TilesRendererPlugin], |
| "@threepipe/plugin-gltf-transform": "./../../plugins/gltf-transform/dist/index.mjs", | "@threepipe/plugin-gltf-transform": "./../../plugins/gltf-transform/dist/index.mjs", | ||||
| "@threepipe/plugin-gaussian-splatting": "./../../plugins/gaussian-splatting/dist/index.mjs", | "@threepipe/plugin-gaussian-splatting": "./../../plugins/gaussian-splatting/dist/index.mjs", | ||||
| "@threepipe/plugin-3d-tiles-renderer": "./../../plugins/3d-tiles-renderer/dist/index.mjs", | "@threepipe/plugin-3d-tiles-renderer": "./../../plugins/3d-tiles-renderer/dist/index.mjs", | ||||
| "@threepipe/plugin-assimpjs": "./../../plugins/assimpjs/dist/index.mjs", | |||||
| "@threepipe/webgi-plugins": "https://unpkg.com/@threepipe/webgi-plugins@0.4.1/dist/index.mjs" | "@threepipe/webgi-plugins": "https://unpkg.com/@threepipe/webgi-plugins@0.4.1/dist/index.mjs" | ||||
| } | } | ||||
| } | } |
| { | { | ||||
| "name": "@threepipe/plugin-assimpjs", | "name": "@threepipe/plugin-assimpjs", | ||||
| "version": "0.1.0", | |||||
| "version": "0.2.0", | |||||
| "lockfileVersion": 3, | "lockfileVersion": 3, | ||||
| "requires": true, | "requires": true, | ||||
| "packages": { | "packages": { | ||||
| "": { | "": { | ||||
| "name": "@threepipe/plugin-assimpjs", | "name": "@threepipe/plugin-assimpjs", | ||||
| "version": "0.1.0", | |||||
| "version": "0.2.0", | |||||
| "license": "Apache-2.0", | "license": "Apache-2.0", | ||||
| "dependencies": { | "dependencies": { | ||||
| "assimpjs": "^0.0.10", | "assimpjs": "^0.0.10", |
| { | { | ||||
| "name": "@threepipe/plugin-assimpjs", | "name": "@threepipe/plugin-assimpjs", | ||||
| "description": "Assimp.js Plugin for ThreePipe", | "description": "Assimp.js Plugin for ThreePipe", | ||||
| "version": "0.1.0", | |||||
| "version": "0.2.0", | |||||
| "devDependencies": { | "devDependencies": { | ||||
| }, | }, | ||||
| "dependencies": { | "dependencies": { | ||||
| "repository": { | "repository": { | ||||
| "type": "git", | "type": "git", | ||||
| "url": "git://github.com/repalash/threepipe.git", | "url": "git://github.com/repalash/threepipe.git", | ||||
| "directory": "plugins/plugin-template-vite" | |||||
| "directory": "plugins/assimpjs" | |||||
| }, | }, | ||||
| "clean-package": { | "clean-package": { | ||||
| "remove": [ | "remove": [ |
| import {AViewerPluginSync, ThreeViewer, getUrlQueryParam, createScriptFromURL} from 'threepipe' | |||||
| import { | |||||
| AViewerPluginSync, | |||||
| ThreeViewer, | |||||
| getUrlQueryParam, | |||||
| createScriptFromURL, | |||||
| IObject3D, | |||||
| ExportFileOptions, uiButton, uiToggle, PickingPlugin, uiFolderContainer, | |||||
| } from 'threepipe' | |||||
| interface AssimpJsInterface{ | interface AssimpJsInterface{ | ||||
| [key: string]: any | [key: string]: any | ||||
| * Assimpjs - https://github.com/kovacsv/assimpjs | * Assimpjs - https://github.com/kovacsv/assimpjs | ||||
| * Fork with a custom build to support fbx export - https://github.com/repalash/assimpjs | * Fork with a custom build to support fbx export - https://github.com/repalash/assimpjs | ||||
| */ | */ | ||||
| @uiFolderContainer('Assimp') | |||||
| export class AssimpJsPlugin extends AViewerPluginSync { | export class AssimpJsPlugin extends AViewerPluginSync { | ||||
| public static readonly PluginType: string = 'SamplePlugin' | public static readonly PluginType: string = 'SamplePlugin' | ||||
| enabled = true | enabled = true | ||||
| dependencies = [] | dependencies = [] | ||||
| initOnAdd: boolean | |||||
| // public static LIBRARY_PATH = `https://cdn.jsdelivr.net/npm/assimpjs@${getUrlQueryParam('assimpjs', '0.0.10')}/` | // public static LIBRARY_PATH = `https://cdn.jsdelivr.net/npm/assimpjs@${getUrlQueryParam('assimpjs', '0.0.10')}/` | ||||
| // adds fbx export support | // adds fbx export support | ||||
| public static LIBRARY_PATH = `https://cdn.jsdelivr.net/gh/repalash/assimpjs@${getUrlQueryParam('assimpjs', 'main')}/` | public static LIBRARY_PATH = `https://cdn.jsdelivr.net/gh/repalash/assimpjs@${getUrlQueryParam('assimpjs', 'main')}/` | ||||
| constructor() { | |||||
| constructor(initOnAdd = true) { | |||||
| super() | super() | ||||
| this.initOnAdd = initOnAdd | |||||
| } | } | ||||
| protected _scriptElement?: HTMLScriptElement | protected _scriptElement?: HTMLScriptElement | ||||
| private _initing: Promise<void>|undefined | private _initing: Promise<void>|undefined | ||||
| } | } | ||||
| ajs?: AssimpJsInterface | ajs?: AssimpJsInterface | ||||
| initOnAdd = true | |||||
| onAdded(viewer: ThreeViewer) { | onAdded(viewer: ThreeViewer) { | ||||
| super.onAdded(viewer) | super.onAdded(viewer) | ||||
| if (this.initOnAdd) this.init() | if (this.initOnAdd) this.init() | ||||
| const blob = new Blob([resultFile.GetContent()], {type: 'application/octet-stream'}) | const blob = new Blob([resultFile.GetContent()], {type: 'application/octet-stream'}) | ||||
| return blob | return blob | ||||
| } | } | ||||
| async exportModel(format: 'fbx'|'gltf2'|'glb2'|'assjson' = 'glb2', object?: IObject3D, options: ExportFileOptions = {embedUrlImages: true}) { | |||||
| if (!this._viewer) { | |||||
| console.error('AssimpJsPlugin - No viewer attached, please add the plugin to a viewer instance.') | |||||
| return | |||||
| } | |||||
| const initing = this.init() | |||||
| const selected = this.exportSelected ? this._viewer.getPlugin(PickingPlugin)?.getSelectedObject() : undefined | |||||
| object = object || selected || this._viewer.scene.modelRoot | |||||
| // export to glb | |||||
| const blob = await this._viewer.export(object, options) | |||||
| if (!blob || blob.ext !== 'glb') { | |||||
| console.error('AssimpJsPlugin - Unable to export model, no blob returned.') | |||||
| return | |||||
| } | |||||
| await initing // wait for assimp.js to be initialized | |||||
| const blob2 = this.convertFiles({['file.glb']: await blob.arrayBuffer()}, format) | |||||
| if (!blob2) { | |||||
| console.error('AssimpJsPlugin - Unable to convert model to format:', format) | |||||
| return | |||||
| } | |||||
| // convert to ArrayBuffer | |||||
| return new File([blob2], 'model.' + format.replace(/2$/, ''), {type: 'application/octet-stream'}) | |||||
| } | |||||
| @uiToggle('Export Selected') | |||||
| exportSelected = true | |||||
| @uiButton('Download FBX') | |||||
| async exportAsFbx(object?: IObject3D) { | |||||
| if (!object?.isObject3D) object = undefined | |||||
| const blob = await this.exportModel('fbx', object) | |||||
| if (blob) { | |||||
| await this._viewer?.exportBlob(blob, blob.name) | |||||
| } | |||||
| } | |||||
| @uiButton('Download Assimp JSON') | |||||
| async exportAsGlb(object?: IObject3D) { | |||||
| if (!object?.isObject3D) object = undefined | |||||
| const blob = await this.exportModel('assjson', object) | |||||
| if (blob) { | |||||
| await this._viewer?.exportBlob(blob, blob.name) | |||||
| } | |||||
| } | |||||
| } | } |
| [](https://www.npmjs.com/package/@threepipe/plugin-assimpjs) | [](https://www.npmjs.com/package/@threepipe/plugin-assimpjs) | ||||
| This package uses a custom fork of [assimpjs](https://github.com/kovacsv/assimpjs) with custom build to support fbx export etc - https://github.com/repalash/assimpjs | |||||
| ```bash | ```bash | ||||
| npm install @threepipe/plugin-assimpjs | npm install @threepipe/plugin-assimpjs | ||||
| ``` | ``` | ||||
| // load some models | // load some models | ||||
| const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf') | const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf') | ||||
| // export to glb | |||||
| const blob = await viewer.export(result, { | |||||
| const fbxBlob = await assimp.exportModel('fbx', result, { | |||||
| embedUrlImages: true, | embedUrlImages: true, | ||||
| }) | }) | ||||
| fbxBlob = assimp.convertFiles({['file.glb']: await blob.arrayBuffer()}, 'fbx') | |||||
| if (!fbxBlob) { | |||||
| console.error('Failed to convert files to fbx') | |||||
| return | |||||
| } | |||||
| // download the fbx file | // download the fbx file | ||||
| downloadBlob(fbxBlob, 'model.fbx') | downloadBlob(fbxBlob, 'model.fbx') | ||||
| ``` | ``` |