threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
171 lines (159 loc) • 6.72 kB
text/typescript
import {serialize} from 'ts-browser-helpers'
import {BlobExt, ExportFileOptions} from '../../assetmanager'
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {UiObjectConfig} from 'uiconfig.js'
import {PickingPlugin} from '../interaction/PickingPlugin'
// export enum EncoderMethod {
// EDGEBREAKER = 1,
// SEQUENTIAL = 0
// }
export interface ExportAssetOptions extends ExportFileOptions {
convertMeshToIndexed?: boolean // convert mesh to indexed geometry
name?: string
compress?: boolean,
}
// todo deprecate the plugin and add functionality to AssetManager maybe
/**
* Asset Exporter Plugin
* Provides options and methods to export the scene, object GLB or Viewer Config.
* All the functionality is available in the viewer directly, this provides only a ui-config and maintains state of the options.
*/
export class AssetExporterPlugin extends AViewerPluginSync {
public static readonly PluginType = 'AssetExporterPlugin'
enabled = true
constructor() {
super()
this.exportScene = this.exportScene.bind(this)
}
onAdded(viewer: ThreeViewer) {
super.onAdded(viewer)
// todo Convert all non-indexed geometries to indexed geometries, because DRACO compression requires indexed geometry.
// this.exporter.processors.add('model', {
// forAssetType: 'model',
// processAsync: async(obj: IObject3D, options) => {
// if (options.convertMeshToIndexed)
// obj?.traverse((o: IObject3D)=>{
// if (!o.geometry) return
// if (o.geometry.attributes.index) return
// o.geometry = toIndexedGeometry(o.geometry)
// })
// return obj
// },
// })
}
onRemove(viewer: ThreeViewer) {
return super.onRemove(viewer)
}
// readonly because bound to ui
readonly exportOptions: ExportAssetOptions = {
name: 'scene',
viewerConfig: true,
encodeUint16Rgbe: false,
convertMeshToIndexed: false,
embedUrlImagePreviews: false,
embedUrlImages: false,
encrypt: false,
encryptKey: '',
ignoreInvalidMorphTargetTracks: true,
ignoreEmptyTextures: true,
}
async exportScene(options?: ExportAssetOptions): Promise<BlobExt | undefined> {
return this._viewer?.assetManager.exporter?.exportObject(this._viewer?.scene.modelRoot, options || {...this.exportOptions})
}
async downloadSceneGlb() {
const blob = await this.exportScene(this.exportOptions)
if (blob) await this._viewer?.exportBlob(blob, this.exportOptions.name + '.' + blob.ext)
}
async exportSelected(options?: ExportAssetOptions, download = true) {
const selected = this._viewer?.getPlugin<PickingPlugin>('PickingPlugin')?.getSelectedObject() as any
if (!selected) {
this._viewer?.dialog.alert('Export Selected: Nothing selected')
return
}
const name = selected.name || 'selected'
const blob = await this._viewer!.assetManager.exporter.exportObject(selected, options ?? this.exportOptions)
if (blob && download) await this._viewer?.exportBlob(blob, name + '.' + blob.ext)
return blob
}
uiConfig: UiObjectConfig = {
type: 'folder',
label: 'Asset Export',
expanded: true,
children: [
{
type: 'input',
property: [this.exportOptions, 'name'],
},
{
type: 'folder',
label: 'GLB Export',
expanded: true,
children: [
{
type: 'checkbox',
label: 'Viewer Config (All Settings)',
property: [this.exportOptions, 'viewerConfig'],
onChange: ()=>this.uiConfig.uiRefresh?.(true),
},
{
type: 'checkbox',
label: 'Embed Image Previews',
property: [this.exportOptions, 'embedUrlImagePreviews'],
},
{
type: 'checkbox',
label: 'Encrypt',
property: [this.exportOptions, 'encrypt'],
onChange: ()=>this.uiConfig.uiRefresh?.(true),
},
{
type: 'input',
label: 'Encrypt Password',
hidden: ()=>!this.exportOptions.encrypt,
property: [this.exportOptions, 'encryptKey'],
},
{
type: 'checkbox',
label: 'Compress hdr env maps',
hidden: ()=>!this.exportOptions.viewerConfig,
property: [this.exportOptions, 'encodeUint16Rgbe'],
},
// { // todo
// type: 'checkbox',
// label: 'Convert to indexed',
// property: [this.exportOptions, 'convertMeshToIndexed'],
// },
{
type: 'checkbox',
label: 'Ignore invalid animations',
property: [this.exportOptions, 'ignoreInvalidMorphTargetTracks'],
},
{
type: 'checkbox',
label: 'Ignore invalid textures',
property: [this.exportOptions, 'ignoreInvalidTextures'],
},
{
type: 'button',
label: 'Export GLB',
property: [this, 'downloadSceneGlb'],
},
],
},
{
type: 'button',
label: 'Export Config',
value: async()=>{
const blob = new Blob([JSON.stringify(this._viewer?.exportConfig(false), null, 2)], {type: 'application/json'})
if (blob) await this._viewer?.exportBlob(blob, this.exportOptions.name + '.' + ThreeViewer.ConfigTypeSlug)
},
},
{
type: 'button',
label: 'Export Selected',
hidden: ()=>!this._viewer?.getPlugin<PickingPlugin>('PickingPlugin'),
value: async()=>this.exportSelected(this.exportOptions, true),
},
],
}
}