threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

GLTFKHRMaterialVariantsPlugin.ts 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import {AViewerPluginSync, ThreeViewer} from '../../viewer'
  2. import {GLTFLoader2} from '../../assetmanager'
  3. import {onChange, serialize} from 'ts-browser-helpers'
  4. import {IMaterial, IObject3D} from '../../core'
  5. import {UiObjectConfig} from 'uiconfig.js'
  6. import {
  7. GLTFMaterialsVariantsExtensionImport,
  8. khrMaterialsVariantsGLTF,
  9. } from './helpers/GLTFMaterialsVariantsExtensionImport'
  10. import {gltfExporterMaterialsVariantsExtensionExport} from './helpers/GLTFMaterialsVariantsExtensionExport'
  11. /**
  12. * GLTF khr_material_variants plugin
  13. *
  14. * This plugin allows to import and export gltf files with KHR_materials_variants extension.
  15. * The material data is stored in the object userData. The plugin also provides a UI to select the variant.
  16. *
  17. * @category Plugins
  18. */
  19. export class GLTFKHRMaterialVariantsPlugin extends AViewerPluginSync<''> {
  20. public static readonly PluginType = 'GLTFKHRMaterialVariantsPlugin'
  21. enabled = true
  22. constructor() {
  23. super()
  24. this._loaderCreate = this._loaderCreate.bind(this)
  25. }
  26. onAdded(v: ThreeViewer): void {
  27. super.onAdded(v)
  28. // v.addEventListener('preRender', this._preRender)
  29. v.scene.addEventListener('addSceneObject', this._objectAdded)
  30. v.assetManager.importer.addEventListener('loaderCreate', this._loaderCreate as any)
  31. v.assetManager.exporter.getExporter('gltf', 'glb')?.extensions?.push(gltfExporterMaterialsVariantsExtensionExport)
  32. }
  33. private _loaderCreate({loader}: {loader: GLTFLoader2}) {
  34. if (!loader.isGLTFLoader2) return
  35. loader.register((p) => new GLTFMaterialsVariantsExtensionImport(p))
  36. }
  37. onRemove(v: ThreeViewer): void {
  38. v.scene.removeEventListener('addSceneObject', this._objectAdded)
  39. v.assetManager.importer.removeEventListener('loaderCreate', this._loaderCreate as any)
  40. const exportExts = v.assetManager.exporter.getExporter('gltf', 'glb')?.extensions || []
  41. const i = exportExts.indexOf(gltfExporterMaterialsVariantsExtensionExport)
  42. if (i !== -1) exportExts.splice(i, 1)
  43. this.variants = {}
  44. return super.onRemove(v)
  45. }
  46. variants: Record<string, IObject3D[]> = {} // dont serialize this
  47. /**
  48. * The selected variant. Changing this will automatically apply the variant to the objects.
  49. */
  50. @onChange(GLTFKHRMaterialVariantsPlugin.prototype._variantChanged)
  51. @serialize()
  52. selectedVariant = ''
  53. /**
  54. * If true, the first variant will be applied to the objects when object is added and nothing is selected.
  55. */
  56. @serialize()
  57. applyFirstVariantOnLoad = true
  58. private _variantChanged() {
  59. this.applyVariant(this.selectedVariant || '', true)
  60. }
  61. /**
  62. * Apply the variant to objects.
  63. * It will also change the `selectedVariant` if `root` is not provided.
  64. * @param name
  65. * @param force
  66. * @param root
  67. * @param doTraverse
  68. */
  69. applyVariant(name: string, force = false, root?: IObject3D[], doTraverse = true) {
  70. if (!force && !root && this.selectedVariant === name) return
  71. if (!name) return
  72. if (!root) this.selectedVariant = name
  73. const objects = root ?
  74. Array.isArray(root) ? root : [root] :
  75. name ? this.variants[name] || [] : Object.values(this.variants).flat()
  76. for (const object of objects) {
  77. const done = new Set()
  78. const apply = (obj: IObject3D)=>{
  79. if (!obj.userData._variantMaterials || done.has(obj)) return
  80. const va = name ? obj.userData._variantMaterials[name]?.material : obj.userData._originalMaterial
  81. if (va) {
  82. if (!obj.userData._originalMaterial) obj.userData._originalMaterial = obj.material
  83. obj.material = va
  84. }
  85. done.add(obj)
  86. }
  87. if (doTraverse) object.traverse(apply)
  88. else apply(object)
  89. }
  90. }
  91. private _objectAdded = (ev: any)=>{
  92. const object = ev.object as IObject3D
  93. if (!object?.isObject3D) return
  94. if (!this._viewer) return
  95. object.traverse((obj)=>{
  96. if (obj.userData._variantMaterials) {
  97. for (const val of Object.values(obj.userData._variantMaterials) as any) {
  98. if (val?.material) val.material = this._viewer?.materialManager.convertToIMaterial(val.material, {}) || val.material
  99. }
  100. }
  101. const d = obj.userData?.__importData?.[khrMaterialsVariantsGLTF]
  102. if (!d) return
  103. const names = d.names || [] as string[]
  104. for (const name of names) {
  105. if (!this.variants[name]) this.variants[name] = []
  106. this.variants[name].push(obj)
  107. }
  108. delete obj.userData.__importData[khrMaterialsVariantsGLTF]
  109. })
  110. if (!this.selectedVariant && this.applyFirstVariantOnLoad) {
  111. this.selectedVariant = Object.keys(this.variants)[0] || ''
  112. }
  113. this.uiConfig.uiRefresh?.()
  114. return
  115. }
  116. uiConfig: UiObjectConfig = {
  117. type: 'folder',
  118. label: 'KHR Material Variants',
  119. children: [
  120. ()=>({
  121. children: [null, ...Object.keys(this.variants)].map((label) => !label ? {label: 'none', value: ''} : {label}),
  122. type: 'dropdown',
  123. label: 'Variant',
  124. property: [this, 'selectedVariant'],
  125. }),
  126. ],
  127. }
  128. }
  129. declare module './../../core/IObject'{
  130. interface IObject3DUserData{
  131. /**
  132. * Starts with `_` so that its not saved in gltf, but saved in json.
  133. */
  134. _variantMaterials?: Record<string, {material: IMaterial}>
  135. _originalMaterial?: IObject3D['material']
  136. }
  137. }