Skip to content

Commit

Permalink
fix(InstancedUniformsMesh): reuse same derived material instance when…
Browse files Browse the repository at this point in the history
… uniform names change

Fixes #154. Adding new uniforms to the set of instanced uniforms no longer
creates a whole new derived material instance, so changes that were made
to the previous one (e.g. adding a custom onBeforeCompile) aren't lost.
  • Loading branch information
lojjic committed Sep 17, 2021
1 parent 164fb8f commit bd7cea6
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ const precededByUniformRE = /\buniform\s+(int|float|vec[234])\s+$/
const attrRefReplacer = (name, index, str) => (precededByUniformRE.test(str.substr(0, index)) ? name : `troika_attr_${name}`)
const varyingRefReplacer = (name, index, str) => (precededByUniformRE.test(str.substr(0, index)) ? name : `troika_vary_${name}`)

export function createInstancedUniformsDerivedMaterial (baseMaterial, uniformNames) {
export function createInstancedUniformsDerivedMaterial (baseMaterial) {
let _uniformNames = []
let _uniformNamesKey = ''

const derived = createDerivedMaterial(baseMaterial, {
chained: true,

defines: {
TROIKA_INSTANCED_UNIFORMS: uniformNames.sort().join('|')
},

customRewriter ({ vertexShader, fragmentShader }) {
let vertexDeclarations = []
let vertexAssignments = []
Expand All @@ -22,7 +21,7 @@ export function createInstancedUniformsDerivedMaterial (baseMaterial, uniformNam
let fragmentUniforms = getShaderUniformTypes(fragmentShader)

// Add attributes and varyings for, and rewrite references to, the instanced uniforms
uniformNames.forEach((name) => {
_uniformNames.forEach((name) => {
let vertType = vertexUniforms[name]
let fragType = fragmentUniforms[name]
if (vertType || fragType) {
Expand Down Expand Up @@ -53,6 +52,26 @@ export function createInstancedUniformsDerivedMaterial (baseMaterial, uniformNam
}
})

/**
* Update the set of uniform names that will be enabled for per-instance values. This
* can be changed dynamically after instantiation.
* @param {string[]} uniformNames
*/
derived.setUniformNames = function(uniformNames) {
_uniformNames = uniformNames || []
const key = _uniformNames.sort().join('|')
if (key !== _uniformNamesKey) {
_uniformNamesKey = key
this.needsUpdate = true
}
}

// Custom program cache key that allows for changing instanced uniforms
const baseKey = derived.customProgramCacheKey()
derived.customProgramCacheKey = function() {
return baseKey + '|' + _uniformNamesKey
}

derived.isInstancedUniformsMaterial = true
return derived
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,15 @@ export class InstancedUniformsMesh extends InstancedMesh {
get material () {
let derivedMaterial = this._derivedMaterial
const baseMaterial = this._baseMaterial || this._defaultMaterial || (this._defaultMaterial = new MeshBasicMaterial())
const uniformNames = this._instancedUniformNames
if (!derivedMaterial || derivedMaterial.baseMaterial !== baseMaterial || derivedMaterial._instancedUniformNames !== uniformNames) {
derivedMaterial = this._derivedMaterial = createInstancedUniformsDerivedMaterial(baseMaterial, uniformNames)
derivedMaterial._instancedUniformNames = uniformNames
if (!derivedMaterial || derivedMaterial.baseMaterial !== baseMaterial) {
derivedMaterial = this._derivedMaterial = createInstancedUniformsDerivedMaterial(baseMaterial)
// dispose the derived material when its base material is disposed:
baseMaterial.addEventListener('dispose', function onDispose () {
baseMaterial.removeEventListener('dispose', onDispose)
derivedMaterial.dispose()
})
}
derivedMaterial.setUniformNames(this._instancedUniformNames)
return derivedMaterial
}

Expand Down
2 changes: 1 addition & 1 deletion packages/troika-three-utils/src/DerivedMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function createDerivedMaterial(baseMaterial, options) {
baseMaterial.onBeforeCompile.call(this, shaderInfo)

// Upgrade the shaders, caching the result by incoming source code
const cacheKey = optionsKey + '|||' + shaderInfo.vertexShader + '|||' + shaderInfo.fragmentShader
const cacheKey = this.customProgramCacheKey() + '|' + shaderInfo.vertexShader + '|' + shaderInfo.fragmentShader
let upgradedShaders = SHADER_UPGRADE_CACHE[cacheKey]
if (!upgradedShaders) {
const upgraded = upgradeShaders(shaderInfo, options, optionsKey)
Expand Down

0 comments on commit bd7cea6

Please sign in to comment.