| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- import {AViewerPluginSync, ThreeViewer} from '../../viewer'
- import {GLTFLoader2} from '../../assetmanager'
- import {onChange, serialize} from 'ts-browser-helpers'
- import {IMaterial, IObject3D} from '../../core'
- import {UiObjectConfig} from 'uiconfig.js'
- import {
- GLTFMaterialsVariantsExtensionImport,
- khrMaterialsVariantsGLTF,
- } from './helpers/GLTFMaterialsVariantsExtensionImport'
- import {gltfExporterMaterialsVariantsExtensionExport} from './helpers/GLTFMaterialsVariantsExtensionExport'
-
- /**
- * GLTF khr_material_variants plugin
- *
- * This plugin allows to import and export gltf files with KHR_materials_variants extension.
- * The material data is stored in the object userData. The plugin also provides a UI to select the variant.
- *
- * @category Plugins
- */
- export class GLTFKHRMaterialVariantsPlugin extends AViewerPluginSync<''> {
- public static readonly PluginType = 'GLTFKHRMaterialVariantsPlugin'
- enabled = true
-
- constructor() {
- super()
- this._loaderCreate = this._loaderCreate.bind(this)
- }
-
- onAdded(v: ThreeViewer): void {
- super.onAdded(v)
- // v.addEventListener('preRender', this._preRender)
- v.scene.addEventListener('addSceneObject', this._objectAdded)
- v.assetManager.importer.addEventListener('loaderCreate', this._loaderCreate as any)
- v.assetManager.exporter.getExporter('gltf', 'glb')?.extensions?.push(gltfExporterMaterialsVariantsExtensionExport)
- }
-
- private _loaderCreate({loader}: {loader: GLTFLoader2}) {
- if (!loader.isGLTFLoader2) return
- loader.register((p) => new GLTFMaterialsVariantsExtensionImport(p))
- }
-
- onRemove(v: ThreeViewer): void {
- v.scene.removeEventListener('addSceneObject', this._objectAdded)
- v.assetManager.importer.removeEventListener('loaderCreate', this._loaderCreate as any)
- const exportExts = v.assetManager.exporter.getExporter('gltf', 'glb')?.extensions || []
- const i = exportExts.indexOf(gltfExporterMaterialsVariantsExtensionExport)
- if (i !== -1) exportExts.splice(i, 1)
- this.variants = {}
- return super.onRemove(v)
- }
-
-
- variants: Record<string, IObject3D[]> = {} // dont serialize this
-
- /**
- * The selected variant. Changing this will automatically apply the variant to the objects.
- */
- @onChange(GLTFKHRMaterialVariantsPlugin.prototype._variantChanged)
- @serialize()
- selectedVariant = ''
-
- /**
- * If true, the first variant will be applied to the objects when object is added and nothing is selected.
- */
- @serialize()
- applyFirstVariantOnLoad = true
-
- private _variantChanged() {
- this.applyVariant(this.selectedVariant || '', true)
- }
-
- /**
- * Apply the variant to objects.
- * It will also change the `selectedVariant` if `root` is not provided.
- * @param name
- * @param force
- * @param root
- * @param doTraverse
- */
- applyVariant(name: string, force = false, root?: IObject3D[], doTraverse = true) {
- if (!force && !root && this.selectedVariant === name) return
- if (!name) return
- if (!root) this.selectedVariant = name
- const objects = root ?
- Array.isArray(root) ? root : [root] :
- name ? this.variants[name] || [] : Object.values(this.variants).flat()
- for (const object of objects) {
- const done = new Set()
- const apply = (obj: IObject3D)=>{
- if (!obj.userData._variantMaterials || done.has(obj)) return
- const va = name ? obj.userData._variantMaterials[name]?.material : obj.userData._originalMaterial
- if (va) {
- if (!obj.userData._originalMaterial) obj.userData._originalMaterial = obj.material
- obj.material = va
- }
- done.add(obj)
- }
- if (doTraverse) object.traverse(apply)
- else apply(object)
- }
- }
-
- private _objectAdded = (ev: any)=>{
- const object = ev.object as IObject3D
- if (!object?.isObject3D) return
- if (!this._viewer) return
- object.traverse((obj)=>{
- if (obj.userData._variantMaterials) {
- for (const val of Object.values(obj.userData._variantMaterials) as any) {
- if (val?.material) val.material = this._viewer?.materialManager.convertToIMaterial(val.material, {}) || val.material
- }
- }
-
- const d = obj.userData?.__importData?.[khrMaterialsVariantsGLTF]
- if (!d) return
- const names = d.names || [] as string[]
- for (const name of names) {
- if (!this.variants[name]) this.variants[name] = []
- this.variants[name].push(obj)
- }
- delete obj.userData.__importData[khrMaterialsVariantsGLTF]
- })
- if (!this.selectedVariant && this.applyFirstVariantOnLoad) {
- this.selectedVariant = Object.keys(this.variants)[0] || ''
-
- }
- this.uiConfig.uiRefresh?.()
- return
- }
-
- uiConfig: UiObjectConfig = {
- type: 'folder',
- label: 'KHR Material Variants',
- children: [
- ()=>({
- children: [null, ...Object.keys(this.variants)].map((label) => !label ? {label: 'none', value: ''} : {label}),
- type: 'dropdown',
- label: 'Variant',
- property: [this, 'selectedVariant'],
- }),
- ],
- }
-
- }
-
- declare module './../../core/IObject'{
- interface IObject3DUserData{
- /**
- * Starts with `_` so that its not saved in gltf, but saved in json.
- */
- _variantMaterials?: Record<string, {material: IMaterial}>
- _originalMaterial?: IObject3D['material']
- }
- }
|