threepipe
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

GLTFDracoExportPlugin.ts 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import {
  2. AssetExporterPlugin,
  3. AViewerPluginSync,
  4. DRACOLoader2,
  5. generateUUID,
  6. GLTFLightExtrasExtension,
  7. GLTFMaterialExtrasExtension,
  8. GLTFMaterialsAlphaMapExtension,
  9. GLTFMaterialsBumpMapExtension,
  10. GLTFMaterialsDisplacementMapExtension,
  11. GLTFMaterialsLightMapExtension,
  12. GLTFObject3DExtrasExtension,
  13. IExporter,
  14. ThreeViewer,
  15. clearCoatTintGLTFExtension,
  16. customBumpMapGLTFExtension,
  17. noiseBumpMaterialGLTFExtension,
  18. fragmentClippingGLTFExtension,
  19. } from 'threepipe'
  20. import {
  21. createGenericExtensionClass,
  22. GLTFDracoExporter,
  23. GLTFViewerConfigExtensionGP,
  24. } from './GLTFDracoExporter'
  25. import {UiObjectConfig} from 'uiconfig.js'
  26. import {EncoderOptions} from '@gltf-transform/extensions/dist/khr-draco-mesh-compression/encoder'
  27. import {Extension} from '@gltf-transform/core'
  28. export enum EncoderMethod {
  29. EDGEBREAKER = 1,
  30. SEQUENTIAL = 0
  31. }
  32. /**
  33. * GLTF Draco Export Plugin
  34. *
  35. * Overloads the default gltf exporter in the asset manager with GLTFDracoExporter. When exporting with compress = true, the output will be compressed.
  36. * Note - Only `glb` supported right now.
  37. *
  38. * @category Plugins
  39. */
  40. export class GLTFDracoExportPlugin extends AViewerPluginSync {
  41. public static readonly PluginType = 'GLTFDracoExportPlugin'
  42. enabled = true
  43. /**
  44. * These are added here, but also added as plugins. Added here by default so that the data is not lost if some plugin is not added in an app.
  45. * To explicitly remove the data, use `removeExtension` with the name of the extension
  46. */
  47. extraExtensions = [ // its array because we want to keep the order of extensions
  48. [GLTFMaterialsBumpMapExtension.WebGiMaterialsBumpMapExtension, GLTFMaterialsBumpMapExtension.Textures],
  49. [GLTFMaterialsLightMapExtension.WebGiMaterialsLightMapExtension, GLTFMaterialsLightMapExtension.Textures],
  50. [GLTFMaterialsAlphaMapExtension.WebGiMaterialsAlphaMapExtension, GLTFMaterialsAlphaMapExtension.Textures],
  51. [GLTFMaterialsDisplacementMapExtension.WebGiMaterialsDisplacementMapExtension, GLTFMaterialsDisplacementMapExtension.Textures],
  52. [customBumpMapGLTFExtension.name, customBumpMapGLTFExtension.textures],
  53. [GLTFLightExtrasExtension.WebGiLightExtrasExtension, GLTFLightExtrasExtension.Textures],
  54. [GLTFObject3DExtrasExtension.WebGiObject3DExtrasExtension, GLTFObject3DExtrasExtension.Textures],
  55. [GLTFMaterialExtrasExtension.WebGiMaterialExtrasExtension, GLTFMaterialExtrasExtension.Textures],
  56. [clearCoatTintGLTFExtension.name, clearCoatTintGLTFExtension.textures],
  57. [noiseBumpMaterialGLTFExtension.name, noiseBumpMaterialGLTFExtension.textures],
  58. [fragmentClippingGLTFExtension.name, fragmentClippingGLTFExtension.textures],
  59. // extenal plugins
  60. // AnisotropyMaterialExtension
  61. ['WEBGI_materials_anisotropy', {
  62. anisotropyDirection: 'RGB',
  63. }],
  64. // todo port
  65. // DiamondMaterialExtension
  66. // AnimationMarkersExtension
  67. // ThinFilmLayerMaterialExtension
  68. // TriplanarMappingMaterialExtension
  69. // SSBevelMaterialExtension
  70. ] as [string, Record<string, string|number>|undefined][]
  71. get gltfTransformExtensions(): (typeof Extension)[] {
  72. return [GLTFViewerConfigExtensionGP, ...this.extraExtensions.map(e => createGenericExtensionClass(e[0], e[1]))]
  73. }
  74. addExtension(name: string, textures?: Record<string, string|number>) {
  75. const ext = this.extraExtensions.findIndex(e => e[0] === name)
  76. if (ext >= 0) this.extraExtensions[ext] = [name, textures]
  77. else this.extraExtensions.push([name, textures])
  78. }
  79. /**
  80. * Note - don't remove an extension when removing a plugin.
  81. *
  82. * extensions can be removed if you don't want to save the data of some plugin when transforming glb. But since this is not desirable in most cases, it is not recommended.
  83. * @param name
  84. */
  85. removeExtension(name: string) {
  86. const ext = this.extraExtensions.findIndex(e => e[0] === name)
  87. if (ext >= 0) this.extraExtensions.splice(ext, 1)
  88. }
  89. private _lastExporter?: IExporter['ctor'] = undefined
  90. protected _ctor: IExporter['ctor'] = (_, _exporter) => {
  91. if (!this._viewer) throw new Error('Viewer not set')
  92. const tempFile = generateUUID() + '.drc' // dummy
  93. const ex = new GLTFDracoExporter({},
  94. // todo unregister on dispose
  95. this._viewer.assetManager.importer.registerFile(tempFile) as DRACOLoader2)
  96. ex.setup(this._viewer, _exporter.extensions)
  97. ex.addExtension(...this.gltfTransformExtensions)
  98. return ex
  99. }
  100. onAdded(viewer: ThreeViewer): void {
  101. super.onAdded(viewer)
  102. const exporter = viewer.assetManager.exporter
  103. let glbExporter = exporter.getExporter('glb')
  104. this._lastExporter = glbExporter?.ctor
  105. if (!glbExporter) {
  106. console.error('GLTFDracoExportPlugin: GLB exporter not found in AssetManager.exporter, creating a new one.')
  107. glbExporter = {
  108. ext: ['glb', 'gltf'],
  109. extensions: [],
  110. ctor: this._ctor,
  111. }
  112. exporter.addExporter(glbExporter)
  113. } else {
  114. glbExporter.ctor = this._ctor
  115. }
  116. // for ui. todo use viewer.forPlugin
  117. const exportPlugin = viewer.getPlugin(AssetExporterPlugin)
  118. if (exportPlugin) {
  119. Object.assign(exportPlugin.exportOptions, {
  120. compress: false,
  121. dracoOptions: {
  122. encodeSpeed: 5,
  123. method: EncoderMethod.EDGEBREAKER,
  124. quantizationVolume: 'mesh',
  125. quantizationBits: {
  126. ['POSITION']: 14,
  127. ['NORMAL']: 10,
  128. ['COLOR']: 8,
  129. ['TEX_COORD']: 12,
  130. ['GENERIC']: 12,
  131. },
  132. } as EncoderOptions,
  133. })
  134. const exportOptions = exportPlugin.uiConfig.children?.find(c => (c as UiObjectConfig).label === 'GLB Export') as UiObjectConfig
  135. if (exportOptions) {
  136. exportOptions.children = [this._makeUi(exportPlugin), ...exportOptions.children || []]
  137. } else {
  138. console.warn('GLTFDracoExportPlugin: Unable to setup UI')
  139. }
  140. }
  141. }
  142. onRemove(viewer: ThreeViewer) {
  143. const exporter = viewer.assetManager.exporter
  144. const glbExporter = exporter.getExporter('glb')
  145. if (glbExporter && this._lastExporter) {
  146. glbExporter.ctor = this._lastExporter
  147. }
  148. super.onRemove(viewer)
  149. }
  150. protected _makeUi = (exporter: AssetExporterPlugin)=>[
  151. {
  152. type: 'checkbox',
  153. label: 'DRACO Compress',
  154. property: [exporter.exportOptions, 'compress'],
  155. onChange: ()=>exporter.uiConfig.uiRefresh?.(true),
  156. },
  157. {
  158. type: 'folder',
  159. hidden: ()=>!exporter.exportOptions.compress,
  160. label: 'DRACO Options',
  161. children: [
  162. {
  163. type: 'slider',
  164. label: 'Encode Speed',
  165. bounds: [1, 10],
  166. property: [exporter.exportOptions.dracoOptions, 'encodeSpeed'],
  167. },
  168. {
  169. type: 'dropdown',
  170. label: 'Encoder Method',
  171. property: [exporter.exportOptions.dracoOptions, 'method'],
  172. children: Object.entries(EncoderMethod).map(([k, v]) => ({label: k, value: v})),
  173. },
  174. {
  175. type: 'dropdown',
  176. label: 'Quantization Volume',
  177. property: [exporter.exportOptions.dracoOptions, 'quantizationVolume'],
  178. children: ['mesh', 'scene', 'bbox'].map(v => ({label: v})),
  179. },
  180. {
  181. type: 'folder',
  182. label: 'Quantization Bits',
  183. children: Object.keys(exporter.exportOptions.dracoOptions?.quantizationBits || {}).map(k => ({
  184. type: 'slider',
  185. label: k,
  186. bounds: [1, 16],
  187. stepSize: 1,
  188. property: [exporter.exportOptions.dracoOptions?.quantizationBits, k],
  189. })),
  190. },
  191. ],
  192. },
  193. ]
  194. }