diff --git a/.vscode/settings.json b/.vscode/settings.json index f77d231e7f5..0408b36ec44 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "**/.rollup.cache": true, "**/packages/dev/**/Shaders/**/*.ts": true, "**/packages/dev/**/shaders/**/*.ts": true, + "**/packages/dev/**/ShadersWGSL/**/*.ts": true, "**/*.fragment.ts": true, "**/*.vertex.ts": true, "**/*.compute.ts": true, diff --git a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts index d34c362106a..ba59397bee3 100644 --- a/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts +++ b/packages/dev/core/src/Engines/WebGPU/webgpuShaderProcessorsWGSL.ts @@ -239,8 +239,15 @@ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { return texture; } - public postProcessor(code: string) { - return code; + public postProcessor(code: string, defines: string[]) { + const defineToValue: { [key: string]: string } = {}; + for (const define of defines) { + const parts = define.split(/ +/); + defineToValue[parts[1]] = parts.length > 2 ? parts[2] : ""; + } + return code.replace(/\$(\w+)\$/g, (_, p1) => { + return defineToValue[p1] ?? p1; + }); } public finalizeShaders(vertexCode: string, fragmentCode: string): { vertexCode: string; fragmentCode: string } { diff --git a/packages/dev/core/src/Materials/materialHelper.ts b/packages/dev/core/src/Materials/materialHelper.ts index 14b5e6dd517..4e0b287e08a 100644 --- a/packages/dev/core/src/Materials/materialHelper.ts +++ b/packages/dev/core/src/Materials/materialHelper.ts @@ -222,8 +222,8 @@ export class MaterialHelper { defines["MORPHTARGETS_UV"] = manager.supportsUVs && defines["UV1"]; defines["MORPHTARGETS_TANGENT"] = manager.supportsTangents && defines["TANGENT"]; defines["MORPHTARGETS_NORMAL"] = manager.supportsNormals && defines["NORMAL"]; - defines["MORPHTARGETS"] = manager.numInfluencers > 0; - defines["NUM_MORPH_INFLUENCERS"] = manager.numInfluencers; + defines["NUM_MORPH_INFLUENCERS"] = manager.numMaxInfluencers || manager.numInfluencers; + defines["MORPHTARGETS"] = defines["NUM_MORPH_INFLUENCERS"] > 0; defines["MORPHTARGETS_TEXTURE"] = manager.isUsingTextureForTargets; } else { @@ -700,6 +700,7 @@ export class MaterialHelper { if (defines["NUM_MORPH_INFLUENCERS"]) { uniformsList.push("morphTargetInfluences"); + uniformsList.push("morphTargetCount"); } if (defines["BAKED_VERTEX_ANIMATION_TEXTURE"]) { @@ -815,7 +816,7 @@ export class MaterialHelper { /** * Prepares the list of attributes required for baked vertex animations according to the effect defines. * @param attribs The current list of supported attribs - * @param mesh The mesh to prepare the morph targets attributes for + * @param mesh The mesh to prepare for baked vertex animations * @param defines The current Defines of the effect */ public static PrepareAttributesForBakedVertexAnimation(attribs: string[], mesh: AbstractMesh, defines: any): void { diff --git a/packages/dev/core/src/Morph/morphTargetManager.ts b/packages/dev/core/src/Morph/morphTargetManager.ts index 875e5cd97fa..abf18e576ef 100644 --- a/packages/dev/core/src/Morph/morphTargetManager.ts +++ b/packages/dev/core/src/Morph/morphTargetManager.ts @@ -108,6 +108,29 @@ export class MorphTargetManager implements IDisposable { } } + private _numMaxInfluencers = 0; + + /** + * Gets or sets the maximum number of influencers (targets) (default value: 0). + * Setting a value for this property can lead to a smoother experience, as only one shader will be compiled, which will use this value as the maximum number of influencers. + * If you leave the value at 0 (default), a new shader will be compiled every time the number of active influencers changes. This can cause problems, as compiling a shader takes time. + * If you assign a non-zero value to this property, you need to ensure that this value is greater than the maximum number of (active) influencers you'll need for this morph manager. + * Otherwise, the number of active influencers will be truncated at the value you set for this property, which can lead to unexpected results. + * Note that this property has no effect if "useTextureToStoreTargets" is false. + */ + public get numMaxInfluencers(): number { + return this._numMaxInfluencers; + } + + public set numMaxInfluencers(value: number) { + if (this._numMaxInfluencers === value) { + return; + } + + this._numMaxInfluencers = value; + this._syncActiveTargets(true); + } + /** * Gets the unique ID of this manager */ @@ -252,6 +275,7 @@ export class MorphTargetManager implements IDisposable { effect.setFloat3("morphTargetTextureInfo", this._textureVertexStride, this._textureWidth, this._textureHeight); effect.setFloatArray("morphTargetTextureIndices", this._morphTargetTextureIndices); effect.setTexture("morphTargets", this._targetStoreTexture); + effect.setInt("morphTargetCount", this.numInfluencers); } /** @@ -365,7 +389,7 @@ export class MorphTargetManager implements IDisposable { return; } - if (this.isUsingTextureForTargets && this._vertexCount) { + if (this.isUsingTextureForTargets && (this._vertexCount || this.numMaxInfluencers > 0)) { this._textureVertexStride = 1; if (this._supportsNormals) { @@ -380,7 +404,7 @@ export class MorphTargetManager implements IDisposable { this._textureVertexStride++; } - this._textureWidth = this._vertexCount * this._textureVertexStride; + this._textureWidth = this._vertexCount * this._textureVertexStride || 1; this._textureHeight = 1; const maxTextureSize = this._scene.getEngine().getCaps().maxTextureSize; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertex.fx b/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertex.fx index fda894fae08..15267e5c350 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertex.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertex.fx @@ -1,21 +1,27 @@ #ifdef MORPHTARGETS - #ifdef MORPHTARGETS_TEXTURE - vertexID = float(gl_VertexID) * morphTargetTextureInfo.x; - positionUpdated += (readVector3FromRawSampler({X}, vertexID) - position) * morphTargetInfluences[{X}]; - vertexID += 1.0; - - #ifdef MORPHTARGETS_NORMAL - normalUpdated += (readVector3FromRawSampler({X}, vertexID) - normal) * morphTargetInfluences[{X}]; - vertexID += 1.0; - #endif + #ifdef MORPHTARGETS_TEXTURE + #if {X} == 0 + for (int i = 0; i < NUM_MORPH_INFLUENCERS; i++) { + if (i >= morphTargetCount) break; - #ifdef MORPHTARGETS_UV - uvUpdated += (readVector3FromRawSampler({X}, vertexID).xy - uv) * morphTargetInfluences[{X}]; + vertexID = float(gl_VertexID) * morphTargetTextureInfo.x; + positionUpdated += (readVector3FromRawSampler(i, vertexID) - position) * morphTargetInfluences[i]; vertexID += 1.0; - #endif + + #ifdef MORPHTARGETS_NORMAL + normalUpdated += (readVector3FromRawSampler(i, vertexID) - normal) * morphTargetInfluences[i]; + vertexID += 1.0; + #endif - #ifdef MORPHTARGETS_TANGENT - tangentUpdated.xyz += (readVector3FromRawSampler({X}, vertexID) - tangent.xyz) * morphTargetInfluences[{X}]; + #ifdef MORPHTARGETS_UV + uvUpdated += (readVector3FromRawSampler(i, vertexID).xy - uv) * morphTargetInfluences[i]; + vertexID += 1.0; + #endif + + #ifdef MORPHTARGETS_TANGENT + tangentUpdated.xyz += (readVector3FromRawSampler(i, vertexID) - tangent.xyz) * morphTargetInfluences[i]; + #endif + } #endif #else positionUpdated += (position{X} - position) * morphTargetInfluences[{X}]; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertexDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertexDeclaration.fx index 2b6dc40b0cd..d43ac95ebaf 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertexDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/morphTargetsVertexDeclaration.fx @@ -13,5 +13,7 @@ #ifdef MORPHTARGETS_UV attribute vec2 uv_{X}; #endif + #elif {X} == 0 + uniform int morphTargetCount; #endif #endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx index 597f07a2827..128c3a42553 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertex.fx @@ -1,21 +1,29 @@ #ifdef MORPHTARGETS - #ifdef MORPHTARGETS_TEXTURE - vertexID = f32(vertexInputs.vertexIndex) * uniforms.morphTargetTextureInfo.x; - positionUpdated = positionUpdated + (readVector3FromRawSampler({X}, vertexID) - vertexInputs.position) * uniforms.morphTargetInfluences[{X}]; - vertexID = vertexID + 1.0; - - #ifdef MORPHTARGETS_NORMAL - normalUpdated = normalUpdated + (readVector3FromRawSampler({X}, vertexID) - vertexInputs.normal) * uniforms.morphTargetInfluences[{X}]; - vertexID = vertexID + 1.0; - #endif + #ifdef MORPHTARGETS_TEXTURE + #if {X} == 0 + for (var i = 0; i < $NUM_MORPH_INFLUENCERS$; i = i + 1) { + if (i >= uniforms.morphTargetCount) { + break; + } - #ifdef MORPHTARGETS_UV - uvUpdated = uvUpdated + (readVector3FromRawSampler({X}, vertexID).xy - vertexInputs.uv) * uniforms.morphTargetInfluences[{X}]; - vertexID = vertexID + 1.0; - #endif + vertexID = f32(vertexInputs.vertexIndex) * uniforms.morphTargetTextureInfo.x; + positionUpdated = positionUpdated + (readVector3FromRawSampler({X}, vertexID) - vertexInputs.position) * uniforms.morphTargetInfluences[{X}]; + vertexID = vertexID + 1.0; + + #ifdef MORPHTARGETS_NORMAL + normalUpdated = normalUpdated + (readVector3FromRawSampler({X}, vertexID) - vertexInputs.normal) * uniforms.morphTargetInfluences[{X}]; + vertexID = vertexID + 1.0; + #endif - #ifdef MORPHTARGETS_TANGENT - tangentUpdated.xyz = tangentUpdated.xyz + (readVector3FromRawSampler({X}, vertexID) - vertexInputs.tangent.xyz) * uniforms.morphTargetInfluences[{X}]; + #ifdef MORPHTARGETS_UV + uvUpdated = uvUpdated + (readVector3FromRawSampler({X}, vertexID).xy - vertexInputs.uv) * uniforms.morphTargetInfluences[{X}]; + vertexID = vertexID + 1.0; + #endif + + #ifdef MORPHTARGETS_TANGENT + tangentUpdated.xyz = tangentUpdated.xyz + (readVector3FromRawSampler({X}, vertexID) - vertexInputs.tangent.xyz) * uniforms.morphTargetInfluences[{X}]; + #endif + } #endif #else positionUpdated = positionUpdated + (position{X} - vertexInputs.position) * uniforms.morphTargetInfluences[{X}]; diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration.fx index eb7274eb086..e571c05e9c3 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration.fx @@ -13,5 +13,7 @@ #ifdef MORPHTARGETS_UV attribute uv_{X} : vec2; #endif + #elif {X} == 0 + uniform morphTargetCount: i32; #endif #endif \ No newline at end of file