Browse Source

Add events to materials - addToMesh, removeFromMesh, Add hooks to MaterialExtension - onMaterialUpdate, onAddToMesh, onRemoveFromMesh, onRegister, onUnregister. Add KTX2LoadPlugin.SAVE_SOURCE_BLOBS and disabled by default.

master
Palash Bansal 1 year ago
parent
commit
e9e2060f33
No account linked to committer's email address

+ 2
- 1
examples/tweakpane-editor/script.ts View File

GLTFMeshOptDecodePlugin, GLTFMeshOptDecodePlugin,
HalfFloatType, HalfFloatType,
HDRiGroundPlugin, HDRiGroundPlugin,
HemisphereLight,
InteractionPromptPlugin, InteractionPromptPlugin,
KTX2LoadPlugin, KTX2LoadPlugin,
KTXLoadPlugin, KTXLoadPlugin,
TransfrSharePlugin, TransfrSharePlugin,
]) ])


KTX2LoadPlugin.SAVE_SOURCE_BLOBS = true // so that ktx files can be exported. todo - add this to blueprint editor init as well

// to show more details in the UI and allow to edit changes in title etc. // to show more details in the UI and allow to edit changes in title etc.
viewer.getPlugin(MaterialConfiguratorPlugin)!.enableEditContextMenus = true viewer.getPlugin(MaterialConfiguratorPlugin)!.enableEditContextMenus = true
viewer.getPlugin(SwitchNodePlugin)!.enableEditContextMenus = true viewer.getPlugin(SwitchNodePlugin)!.enableEditContextMenus = true

+ 15
- 0
src/core/IMaterial.ts View File

geometry: BufferGeometry geometry: BufferGeometry
object: Object3D object: Object3D
} }
/**
* Fires when the material is set/added to a mesh
* This is applicable of all types of Object3D, like Line etc, not just Mesh
*/
addToMesh: {
object: Object3D
}
/**
* Fires when the material is changed/removed to a mesh
* This is applicable of all types of Object3D, like Line etc, not just Mesh
*/
removeFromMesh: {
object: Object3D
}
/** /**
* For internal use * For internal use
*/ */
declare module 'three'{ declare module 'three'{
export interface MaterialEventMap{ export interface MaterialEventMap{
materialUpdate: { materialUpdate: {
// These are handled in dispatchEvent override in iMaterialCommons
bubbleToObject?: boolean bubbleToObject?: boolean
bubbleToParent?: boolean bubbleToParent?: boolean
uiChangeEvent?: ChangeEvent uiChangeEvent?: ChangeEvent

+ 1
- 0
src/core/ITexture.ts View File



source: Source & { source: Source & {
_sourceImgBuffer?: ArrayBuffer|Uint8Array // see KTX2LoadPlugin and serializeTextureInExtras _sourceImgBuffer?: ArrayBuffer|Uint8Array // see KTX2LoadPlugin and serializeTextureInExtras
_canSerialize?: boolean // see KTX2LoadPlugin and GLTFExporter.js. Disables auto decompress for glTF export
} }


_appliedMaterials?: Set<IMaterial> // for internal use only. refers to the materials that this texture is applied to _appliedMaterials?: Set<IMaterial> // for internal use only. refers to the materials that this texture is applied to

+ 31
- 6
src/core/object/iObjectCommons.ts View File

// Remove old material listeners // Remove old material listeners
const oldMats = this.material const oldMats = this.material
const mats = Array.isArray(oldMats) ? [...oldMats] : [oldMats!] const mats = Array.isArray(oldMats) ? [...oldMats] : [oldMats!]

let removed = []
const added = []

for (const mat of mats) { for (const mat of mats) {
if (!mat) continue if (!mat) continue
if (mat.appliedMeshes) {
mat.appliedMeshes.delete(this)
// if (mat.userData && mat.appliedMeshes?.size === 0 && mat.userData.disposeOnIdle !== false)
mat.dispose(false) // this will dispose textures(if they are idle) if the material is registered in the material manager
}
removed.push(mat)
// if (mat.appliedMeshes) {
// mat.appliedMeshes.delete(this)
// // if (mat.userData && mat.appliedMeshes?.size === 0 && mat.userData.disposeOnIdle !== false)
// mat.dispose(false) // this will dispose textures(if they are idle) if the material is registered in the material manager
// }
} }


const materials = [] const materials = []
console.warn('Upgrading Material', mat) console.warn('Upgrading Material', mat)
iMaterialCommons.upgradeMaterial.call(mat) iMaterialCommons.upgradeMaterial.call(mat)
} }
if (removed.includes(mat)) removed = removed.filter(m=>m !== mat)
else added.push(mat)
materials.push(mat) materials.push(mat)
if (mat && mat.appliedMeshes) {
// if (mat && mat.appliedMeshes) {
// mat.appliedMeshes.add(this)
// }
}

// todo should these be before or after `materialChanged` event? right now its before, also .material will return the old one since _currentMaterial is old
for (const mat of removed) {
if (mat.appliedMeshes) {
mat.appliedMeshes.delete(this)
// if (mat.userData && mat.appliedMeshes?.size === 0 && mat.userData.disposeOnIdle !== false)
mat.dispose(false) // this will dispose textures(if they are idle) if the material is registered in the material manager
}
mat.dispatchEvent({type: 'removeFromMesh', object: this})
}
for (const mat of added) {
if (mat.appliedMeshes) {
mat.appliedMeshes.add(this) mat.appliedMeshes.add(this)
} }
mat.dispatchEvent({type: 'addToMesh', object: this})
// note - material bubbleToObject is handled in dispatchEvent override in iMaterialCommons
} }

this._currentMaterial = !materials.length ? null : materials.length !== 1 ? materials : materials[0] || null this._currentMaterial = !materials.length ? null : materials.length !== 1 ? materials : materials[0] || null


this.dispatchEvent({type: 'materialChanged', material: this._currentMaterial ?? null, oldMaterial: oldMats ?? null, object: this, bubbleToParent: true}) this.dispatchEvent({type: 'materialChanged', material: this._currentMaterial ?? null, oldMaterial: oldMats ?? null, object: this, bubbleToParent: true})

+ 56
- 3
src/materials/MaterialExtender.ts View File

updateMaterialDefines(materialExtension.extraDefines, material) updateMaterialDefines(materialExtension.extraDefines, material)


// Call shaderExtender if defined // Call shaderExtender if defined
materialExtension.shaderExtender?.(shader as any, material, renderer)
materialExtension.shaderExtender && materialExtension.shaderExtender(shader as any, material, renderer)
// Save last shader so that it can be used to check if shader has changed in extensions // Save last shader so that it can be used to check if shader has changed in extensions
material.lastShader = shader material.lastShader = shader
} }
(material as any).__ext_afterRenderListen = true (material as any).__ext_afterRenderListen = true
material.addEventListener('afterRender', materialAfterRender) material.addEventListener('afterRender', materialAfterRender)
} }
if (!(material as any).__ext_addToMeshListen) {
(material as any).__ext_addToMeshListen = true
material.addEventListener('addToMesh', materialAddToMesh)
}
if (!(material as any).__ext_removeFromMeshListen) {
(material as any).__ext_removeFromMeshListen = true
material.addEventListener('removeFromMesh', materialRemovedFromMesh)
}
if (!(material as any).__ext_materialUpdateListen) {
(material as any).__ext_materialUpdateListen = true
material.addEventListener('materialUpdate', materialUpdate)
}


for (const ext of exts) {
ext.onRegister && ext.onRegister(material)
}
material.needsUpdate = true material.needsUpdate = true
return exts return exts
} }
static UnregisterExtensions(material: IMaterial, customMaterialExtensions?: MaterialExtension[]) { static UnregisterExtensions(material: IMaterial, customMaterialExtensions?: MaterialExtension[]) {
if (customMaterialExtensions) { if (customMaterialExtensions) {
material.materialExtensions = material.materialExtensions?.filter((v)=>!customMaterialExtensions.includes(v)) || [] material.materialExtensions = material.materialExtensions?.filter((v)=>!customMaterialExtensions.includes(v)) || []
for (const ext of customMaterialExtensions) {
ext.onUnregister && ext.onUnregister(material)
}
} }

if (!material.materialExtensions?.length) { if (!material.materialExtensions?.length) {
material.removeEventListener('beforeRender', materialBeforeRender) material.removeEventListener('beforeRender', materialBeforeRender)
material.removeEventListener('afterRender', materialAfterRender) material.removeEventListener('afterRender', materialAfterRender)
material.removeEventListener('addToMesh', materialAddToMesh)
material.removeEventListener('removeFromMesh', materialRemovedFromMesh)
material.removeEventListener('materialUpdate', materialUpdate)

;(material as any).__ext_beforeRenderListen = false ;(material as any).__ext_beforeRenderListen = false
;(material as any).__ext_afterRenderListen = false ;(material as any).__ext_afterRenderListen = false
;(material as any).__ext_addToMeshListen = false
;(material as any).__ext_removeFromMeshListen = false
;(material as any).__ext_materialUpdateListen = false
} }
} }
} }
if (!material || !object || !renderer) throw new Error('Invalid material, object or renderer') if (!material || !object || !renderer) throw new Error('Invalid material, object or renderer')
if (!material.materialExtensions) return if (!material.materialExtensions) return
for (const value of material.materialExtensions) { for (const value of material.materialExtensions) {
value.onObjectRender?.(object, material, renderer)
value.onObjectRender && value.onObjectRender(object, material, renderer)


if ((material as any).lastShader) { if ((material as any).lastShader) {
const updater = getOrCall(value.updaters) || [] const updater = getOrCall(value.updaters) || []
if (!material || !object || !renderer) throw new Error('Invalid material, object or renderer') if (!material || !object || !renderer) throw new Error('Invalid material, object or renderer')
if (!material.materialExtensions) return if (!material.materialExtensions) return
for (const value of material.materialExtensions) { for (const value of material.materialExtensions) {
value.onAfterRender?.(object, material, renderer)
value.onAfterRender && value.onAfterRender(object, material, renderer)
}
}

function materialAddToMesh({target, object}:{object?: Object3D, target: IMaterial}) {
const material = target
if (!material || !object) throw new Error('Invalid material or object')
if (!material.materialExtensions) return
for (const value of material.materialExtensions) {
value.onAddToMesh && value.onAddToMesh(object, material)
}
}

function materialRemovedFromMesh({target, object}:{object?: Object3D, target: IMaterial}) {
const material = target
if (!material || !object) throw new Error('Invalid material or object')
if (!material.materialExtensions) return
for (const value of material.materialExtensions) {
value.onRemoveFromMesh && value.onRemoveFromMesh(object, material)
}
}

function materialUpdate({target}:{target: IMaterial}) {
const material = target
if (!material) throw new Error('Invalid material')
if (!material.materialExtensions) return
for (const value of material.materialExtensions) {
value.onMaterialUpdate && value.onMaterialUpdate(material)
} }
} }



+ 14
- 2
src/plugins/import/KTX2LoadPlugin.ts View File



public static TRANSCODER_LIBRARY_PATH = 'https://cdn.jsdelivr.net/gh/BinomialLLC/basis_universal@1.16.4/webgl/transcoder/build/' public static TRANSCODER_LIBRARY_PATH = 'https://cdn.jsdelivr.net/gh/BinomialLLC/basis_universal@1.16.4/webgl/transcoder/build/'


/**
* Flag to save the source buffer data in the texture object, it can be used later when downloading/serializing
* the texture like when downloading glb with embedded textures.
*/
public static SAVE_SOURCE_BLOBS = false

onAdded(viewer: ThreeViewer) { onAdded(viewer: ThreeViewer) {
this._importer.onCtor = (l: KTX2Loader2) => l this._importer.onCtor = (l: KTX2Loader2) => l
.setTranscoderPath(KTX2LoadPlugin.TRANSCODER_LIBRARY_PATH) .setTranscoderPath(KTX2LoadPlugin.TRANSCODER_LIBRARY_PATH)
} }


export class KTX2Loader2 extends KTX2Loader implements ILoader { export class KTX2Loader2 extends KTX2Loader implements ILoader {

private _initTexture(t: CompressedTexture & ITexture) { private _initTexture(t: CompressedTexture & ITexture) {
upgradeTexture.call(t) upgradeTexture.call(t)
t.userData.mimeType = 'image/ktx2' t.userData.mimeType = 'image/ktx2'
return t return t
} }
async createTexture(buffer: ArrayBuffer, config: any): Promise<CompressedTexture> { async createTexture(buffer: ArrayBuffer, config: any): Promise<CompressedTexture> {
const buffer2 = new Uint8Array(buffer.slice(0)) // clones the buffer
const buffer2 = KTX2LoadPlugin.SAVE_SOURCE_BLOBS ? new Uint8Array(buffer.slice(0)) : undefined // clones the buffer
const texture = (await super.createTexture(buffer, config)) as CompressedTexture & ITexture const texture = (await super.createTexture(buffer, config)) as CompressedTexture & ITexture
texture.source._sourceImgBuffer = buffer2 // keep the same buffer when cloned and all, used in serializeTextureInExtras
// todo check if rootPath is set?
if (KTX2LoadPlugin.SAVE_SOURCE_BLOBS && buffer2) {
texture.source._sourceImgBuffer = buffer2 // keep the same buffer when cloned and all, used in serializeTextureInExtras
texture.source._canSerialize = true
}
this._initTexture(texture) this._initTexture(texture)
return texture return texture
} }


} }



export const KHR_TEXTURE_BASISU = 'KHR_texture_basisu' export const KHR_TEXTURE_BASISU = 'KHR_texture_basisu'


const glTFTextureBasisUExtensionExport = (w: GLTFWriter2)=> ({ const glTFTextureBasisUExtensionExport = (w: GLTFWriter2)=> ({

Loading…
Cancel
Save