threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GLTFDracoExportPlugin.ts 8.0KB

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