morphTargetsBlock.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. import { NodeMaterialBlock } from '../../nodeMaterialBlock';
  2. import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
  3. import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
  4. import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
  5. import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
  6. import { AbstractMesh } from '../../../../Meshes/abstractMesh';
  7. import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
  8. import { Effect } from '../../../effect';
  9. import { Mesh } from '../../../../Meshes/mesh';
  10. import { MaterialHelper } from '../../../materialHelper';
  11. import { VertexBuffer } from '../../../../Meshes/buffer';
  12. import { InputBlock } from '../Input/inputBlock';
  13. import { _TypeStore } from '../../../../Misc/typeStore';
  14. import "../../../../Shaders/ShadersInclude/morphTargetsVertexDeclaration";
  15. import "../../../../Shaders/ShadersInclude/morphTargetsVertexGlobalDeclaration";
  16. /**
  17. * Block used to add morph targets support to vertex shader
  18. */
  19. export class MorphTargetsBlock extends NodeMaterialBlock {
  20. private _repeatableContentAnchor: string;
  21. private _repeatebleContentGenerated = 0;
  22. /**
  23. * Create a new MorphTargetsBlock
  24. * @param name defines the block name
  25. */
  26. public constructor(name: string) {
  27. super(name, NodeMaterialBlockTargets.Vertex);
  28. this.registerInput("position", NodeMaterialBlockConnectionPointTypes.Vector3);
  29. this.registerInput("normal", NodeMaterialBlockConnectionPointTypes.Vector3);
  30. this.registerInput("tangent", NodeMaterialBlockConnectionPointTypes.Vector3);
  31. this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2);
  32. this.registerOutput("positionOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
  33. this.registerOutput("normalOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
  34. this.registerOutput("tangentOutput", NodeMaterialBlockConnectionPointTypes.Vector3);
  35. this.registerOutput("uvOutput", NodeMaterialBlockConnectionPointTypes.Vector2);
  36. }
  37. /**
  38. * Gets the current class name
  39. * @returns the class name
  40. */
  41. public getClassName() {
  42. return "MorphTargetsBlock";
  43. }
  44. /**
  45. * Gets the position input component
  46. */
  47. public get position(): NodeMaterialConnectionPoint {
  48. return this._inputs[0];
  49. }
  50. /**
  51. * Gets the normal input component
  52. */
  53. public get normal(): NodeMaterialConnectionPoint {
  54. return this._inputs[1];
  55. }
  56. /**
  57. * Gets the tangent input component
  58. */
  59. public get tangent(): NodeMaterialConnectionPoint {
  60. return this._inputs[2];
  61. }
  62. /**
  63. * Gets the tangent input component
  64. */
  65. public get uv(): NodeMaterialConnectionPoint {
  66. return this._inputs[3];
  67. }
  68. /**
  69. * Gets the position output component
  70. */
  71. public get positionOutput(): NodeMaterialConnectionPoint {
  72. return this._outputs[0];
  73. }
  74. /**
  75. * Gets the normal output component
  76. */
  77. public get normalOutput(): NodeMaterialConnectionPoint {
  78. return this._outputs[1];
  79. }
  80. /**
  81. * Gets the tangent output component
  82. */
  83. public get tangentOutput(): NodeMaterialConnectionPoint {
  84. return this._outputs[2];
  85. }
  86. /**
  87. * Gets the tangent output component
  88. */
  89. public get uvOutput(): NodeMaterialConnectionPoint {
  90. return this._outputs[3];
  91. }
  92. public initialize(state: NodeMaterialBuildState) {
  93. state._excludeVariableName("morphTargetInfluences");
  94. }
  95. public autoConfigure(material: NodeMaterial) {
  96. if (!this.position.isConnected) {
  97. let positionInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "position");
  98. if (!positionInput) {
  99. positionInput = new InputBlock("position");
  100. positionInput.setAsAttribute();
  101. }
  102. positionInput.output.connectTo(this.position);
  103. }
  104. if (!this.normal.isConnected) {
  105. let normalInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "normal");
  106. if (!normalInput) {
  107. normalInput = new InputBlock("normal");
  108. normalInput.setAsAttribute("normal");
  109. }
  110. normalInput.output.connectTo(this.normal);
  111. }
  112. if (!this.tangent.isConnected) {
  113. let tangentInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "tangent");
  114. if (!tangentInput) {
  115. tangentInput = new InputBlock("tangent");
  116. tangentInput.setAsAttribute("tangent");
  117. }
  118. tangentInput.output.connectTo(this.tangent);
  119. }
  120. if (!this.uv.isConnected) {
  121. let uvInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "uv");
  122. if (!uvInput) {
  123. uvInput = new InputBlock("uv");
  124. uvInput.setAsAttribute("uv");
  125. }
  126. uvInput.output.connectTo(this.uv);
  127. }
  128. }
  129. public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
  130. if (!defines._areAttributesDirty) {
  131. return;
  132. }
  133. MaterialHelper.PrepareDefinesForMorphTargets(mesh, defines);
  134. }
  135. public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
  136. if (mesh && this._repeatebleContentGenerated) {
  137. MaterialHelper.BindMorphTargetParameters(mesh, effect);
  138. }
  139. }
  140. public replaceRepeatableContent(vertexShaderState: NodeMaterialBuildState, fragmentShaderState: NodeMaterialBuildState, mesh: AbstractMesh, defines: NodeMaterialDefines) {
  141. let position = this.position;
  142. let normal = this.normal;
  143. let tangent = this.tangent;
  144. let uv = this.uv;
  145. let positionOutput = this.positionOutput;
  146. let normalOutput = this.normalOutput;
  147. let tangentOutput = this.tangentOutput;
  148. let uvOutput = this.uvOutput;
  149. let state = vertexShaderState;
  150. let repeatCount = defines.NUM_MORPH_INFLUENCERS as number;
  151. this._repeatebleContentGenerated = repeatCount;
  152. var manager = (<Mesh>mesh).morphTargetManager;
  153. var hasNormals = manager && manager.supportsNormals && defines["NORMAL"];
  154. var hasTangents = manager && manager.supportsTangents && defines["TANGENT"];
  155. var hasUVs = manager && manager.supportsUVs && defines["UV1"];
  156. let injectionCode = "";
  157. for (var index = 0; index < repeatCount; index++) {
  158. injectionCode += `#ifdef MORPHTARGETS\r\n`;
  159. injectionCode += `${positionOutput.associatedVariableName} += (position${index} - ${position.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
  160. if (hasNormals) {
  161. injectionCode += `#ifdef MORPHTARGETS_NORMAL\r\n`;
  162. injectionCode += `${normalOutput.associatedVariableName} += (normal${index} - ${normal.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
  163. injectionCode += `#endif\r\n`;
  164. }
  165. if (hasTangents) {
  166. injectionCode += `#ifdef MORPHTARGETS_TANGENT\r\n`;
  167. injectionCode += `${tangentOutput.associatedVariableName}.xyz += (tangent${index} - ${tangent.associatedVariableName}.xyz) * morphTargetInfluences[${index}];\r\n`;
  168. injectionCode += `#endif\r\n`;
  169. }
  170. if (hasUVs) {
  171. injectionCode += `#ifdef MORPHTARGETS_UV\r\n`;
  172. injectionCode += `${uvOutput.associatedVariableName}.xyz += (uv_${index} - ${uv.associatedVariableName}.xyz) * morphTargetInfluences[${index}];\r\n`;
  173. injectionCode += `#endif\r\n`;
  174. }
  175. injectionCode += `#endif\r\n`;
  176. }
  177. state.compilationString = state.compilationString.replace(this._repeatableContentAnchor, injectionCode);
  178. if (repeatCount > 0) {
  179. for (var index = 0; index < repeatCount; index++) {
  180. state.attributes.push(VertexBuffer.PositionKind + index);
  181. if (hasNormals) {
  182. state.attributes.push(VertexBuffer.NormalKind + index);
  183. }
  184. if (hasTangents) {
  185. state.attributes.push(VertexBuffer.TangentKind + index);
  186. }
  187. }
  188. }
  189. }
  190. protected _buildBlock(state: NodeMaterialBuildState) {
  191. super._buildBlock(state);
  192. // Register for defines
  193. state.sharedData.blocksWithDefines.push(this);
  194. // Register for binding
  195. state.sharedData.bindableBlocks.push(this);
  196. // Register for repeatable content generation
  197. state.sharedData.repeatableContentBlocks.push(this);
  198. // Emit code
  199. let position = this.position;
  200. let normal = this.normal;
  201. let tangent = this.tangent;
  202. let uv = this.uv;
  203. let positionOutput = this.positionOutput;
  204. let normalOutput = this.normalOutput;
  205. let tangentOutput = this.tangentOutput;
  206. let uvOutput = this.uvOutput;
  207. let comments = `//${this.name}`;
  208. state.uniforms.push("morphTargetInfluences");
  209. state._emitFunctionFromInclude("morphTargetsVertexGlobalDeclaration", comments);
  210. state._emitFunctionFromInclude("morphTargetsVertexDeclaration", comments, {
  211. repeatKey: "maxSimultaneousMorphTargets"
  212. });
  213. state.compilationString += `${this._declareOutput(positionOutput, state)} = ${position.associatedVariableName};\r\n`;
  214. state.compilationString += `#ifdef NORMAL\r\n`;
  215. state.compilationString += `${this._declareOutput(normalOutput, state)} = ${normal.associatedVariableName};\r\n`;
  216. state.compilationString += `#endif\r\n`;
  217. state.compilationString += `#ifdef TANGENT\r\n`;
  218. state.compilationString += `${this._declareOutput(tangentOutput, state)} = ${tangent.associatedVariableName};\r\n`;
  219. state.compilationString += `#endif\r\n`;
  220. state.compilationString += `#ifdef UV1\r\n`;
  221. state.compilationString += `${this._declareOutput(uvOutput, state)} = ${uv.associatedVariableName};\r\n`;
  222. state.compilationString += `#endif\r\n`;
  223. // Repeatable content
  224. this._repeatableContentAnchor = state._repeatableContentAnchor;
  225. state.compilationString += this._repeatableContentAnchor;
  226. return this;
  227. }
  228. }
  229. _TypeStore.RegisteredTypes["BABYLON.MorphTargetsBlock"] = MorphTargetsBlock;