threepipe
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

AssetManager.ts 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. import {ImportAssetOptions, ImportResult, ProcessRawOptions, RootSceneImportResult} from './IAssetImporter'
  2. import {
  3. BaseEvent,
  4. Cache as threeCache,
  5. Camera,
  6. EventDispatcher,
  7. Light,
  8. LinearFilter,
  9. LinearMipmapLinearFilter,
  10. LoadingManager,
  11. Object3D,
  12. PerspectiveCamera,
  13. TextureLoader,
  14. } from 'three'
  15. import {ISerializedConfig, IViewerPlugin, ThreeViewer} from '../viewer'
  16. import {AssetImporter} from './AssetImporter'
  17. import {generateUUID, getTextureDataType, overrideThreeCache} from '../three'
  18. import {IAsset} from './IAsset'
  19. import {
  20. AddObjectOptions,
  21. AmbientLight2,
  22. DirectionalLight2,
  23. HemisphereLight2,
  24. ICamera,
  25. iCameraCommons,
  26. ILight,
  27. iLightCommons,
  28. IMaterial,
  29. iMaterialCommons,
  30. IObject3D,
  31. iObjectCommons,
  32. ISceneEventMap,
  33. ITexture,
  34. PerspectiveCamera2,
  35. PointLight2,
  36. RectAreaLight2,
  37. SpotLight2,
  38. upgradeTexture,
  39. } from '../core'
  40. import {Importer} from './Importer'
  41. import {MaterialManager} from './MaterialManager'
  42. import {
  43. DRACOLoader2,
  44. FBXLoader2,
  45. GLTFLoader2,
  46. JSONMaterialLoader,
  47. MTLLoader2,
  48. OBJLoader2,
  49. SVGTextureLoader,
  50. ZipLoader,
  51. } from './import'
  52. import {RGBELoader} from 'three/examples/jsm/loaders/RGBELoader.js'
  53. import {EXRLoader} from 'three/examples/jsm/loaders/EXRLoader.js'
  54. import {Class, ValOrArr} from 'ts-browser-helpers'
  55. import {ILoader} from './IImporter'
  56. import {AssetExporter} from './AssetExporter'
  57. import {IExporter} from './IExporter'
  58. import {GLTFExporter2, GLTFWriter2} from './export'
  59. import {legacySeparateMapSamplerUVFix} from '../utils/legacy'
  60. import type {GLTFLoaderPlugin, GLTFParser} from 'three/examples/jsm/loaders/GLTFLoader'
  61. import {GLTFExporterPlugin} from 'three/examples/jsm/exporters/GLTFExporter'
  62. export interface AssetManagerOptions{
  63. /**
  64. * simple memory based cache for downloaded files, default = false
  65. */
  66. simpleCache?: boolean
  67. /**
  68. * Cache Storage for downloaded files, can use with `caches.open`
  69. * When true and by default uses `caches.open('threepipe-assetmanager')`, set to false to disable
  70. * @default true
  71. */
  72. storage?: Cache | Storage | boolean
  73. }
  74. export interface AddAssetOptions extends AddObjectOptions{
  75. /**
  76. * Automatically set any loaded HDR, EXR file as the scene environment map
  77. * @default true
  78. */
  79. autoSetEnvironment?: boolean
  80. /**
  81. * Automatically set any loaded image(ITexture) file as the scene background
  82. */
  83. autoSetBackground?: boolean
  84. }
  85. export interface ImportAddOptions extends ImportAssetOptions, AddAssetOptions{}
  86. export interface AddRawOptions extends ProcessRawOptions, AddAssetOptions{}
  87. export interface AssetManagerEventMap{
  88. loadAsset: {data: ImportResult}
  89. processStateUpdate: object
  90. }
  91. /**
  92. * Asset Manager
  93. *
  94. * Utility class to manage import, export, and material management.
  95. * @category Asset Manager
  96. */
  97. export class AssetManager extends EventDispatcher<AssetManagerEventMap> {
  98. readonly viewer: ThreeViewer
  99. readonly importer: AssetImporter
  100. readonly exporter: AssetExporter
  101. readonly materials: MaterialManager
  102. private _storage?: Cache | Storage
  103. get storage() {
  104. return this._storage
  105. }
  106. constructor(viewer: ThreeViewer, {simpleCache = false, storage}: AssetManagerOptions = {}) {
  107. super()
  108. this._sceneUpdated = this._sceneUpdated.bind(this)
  109. this.addAsset = this.addAsset.bind(this)
  110. this.addRaw = this.addRaw.bind(this)
  111. this._loaderCreate = this._loaderCreate.bind(this)
  112. this.addImported = this.addImported.bind(this)
  113. this.importer = new AssetImporter(!!viewer.getPlugin('debug'))
  114. this.exporter = new AssetExporter()
  115. this.materials = new MaterialManager()
  116. this.viewer = viewer
  117. this.viewer.scene.addEventListener('addSceneObject', this._sceneUpdated)
  118. this.viewer.scene.addEventListener('materialChanged', this._sceneUpdated)
  119. this.viewer.scene.addEventListener('beforeDeserialize', this._sceneUpdated)
  120. this._initCacheStorage(simpleCache, storage ?? true)
  121. this._setupGltfExtensions()
  122. this._setupObjectProcess()
  123. this._setupProcessState()
  124. this._addImporters()
  125. this._addExporters()
  126. }
  127. async addAsset<T extends ImportResult = ImportResult>(assetOrPath?: string | IAsset | IAsset[] | File | File[], options?: ImportAddOptions): Promise<(T | undefined)[]> {
  128. if (!this.importer || !this.viewer) return []
  129. const imported = await this.importer.import<T>(assetOrPath, options)
  130. if (!imported) {
  131. const path = typeof assetOrPath === 'string' ? assetOrPath : (assetOrPath as IAsset)?.path
  132. if (path && !path.split('?')[0].endsWith('.vjson'))
  133. console.warn('Threepipe AssetManager - Unable to import', assetOrPath, imported)
  134. return []
  135. }
  136. return this.loadImported<(T | undefined)[]>(imported, options)
  137. }
  138. // materials: IMaterial[] = []
  139. // textures: ITexture[] = []
  140. // todo move this function to viewer
  141. async loadImported<T extends ValOrArr<ImportResult | undefined> = ImportResult>(imported: T, {
  142. autoSetEnvironment = true,
  143. autoSetBackground = false,
  144. ...options
  145. }: AddAssetOptions = {}): Promise<T | never[]> {
  146. const arr: (ImportResult | undefined)[] = Array.isArray(imported) ? imported : [imported]
  147. let ret: T = Array.isArray(imported) ? [] : undefined as any
  148. if (options?.importConfig !== false) {
  149. const config = arr.find(v => v?.assetType === 'config') || arr.find(v=>v && !!v.importedViewerConfig)?.importedViewerConfig
  150. if (config) legacySeparateMapSamplerUVFix(config, arr.filter(a=>a?.isObject3D) as Object3D[])
  151. }
  152. for (const obj of arr) {
  153. if (!obj) {
  154. if (Array.isArray(ret)) ret.push(undefined)
  155. continue
  156. }
  157. let r = obj
  158. switch (obj.assetType) {
  159. case 'material':
  160. this.materials.registerMaterial(<IMaterial>obj)
  161. break
  162. case 'texture':
  163. if (autoSetEnvironment && (
  164. obj.__rootPath?.endsWith('.hdr') || obj.__rootPath?.endsWith('.exr')
  165. )) this.viewer.scene.environment = <ITexture>obj
  166. if (autoSetBackground) this.viewer.scene.background = <ITexture>obj
  167. break
  168. case 'model':
  169. case 'light':
  170. case 'camera':
  171. r = await this.viewer.addSceneObject(<IObject3D | RootSceneImportResult>obj, options) // todo update references in scene update event
  172. break
  173. case 'config':
  174. if (options?.importConfig !== false) await this.viewer.importConfig(<ISerializedConfig>obj)
  175. break
  176. default:
  177. // legacy
  178. if (obj.type && typeof obj.type === 'string' && (Array.isArray((obj as any).plugins) ||
  179. (obj as any).type === 'ThreeViewer' || this.viewer.getPlugin((obj as any).type))) {
  180. await this.viewer.importConfig(<ISerializedConfig>obj)
  181. }
  182. break
  183. }
  184. this.dispatchEvent({type: 'loadAsset', data: obj})
  185. if (Array.isArray(ret)) ret.push(r)
  186. else ret = r as T
  187. }
  188. return ret || []
  189. }
  190. /**
  191. * same as {@link loadImported}
  192. * @param imported
  193. * @param options
  194. */
  195. async addProcessedAssets<T extends ImportResult | undefined = ImportResult>(imported: (T | undefined)[], options?: AddAssetOptions): Promise<(T | undefined)[]> {
  196. return this.loadImported(imported, options)
  197. }
  198. async addAssetSingle<T extends ImportResult = ImportResult>(asset?: string | IAsset | File, options?: ImportAssetOptions): Promise<T | undefined> {
  199. return !asset ? undefined : (await this.addAsset<T>(asset, options))?.[0]
  200. }
  201. // processAndAddObjects
  202. async addRaw<T extends (ImportResult | undefined) = ImportResult>(res: T | T[], options: AddRawOptions = {}): Promise<(T | undefined)[]> {
  203. const r = await this.importer.processRaw<T>(res, options)
  204. return this.loadImported<T[]>(r, options)
  205. }
  206. async addRawSingle<T extends ImportResult | undefined = ImportResult | undefined>(res: T, options: AddRawOptions = {}): Promise<T | undefined> {
  207. return (await this.addRaw<T>(res, options))?.[0]
  208. }
  209. private _sceneUpdated<T extends keyof ISceneEventMap>(ev: BaseEvent<T> & ISceneEventMap[T]) { // todo: check if objects are added some other way.
  210. if (ev.type === 'addSceneObject') {
  211. const event = ev as ISceneEventMap['addSceneObject']
  212. const target = event.object as ImportResult
  213. switch (target.assetType) {
  214. case 'material':
  215. this.materials.registerMaterial(<IMaterial>target)
  216. break
  217. case 'texture':
  218. break
  219. case 'model':
  220. case 'light':
  221. case 'camera':
  222. break
  223. default:
  224. break
  225. }
  226. } else if (ev.type === 'materialChanged') {
  227. const event = ev as ISceneEventMap['materialChanged']
  228. const target = event.material as IMaterial | IMaterial[] | undefined
  229. const targets = Array.isArray(target) ? target : target ? [target] : []
  230. for (const t of targets) {
  231. this.materials.registerMaterial(t)
  232. }
  233. } else if (ev.type === 'beforeDeserialize') {
  234. const event = ev as ISceneEventMap['beforeDeserialize']
  235. // object/material/texture to be deserialized
  236. const data = event.data as any
  237. const meta = event.meta
  238. if (!data.metadata) {
  239. console.warn('Invalid data(no metadata)', data)
  240. }
  241. if (event.material) {
  242. if (data.metadata?.type !== 'Material') {
  243. console.warn('Invalid material data', data)
  244. }
  245. JSONMaterialLoader.DeserializeMaterialJSON(data, this.viewer, meta, event.material).then(() => {
  246. //
  247. })
  248. }
  249. } else {
  250. console.error('Unexpected')
  251. }
  252. }
  253. dispose() {
  254. this.importer.dispose()
  255. this.materials.dispose()
  256. this.processState.clear()
  257. this.viewer.scene.removeEventListener('addSceneObject', this._sceneUpdated)
  258. this.viewer.scene.removeEventListener('materialChanged', this._sceneUpdated)
  259. this.exporter.dispose()
  260. }
  261. protected _addImporters() {
  262. const viewer = this.viewer
  263. if (!viewer) return
  264. // todo fix - loading manager getHandler matches backwards?
  265. const importers: Importer[] = [
  266. new Importer(SVGTextureLoader, ['svg', 'data:image/svg'], ['image/svg+xml'], false), // todo: use ImageBitmapLoader if supported (better performance)
  267. new Importer(TextureLoader, ['webp', 'png', 'jpeg', 'jpg', 'ico', 'data:image', 'avif', 'bmp', 'gif', 'tiff'], [
  268. 'image/webp', 'image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/tiff', 'image/x-icon', 'image/avif',
  269. ], false), // todo: use ImageBitmapLoader if supported (better performance)
  270. new Importer<JSONMaterialLoader>(JSONMaterialLoader,
  271. ['mat', ...this.materials.templates.map(t => t.typeSlug!).filter(v => v)], // todo add others
  272. [], false, (loader) => {
  273. if (loader) loader.viewer = this.viewer
  274. return loader
  275. }),
  276. new Importer(class extends RGBELoader {
  277. constructor(manager: LoadingManager) {
  278. super(manager)
  279. this.setDataType(getTextureDataType(viewer.renderManager.renderer))
  280. }
  281. }, ['hdr'], ['image/vnd.radiance'], false),
  282. new Importer(class extends EXRLoader {
  283. constructor(manager: LoadingManager) {
  284. super(manager)
  285. this.setDataType(getTextureDataType(viewer.renderManager.renderer))
  286. }
  287. }, ['exr'], ['image/x-exr'], false),
  288. new Importer(FBXLoader2, ['fbx'], ['model/fbx'], true),
  289. new Importer(ZipLoader, ['zip', 'glbz', 'gltfz'], ['application/zip', 'model/gltf+zip', 'model/zip'], true), // gltfz and glbz are invented zip files with gltf/glb inside along with resources
  290. new Importer(OBJLoader2 as any as Class<ILoader>, ['obj'], ['model/obj'], true),
  291. new Importer(MTLLoader2 as any as Class<ILoader>, ['mtl'], ['model/mtl'], false),
  292. new Importer<GLTFLoader2>(GLTFLoader2, ['gltf', 'glb', 'data:model/gltf', 'data:model/glb'], ['model/gltf', 'model/gltf+json', 'model/gltf-binary', 'model/glb'], true, (l, _, i) => l?.setup(this.viewer, i.extensions)),
  293. new Importer(DRACOLoader2, ['drc'], ['model/mesh+draco', 'model/drc'], true),
  294. ]
  295. this.importer.addImporter(...importers)
  296. }
  297. private _gltfExporter = {
  298. ext: ['gltf', 'glb'],
  299. extensions: [] as (typeof GLTFExporter2.ExportExtensions)[number][],
  300. ctor: (_, exporter) => {
  301. const ex = new GLTFExporter2()
  302. // This should be added at the end.
  303. ex.setup(this.viewer, exporter.extensions)
  304. return ex
  305. },
  306. } satisfies IExporter
  307. protected _addExporters() {
  308. const exporters: IExporter[] = [this._gltfExporter]
  309. this.exporter.addExporter(...exporters)
  310. }
  311. private _initCacheStorage(simpleCache?: boolean, storage?: Cache | Storage | boolean) {
  312. if (storage === true && window?.caches) {
  313. window.caches.open?.('threepipe-assetmanager').then(c => {
  314. this._initCacheStorage(simpleCache, c)
  315. this._storage = c
  316. })
  317. return
  318. }
  319. if (simpleCache || storage) {
  320. // three.js built-in simple memory cache. used in FileLoader.js todo: use local storage somehow
  321. if (simpleCache) threeCache.enabled = true
  322. if (storage && window.Cache && typeof window.Cache === 'function' && storage instanceof window.Cache) {
  323. overrideThreeCache(storage)
  324. // todo: clear cache
  325. }
  326. }
  327. this._storage = typeof storage === 'boolean' ? undefined : storage
  328. }
  329. protected _setupObjectProcess() {
  330. this.importer.addEventListener('processRaw', (event) => {
  331. // console.log('preprocess mat', mat)
  332. const mat = event.data as IMaterial
  333. if (!mat || !mat.isMaterial || !mat.uuid) return
  334. if (this.materials?.findMaterial(mat.uuid)) {
  335. console.warn('imported material uuid already exists, creating new uuid')
  336. mat.uuid = generateUUID()
  337. if (mat.userData.uuid) mat.userData.uuid = mat.uuid
  338. }
  339. // todo: check for name exists also
  340. this.materials.registerMaterial(mat)
  341. })
  342. this.importer.addEventListener('processRawStart', (event) => {
  343. // console.log('preprocess mat', mat)
  344. const res = event.data!
  345. const options = event.options! as ProcessRawOptions
  346. // if (!res.assetType) {
  347. // if (res.isBufferGeometry) { // for eg stl todo
  348. // res = new Mesh(res, new MeshStandardMaterial())
  349. // }
  350. // if (res.isObject3D) {
  351. // }
  352. // }
  353. if (res.isObject3D) {
  354. const cameras: Camera[] = []
  355. const lights: Light[] = []
  356. res.traverse((obj: any) => {
  357. if (obj.material) {
  358. const materials = Array.isArray(obj.material) ? obj.material : [obj.material]
  359. const newMaterials = []
  360. for (const material of materials) {
  361. const mat = this.materials.convertToIMaterial(material, {createFromTemplate: options.replaceMaterials !== false}) || material
  362. mat.uuid = material.uuid
  363. mat.userData.uuid = material.uuid
  364. newMaterials.push(mat)
  365. }
  366. if (Array.isArray(obj.material)) obj.material = newMaterials
  367. else obj.material = newMaterials[0]
  368. }
  369. if (obj.isCamera) cameras.push(obj)
  370. if (obj.isLight) lights.push(obj)
  371. })
  372. for (const camera of cameras) {
  373. if ((camera as PerspectiveCamera2).assetType === 'camera') continue
  374. // todo: OrthographicCamera
  375. if (!(camera as PerspectiveCamera).isPerspectiveCamera || !camera.parent || options.replaceCameras === false) {
  376. iCameraCommons.upgradeCamera.call(camera)
  377. } else {
  378. const newCamera: ICamera = (camera as any).iCamera ??
  379. new PerspectiveCamera2('', this.viewer.canvas)
  380. if (camera === newCamera) continue
  381. camera.parent.children.splice(camera.parent.children.indexOf(camera), 1, newCamera)
  382. newCamera.parent = camera.parent as any
  383. newCamera.copy(camera as any)
  384. camera.parent = null
  385. ;(newCamera as any).uuid = camera.uuid
  386. newCamera.userData.uuid = camera.uuid
  387. ;(camera as any).iCamera = newCamera
  388. // console.log('replacing camera', camera, newCamera)
  389. }
  390. }
  391. for (const light of lights) {
  392. if ((light as ILight).assetType === 'light') continue
  393. if (!light.parent || options.replaceLights === false) {
  394. iLightCommons.upgradeLight.call(light)
  395. } else {
  396. const newLight: ILight | undefined = (light as any).iLight ??
  397. (light as any).isDirectionalLight ? new DirectionalLight2() :
  398. (light as any).isPointLight ? new PointLight2() :
  399. (light as any).isSpotLight ? new SpotLight2() :
  400. (light as any).isAmbientLight ? new AmbientLight2() :
  401. (light as any).isHemisphereLight ? new HemisphereLight2() :
  402. (light as any).isRectAreaLight ? new RectAreaLight2() :
  403. undefined
  404. if (light === newLight || !newLight) continue
  405. light.parent.children.splice(light.parent.children.indexOf(light), 1, newLight)
  406. newLight.parent = light.parent as any
  407. newLight.copy(light as any)
  408. light.parent = null
  409. ;(newLight as any).uuid = light.uuid
  410. newLight.userData.uuid = light.uuid
  411. ;(light as any).iLight = newLight
  412. }
  413. }
  414. iObjectCommons.upgradeObject3D.call(res)
  415. } else if (res.isMaterial) {
  416. iMaterialCommons.upgradeMaterial.call(res)
  417. // todo update res by generating new material?
  418. } else if (res.isTexture) {
  419. upgradeTexture.call(res)
  420. if (event?.options?.generateMipmaps !== undefined)
  421. res.generateMipmaps = event?.options.generateMipmaps
  422. if (!res.generateMipmaps && !res.isRenderTargetTexture) { // todo: do we need to check more?
  423. res.minFilter = res.minFilter === LinearMipmapLinearFilter ? LinearFilter : res.minFilter
  424. res.magFilter = res.magFilter === LinearMipmapLinearFilter ? LinearFilter : res.magFilter
  425. }
  426. }
  427. // todo other asset/object types?
  428. })
  429. }
  430. /**
  431. * State of download/upload/process/other processes in the viewer.
  432. * Subscribes to importer and exporter by default, more can be added by plugins like {@link FileTransferPlugin}
  433. */
  434. processState: Map<string, {state: string, progress?: number | undefined}> = new Map()
  435. /**
  436. * Set process state for a path
  437. * Progress should be a number between 0 and 100
  438. * Pass undefined in value to remove the state
  439. * @param path
  440. * @param value
  441. */
  442. setProcessState(path: string, value: {state: string, progress?: number | undefined} | undefined) {
  443. if (value === undefined) this.processState.delete(path)
  444. else this.processState.set(path, value)
  445. this.dispatchEvent({type: 'processStateUpdate'})
  446. }
  447. protected _setupProcessState() {
  448. this.importer.addEventListener('importFile', (data: any) => {
  449. this.setProcessState(data.path, data.state !== 'done' ? {
  450. state: data.state,
  451. progress: data.progress ? data.progress * 100 : undefined,
  452. } : undefined)
  453. })
  454. this.importer.addEventListener('processRawStart', (data: any) => {
  455. this.setProcessState(data.path, {
  456. state: 'processing',
  457. progress: undefined,
  458. })
  459. })
  460. this.importer.addEventListener('processRaw', (data: any) => {
  461. this.setProcessState(data.path, undefined)
  462. })
  463. this.exporter.addEventListener('exportFile', (data: any) => {
  464. this.setProcessState(data.obj.name, data.state !== 'done' ? {
  465. state: data.state,
  466. progress: data.progress ? data.progress * 100 : undefined,
  467. } : undefined)
  468. })
  469. }
  470. // region glTF extensions registration helpers
  471. gltfExtensions: {
  472. name: string
  473. import: (parser: GLTFParser) => GLTFLoaderPlugin,
  474. export: (parser: GLTFWriter2) => GLTFExporterPlugin,
  475. textures?: Record<string, string|number> // see GLTFDracoExportPlugin
  476. }[] = []
  477. protected _setupGltfExtensions() {
  478. this.importer.addEventListener('loaderCreate', this._loaderCreate as any)
  479. this.viewer.forPlugin('GLTFDracoExportPlugin', (p)=> {
  480. if (!p.addExtension) return
  481. for (const gltfExtension of this.gltfExtensions) {
  482. p.addExtension(gltfExtension.name, gltfExtension.textures)
  483. }
  484. })
  485. }
  486. protected _loaderCreate({loader}: {loader: GLTFLoader2}) {
  487. if (!loader.isGLTFLoader2) return
  488. for (const gltfExtension of this.gltfExtensions) {
  489. loader.register(gltfExtension.import)
  490. }
  491. }
  492. registerGltfExtension(ext: AssetManager['gltfExtensions'][number]) {
  493. const ext1 = this.gltfExtensions.findIndex(e => e.name === ext.name)
  494. if (ext1 >= 0) this.gltfExtensions.splice(ext1, 1)
  495. this.gltfExtensions.push(ext)
  496. this._gltfExporter.extensions.push(ext.export)
  497. const exporter2 = this.exporter.getExporter('gltf', 'glb')
  498. if (exporter2 && exporter2 !== this._gltfExporter)
  499. exporter2.extensions?.push(ext.export)
  500. }
  501. unregisterGltfExtension(name: string) {
  502. const ind = this.gltfExtensions.findIndex(e => e.name === name)
  503. if (ind < 0) return
  504. this.gltfExtensions.splice(ind, 1)
  505. const ind1 = this._gltfExporter.extensions.findIndex(e => e.name === name)
  506. if (ind1 >= 0) this._gltfExporter.extensions.splice(ind1, 1)
  507. const exporter2 = this.exporter.getExporter('gltf', 'glb')
  508. if (exporter2?.extensions && exporter2 !== this._gltfExporter) {
  509. const ind2 = exporter2.extensions.findIndex(e => e.name === name)
  510. if (ind2 >= 0) exporter2.extensions?.splice(ind2, 1)
  511. }
  512. }
  513. // endregion
  514. // region deprecated
  515. /**
  516. * @deprecated use addRaw instead
  517. * @param res
  518. * @param options
  519. */
  520. async addImported<T extends (ImportResult | undefined) = ImportResult>(res: T | T[], options: AddRawOptions = {}): Promise<(T | undefined)[]> {
  521. console.error('addImported is deprecated, use addRaw instead')
  522. return this.addRaw(res, options)
  523. }
  524. /**
  525. * @deprecated use addAsset instead
  526. * @param path
  527. * @param options
  528. */
  529. public async addFromPath(path: string, options: ImportAddOptions = {}): Promise<any[]> {
  530. console.error('addFromPath is deprecated, use addAsset instead')
  531. return this.addAsset(path, options)
  532. }
  533. /**
  534. * @deprecated use {@link ThreeViewer.exportConfig} instead
  535. * @param binary - if set to false, encodes all the array buffers to base64
  536. */
  537. exportViewerConfig(binary = true): Record<string, any> {
  538. if (!this.viewer) return {}
  539. console.error('exportViewerConfig is deprecated, use viewer.toJSON instead')
  540. return this.viewer.toJSON(binary, undefined)
  541. }
  542. /**
  543. * @deprecated use {@link ThreeViewer.exportPluginsConfig} instead
  544. * @param filter
  545. */
  546. exportPluginPresets(filter?: string[]) {
  547. console.error('exportPluginPresets is deprecated, use viewer.exportPluginsConfig instead')
  548. return this.viewer?.exportPluginsConfig(filter)
  549. }
  550. /**
  551. * @deprecated use {@link ThreeViewer.exportPluginConfig} instead
  552. * @param plugin
  553. */
  554. exportPluginPreset(plugin: IViewerPlugin) {
  555. console.error('exportPluginPreset is deprecated, use viewer.exportPluginConfig instead')
  556. return this.viewer?.exportPluginConfig(plugin)
  557. }
  558. /**
  559. * @deprecated use {@link ThreeViewer.importPluginConfig} instead
  560. * @param json
  561. * @param plugin
  562. */
  563. async importPluginPreset(json: any, plugin?: IViewerPlugin) {
  564. console.error('importPluginPreset is deprecated, use viewer.importPluginConfig instead')
  565. return this.viewer?.importPluginConfig(json, plugin)
  566. }
  567. // todo continue from here by moving functions to the viewer.
  568. /**
  569. * @deprecated use {@link ThreeViewer.importConfig} instead
  570. * @param viewerConfig
  571. */
  572. async importViewerConfig(viewerConfig: any) {
  573. return this.viewer?.importConfig(viewerConfig)
  574. }
  575. /**
  576. * @deprecated use {@link ThreeViewer.fromJSON} instead
  577. * @param viewerConfig
  578. */
  579. applyViewerConfig(viewerConfig: any, resources?: any) {
  580. console.error('applyViewerConfig is deprecated, use viewer.fromJSON instead')
  581. return this.viewer?.fromJSON(viewerConfig, resources)
  582. }
  583. /**
  584. * @deprecated moved to {@link ThreeViewer.loadConfigResources}
  585. * @param json
  586. * @param extraResources - preloaded resources in the format of viewer config resources.
  587. */
  588. async importConfigResources(json: any, extraResources?: any) {
  589. if (!this.importer) throw 'Importer not initialized yet.'
  590. if (json.__isLoadedResources) return json
  591. return this.viewer?.loadConfigResources(json, extraResources)
  592. }
  593. /**
  594. * @deprecated not a plugin anymore
  595. */
  596. static readonly PluginType = 'AssetManager'
  597. // endregion
  598. }