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.

GLTFSpecGlossinessConverterPlugin.ts 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import {AViewerPluginSync, DRACOLoader2, GLTFBinaryExtension, GLTFLoader2, GLTFParser, ThreeViewer} from 'threepipe'
  2. import {Extension, WebIO} from '@gltf-transform/core'
  3. import {ALL_EXTENSIONS} from '@gltf-transform/extensions'
  4. import {GLTFDracoExportPlugin} from './GLTFDracoExportPlugin'
  5. import {metalRough} from '@gltf-transform/functions'
  6. /**
  7. * GLTFSpecGlossinessConverterPlugin
  8. *
  9. * Plugin that adds a gltf loader extension that automatically converts GLTF files with specular glossiness materials ([KHR_materials_pbrSpecularGlossiness](https://kcoley.github.io/glTF/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/)) to metallic roughness during import.
  10. *
  11. * To use this plugin, simply add it to the viewer and import a file with specular glossiness materials.
  12. * If `confirm` is set to true, a confirmation dialog will be shown before the conversion.
  13. */
  14. export class GLTFSpecGlossinessConverterPlugin extends AViewerPluginSync {
  15. enabled = true
  16. static readonly PluginType = 'GLTFSpecGlossinessConverterPlugin'
  17. toJSON: any = undefined
  18. // dependencies = [GLTFDracoExportPlugin]
  19. /**
  20. * Whether to show the confirmation dialog when loading a GLTF file with specular glossiness materials.
  21. * If set to false, the conversion will be done automatically without confirmation.
  22. * To disable the conversion entirely, disable the plugin.
  23. */
  24. confirm = true
  25. /**
  26. * The message to show in the confirmation dialog when loading a GLTF file with specular glossiness materials.
  27. */
  28. confirmMessage = 'GLTF Load: This file includes specular glossiness materials, do you want to convert it to metallic roughness before load?'
  29. private _loaderCreate({loader}: {loader: GLTFLoader2}) {
  30. if (!loader.isGLTFLoader2) return
  31. const ep = this._viewer?.getPlugin(GLTFDracoExportPlugin)
  32. if (!ep) {
  33. console.error('GLTFSpecGlossinessConverterPlugin requires GLTFDracoExportPlugin in the plugin for extra extensions')
  34. }
  35. loader.register(gltfKhrPbrSpecularGlossinessConverter(async(m)=>!this.enabled ? false : !this.confirm ? true : this._viewer?.dialog.confirm(this.confirmMessage || m) ?? true, ep?.gltfTransformExtensions))
  36. }
  37. constructor() {
  38. super()
  39. this._loaderCreate = this._loaderCreate.bind(this)
  40. }
  41. onAdded(v: ThreeViewer) {
  42. super.onAdded(v)
  43. v.assetManager.importer.addEventListener('loaderCreate', this._loaderCreate as any)
  44. }
  45. onRemove(v: ThreeViewer) {
  46. v.assetManager.importer.removeEventListener('loaderCreate', this._loaderCreate as any)
  47. return super.onRemove(v)
  48. }
  49. }
  50. export const gltfKhrPbrSpecularGlossinessConverter = (confirm?: (s: string)=>Promise<boolean>, extensions?: (typeof Extension)[])=>(parser: GLTFParser)=>({
  51. name: 'KHR_materials_pbrSpecularGlossiness',
  52. beforeRoot: async() => {
  53. try {
  54. if (!parser.json.extensionsUsed || !parser.json.extensionsUsed.includes('KHR_materials_pbrSpecularGlossiness')) return
  55. const doConfirm = parser.importOptions?.confirmSpecGlossConversion ?? true
  56. if (doConfirm && confirm && !await confirm('Convert KHR_materials_pbrSpecularGlossiness to KHR_materials_pbrMetallicRoughness?')) return
  57. const json = parser.json
  58. const io = new WebIO().registerExtensions(ALL_EXTENSIONS).registerExtensions(extensions ?? [])
  59. if (parser.extensions.KHR_draco_mesh_compression) {
  60. const dracoLoader = parser.extensions.KHR_draco_mesh_compression.dracoLoader as DRACOLoader2
  61. if (!dracoLoader.isDRACOLoader2) {
  62. console.error('gltfKhrPbrSpecularGlossinessConverter: DRACOLoader2 required')
  63. return
  64. }
  65. const decoder = await dracoLoader.initDecoder()
  66. io.registerDependencies({
  67. // 'draco3d.encoder': libs[0],
  68. ['draco3d.decoder']: decoder,
  69. })
  70. }
  71. const document = await io.readJSON({
  72. json, resources: {
  73. ['@glb.bin']: new Uint8Array(parser.extensions.KHR_binary_glTF?.body),
  74. },
  75. })
  76. // Convert materials.
  77. await document.transform(metalRough())
  78. // no need to compress
  79. if (parser.extensions.KHR_draco_mesh_compression) {
  80. document.getRoot().listExtensionsUsed().find(e => e.extensionName === 'KHR_draco_mesh_compression')?.dispose()
  81. }
  82. // Write back to GLB.
  83. const res = await io.writeBinary(document)
  84. const binaryExtension = new GLTFBinaryExtension(res.buffer as any)
  85. parser.extensions.KHR_binary_glTF = binaryExtension
  86. parser.json = JSON.parse(binaryExtension.content)
  87. // this doesn't work for some reason
  88. // const res = await io.writeJSON(document, {format: Format.GLB})
  89. // parser.extensions.KHR_binary_glTF.data = res.resources['@glb.bin'].buffer as any
  90. // parser.extensions.KHR_binary_glTF.content = res.json
  91. // parser.json = res.json
  92. } catch (e) {
  93. console.error(e)
  94. return
  95. }
  96. },
  97. })
  98. declare module 'threepipe'{
  99. interface ImportAddOptions{
  100. /**
  101. * Whether to confirm the conversion of specular glossiness materials to metallic roughness.
  102. * @default true
  103. */
  104. confirmSpecGlossConversion?: boolean
  105. }
  106. }