babylon.materialHelper.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. module BABYLON {
  2. export class MaterialHelper {
  3. public static PrepareDefinesForMisc(mesh: AbstractMesh, scene: Scene, useLogarithmicDepth: boolean, pointsCloud, fogEnabled: boolean, defines: MaterialDefines): void {
  4. if (defines._areMiscDirty) {
  5. defines["LOGARITHMICDEPTH"] = useLogarithmicDepth;
  6. defines["POINTSIZE"] = (pointsCloud || scene.forcePointsCloud);
  7. defines["FOG"] = (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && fogEnabled);
  8. defines["USERIGHTHANDEDSYSTEM"] = scene.useRightHandedSystem;
  9. }
  10. }
  11. public static PrepareDefinesForFrameBoundValues(scene: Scene, engine: Engine, defines: MaterialDefines, useInstances: boolean, forceAlphaTest = false): void {
  12. var changed = false;
  13. if (defines["CLIPPLANE"] !== (scene.clipPlane !== undefined && scene.clipPlane !== null)) {
  14. defines["CLIPPLANE"] = !defines["CLIPPLANE"];
  15. changed = true;
  16. }
  17. if (defines["ALPHATEST"] !== (engine.getAlphaTesting() || forceAlphaTest)) {
  18. defines["ALPHATEST"] = !defines["ALPHATEST"];
  19. changed = true;
  20. }
  21. if (defines["INSTANCES"] !== useInstances) {
  22. defines["INSTANCES"] = useInstances;
  23. changed = true;
  24. }
  25. if (changed) {
  26. defines.markAsUnprocessed();
  27. }
  28. }
  29. public static PrepareDefinesForAttributes(mesh: AbstractMesh, defines: MaterialDefines, useVertexColor: boolean, useBones: boolean, useMorphTargets = false): boolean {
  30. if (!defines._areAttributesDirty && defines._needNormals === defines._normals && defines._needUVs === defines._uvs) {
  31. return false;
  32. }
  33. defines._normals = defines._needNormals;
  34. defines._uvs = defines._needUVs;
  35. defines["NORMAL"] = (defines._needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind));
  36. if (defines._needNormals && mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
  37. defines["TANGENT"] = true;
  38. }
  39. if (defines._needUVs) {
  40. defines["UV1"] = mesh.isVerticesDataPresent(VertexBuffer.UVKind);
  41. defines["UV2"] = mesh.isVerticesDataPresent(VertexBuffer.UV2Kind);
  42. } else {
  43. defines["UV1"] = false;
  44. defines["UV2"] = false;
  45. }
  46. if (useVertexColor) {
  47. defines["VERTEXCOLOR"] = mesh.useVertexColors && mesh.isVerticesDataPresent(VertexBuffer.ColorKind);
  48. defines["VERTEXALPHA"] = mesh.hasVertexAlpha;
  49. }
  50. if (useBones) {
  51. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  52. defines["NUM_BONE_INFLUENCERS"] = mesh.numBoneInfluencers;
  53. defines["BonesPerMesh"] = (mesh.skeleton.bones.length + 1);
  54. } else {
  55. defines["NUM_BONE_INFLUENCERS"] = 0;
  56. defines["BonesPerMesh"] = 0;
  57. }
  58. }
  59. if (useMorphTargets) {
  60. if ((<any>mesh).morphTargetManager) {
  61. var manager = (<Mesh>mesh).morphTargetManager;
  62. defines["MORPHTARGETS_TANGENT"] = manager.supportsTangents && defines["TANGENT"];
  63. defines["MORPHTARGETS_NORMAL"] = manager.supportsNormals && defines["NORMAL"] ;
  64. defines["MORPHTARGETS"] = (manager.numInfluencers > 0);
  65. defines["NUM_MORPH_INFLUENCERS"] = manager.numInfluencers;
  66. } else {
  67. defines["MORPHTARGETS_TANGENT"] = false;
  68. defines["MORPHTARGETS_NORMAL"] = false;
  69. defines["MORPHTARGETS"] = false;
  70. defines["NUM_MORPH_INFLUENCERS"] = 0;
  71. }
  72. }
  73. return true;
  74. }
  75. public static PrepareDefinesForLights(scene: Scene, mesh: AbstractMesh, defines: MaterialDefines, specularSupported: boolean, maxSimultaneousLights = 4, disableLighting = false): boolean {
  76. if (!defines._areLightsDirty) {
  77. return defines._needNormals;
  78. }
  79. var lightIndex = 0;
  80. var needNormals = false;
  81. var needRebuild = false;
  82. var lightmapMode = false;
  83. var shadowEnabled = false;
  84. var specularEnabled = false;
  85. if (scene.lightsEnabled && !disableLighting) {
  86. for (var light of mesh._lightSources) {
  87. needNormals = true;
  88. if (defines["LIGHT" + lightIndex] === undefined) {
  89. needRebuild = true;
  90. }
  91. defines["LIGHT" + lightIndex] = true;
  92. defines["SPOTLIGHT" + lightIndex] = false;
  93. defines["HEMILIGHT" + lightIndex] = false;
  94. defines["POINTLIGHT" + lightIndex] = false;
  95. defines["DIRLIGHT" + lightIndex] = false;
  96. var type;
  97. if (light.getTypeID() === Light.LIGHTTYPEID_SPOTLIGHT) {
  98. type = "SPOTLIGHT" + lightIndex;
  99. } else if (light.getTypeID() === Light.LIGHTTYPEID_HEMISPHERICLIGHT) {
  100. type = "HEMILIGHT" + lightIndex;
  101. } else if (light.getTypeID() === Light.LIGHTTYPEID_POINTLIGHT) {
  102. type = "POINTLIGHT" + lightIndex;
  103. } else {
  104. type = "DIRLIGHT" + lightIndex;
  105. }
  106. defines[type] = true;
  107. // Specular
  108. if (specularSupported && !light.specular.equalsFloats(0, 0, 0)) {
  109. specularEnabled = true;
  110. }
  111. // Shadows
  112. defines["SHADOW" + lightIndex] = false;
  113. defines["SHADOWPCF" + lightIndex] = false;
  114. defines["SHADOWESM" + lightIndex] = false;
  115. defines["SHADOWCUBE" + lightIndex] = false;
  116. if (mesh && mesh.receiveShadows && scene.shadowsEnabled && light.shadowEnabled) {
  117. var shadowGenerator = light.getShadowGenerator();
  118. if (shadowGenerator) {
  119. shadowEnabled = true;
  120. shadowGenerator.prepareDefines(defines, lightIndex);
  121. }
  122. }
  123. if (light.lightmapMode != Light.LIGHTMAP_DEFAULT ) {
  124. lightmapMode = true;
  125. defines["LIGHTMAPEXCLUDED" + lightIndex] = true;
  126. defines["LIGHTMAPNOSPECULAR" + lightIndex] = (light.lightmapMode == Light.LIGHTMAP_SHADOWSONLY);
  127. } else {
  128. defines["LIGHTMAPEXCLUDED" + lightIndex] = false;
  129. defines["LIGHTMAPNOSPECULAR" + lightIndex] = false;
  130. }
  131. lightIndex++;
  132. if (lightIndex === maxSimultaneousLights)
  133. break;
  134. }
  135. }
  136. defines["SPECULARTERM"] = specularEnabled;
  137. defines["SHADOWS"] = shadowEnabled;
  138. // Resetting all other lights if any
  139. for (var index = lightIndex; index < maxSimultaneousLights; index++) {
  140. if (defines["LIGHT" + index] !== undefined) {
  141. defines["LIGHT" + index] = false;
  142. }
  143. }
  144. let caps = scene.getEngine().getCaps();
  145. if (defines["SHADOWFLOAT"] === undefined) {
  146. needRebuild = true;
  147. }
  148. defines["SHADOWFLOAT"] = shadowEnabled &&
  149. ((caps.textureFloatRender && caps.textureFloatLinearFiltering) ||
  150. (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering));
  151. defines["LIGHTMAPEXCLUDED"] = lightmapMode;
  152. if (needRebuild) {
  153. defines.rebuild();
  154. }
  155. return needNormals;
  156. }
  157. public static PrepareUniformsAndSamplersList(uniformsListOrOptions: string[] | EffectCreationOptions, samplersList?: string[], defines?: MaterialDefines, maxSimultaneousLights = 4): void {
  158. var uniformsList: string[], uniformBuffersList: string[], samplersList: string[], defines: MaterialDefines;
  159. if ((<EffectCreationOptions>uniformsListOrOptions).uniformsNames) {
  160. var options = <EffectCreationOptions>uniformsListOrOptions;
  161. uniformsList = options.uniformsNames;
  162. uniformBuffersList = options.uniformBuffersNames;
  163. samplersList = options.samplers;
  164. defines = options.defines;
  165. maxSimultaneousLights = options.maxSimultaneousLights;
  166. } else {
  167. uniformsList = <string[]>uniformsListOrOptions;
  168. }
  169. for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
  170. if (!defines["LIGHT" + lightIndex]) {
  171. break;
  172. }
  173. uniformsList.push(
  174. "vLightData" + lightIndex,
  175. "vLightDiffuse" + lightIndex,
  176. "vLightSpecular" + lightIndex,
  177. "vLightDirection" + lightIndex,
  178. "vLightGround" + lightIndex,
  179. "lightMatrix" + lightIndex,
  180. "shadowsInfo" + lightIndex
  181. );
  182. if (uniformBuffersList) {
  183. uniformBuffersList.push("Light" + lightIndex);
  184. }
  185. samplersList.push("shadowSampler" + lightIndex);
  186. }
  187. if (defines["NUM_MORPH_INFLUENCERS"]) {
  188. uniformsList.push("morphTargetInfluences");
  189. }
  190. }
  191. public static HandleFallbacksForShadows(defines: MaterialDefines, fallbacks: EffectFallbacks, maxSimultaneousLights = 4): void {
  192. if (!defines["SHADOWS"]) {
  193. return;
  194. }
  195. for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
  196. if (!defines["LIGHT" + lightIndex]) {
  197. break;
  198. }
  199. if (lightIndex > 0) {
  200. fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
  201. }
  202. if (defines["SHADOW" + lightIndex]) {
  203. fallbacks.addFallback(0, "SHADOW" + lightIndex);
  204. }
  205. if (defines["SHADOWPCF" + lightIndex]) {
  206. fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
  207. }
  208. if (defines["SHADOWESM" + lightIndex]) {
  209. fallbacks.addFallback(0, "SHADOWESM" + lightIndex);
  210. }
  211. }
  212. }
  213. public static PrepareAttributesForMorphTargets(attribs: string[], mesh: AbstractMesh, defines: MaterialDefines): void {
  214. var influencers = defines["NUM_MORPH_INFLUENCERS"];
  215. if (influencers > 0) {
  216. var maxAttributesCount = Engine.LastCreatedEngine.getCaps().maxVertexAttribs;
  217. var manager = (<Mesh>mesh).morphTargetManager;
  218. var normal = manager.supportsNormals && defines["NORMAL"];
  219. var tangent = manager.supportsTangents && defines["TANGENT"];
  220. for (var index = 0; index < influencers; index++) {
  221. attribs.push(VertexBuffer.PositionKind + index);
  222. if (normal) {
  223. attribs.push(VertexBuffer.NormalKind + index);
  224. }
  225. if (tangent) {
  226. attribs.push(VertexBuffer.TangentKind + index);
  227. }
  228. if (attribs.length > maxAttributesCount) {
  229. Tools.Error("Cannot add more vertex attributes for mesh " + mesh.name);
  230. }
  231. }
  232. }
  233. }
  234. public static PrepareAttributesForBones(attribs: string[], mesh: AbstractMesh, defines: MaterialDefines, fallbacks: EffectFallbacks): void {
  235. if (defines["NUM_BONE_INFLUENCERS"] > 0) {
  236. fallbacks.addCPUSkinningFallback(0, mesh);
  237. attribs.push(VertexBuffer.MatricesIndicesKind);
  238. attribs.push(VertexBuffer.MatricesWeightsKind);
  239. if (defines["NUM_BONE_INFLUENCERS"] > 4) {
  240. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  241. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  242. }
  243. }
  244. }
  245. public static PrepareAttributesForInstances(attribs: string[], defines: MaterialDefines): void {
  246. if (defines["INSTANCES"]) {
  247. attribs.push("world0");
  248. attribs.push("world1");
  249. attribs.push("world2");
  250. attribs.push("world3");
  251. }
  252. }
  253. // Bindings
  254. public static BindLightShadow(light: Light, scene: Scene, mesh: AbstractMesh, lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean {
  255. if (light.shadowEnabled && mesh.receiveShadows) {
  256. var shadowGenerator = light.getShadowGenerator();
  257. if (shadowGenerator) {
  258. depthValuesAlreadySet = shadowGenerator.bindShadowLight(lightIndex, effect, depthValuesAlreadySet);
  259. }
  260. }
  261. return depthValuesAlreadySet;
  262. }
  263. public static BindLightProperties(light: Light, effect: Effect, lightIndex: number): void {
  264. light.transferToEffect(effect, lightIndex + "");
  265. }
  266. public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines, maxSimultaneousLights = 4) {
  267. var lightIndex = 0;
  268. var depthValuesAlreadySet = false;
  269. for (var light of mesh._lightSources) {
  270. light._uniformBuffer.bindToEffect(effect, "Light" + lightIndex);
  271. MaterialHelper.BindLightProperties(light, effect, lightIndex);
  272. light.diffuse.scaleToRef(light.intensity, Tmp.Color3[0]);
  273. light._uniformBuffer.updateColor4("vLightDiffuse", Tmp.Color3[0], light.range, lightIndex + "");
  274. if (defines["SPECULARTERM"]) {
  275. light.specular.scaleToRef(light.intensity, Tmp.Color3[1]);
  276. light._uniformBuffer.updateColor3("vLightSpecular", Tmp.Color3[1], lightIndex + "");
  277. }
  278. // Shadows
  279. if (scene.shadowsEnabled) {
  280. depthValuesAlreadySet = this.BindLightShadow(light, scene, mesh, lightIndex + "", effect, depthValuesAlreadySet);
  281. }
  282. light._uniformBuffer.update();
  283. lightIndex++;
  284. if (lightIndex === maxSimultaneousLights)
  285. break;
  286. }
  287. }
  288. public static BindFogParameters(scene: Scene, mesh: AbstractMesh, effect: Effect): void {
  289. if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
  290. effect.setFloat4("vFogInfos", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
  291. effect.setColor3("vFogColor", scene.fogColor);
  292. }
  293. }
  294. public static BindBonesParameters(mesh: AbstractMesh, effect: Effect): void {
  295. if (mesh && mesh.useBones && mesh.computeBonesUsingShaders) {
  296. var matrices = mesh.skeleton.getTransformMatrices(mesh);
  297. if (matrices) {
  298. effect.setMatrices("mBones", matrices);
  299. }
  300. }
  301. }
  302. public static BindMorphTargetParameters(abstractMesh: AbstractMesh, effect: Effect): void {
  303. if (!abstractMesh || !(<Mesh>abstractMesh).morphTargetManager) {
  304. return;
  305. }
  306. effect.setFloatArray("morphTargetInfluences", (<Mesh>abstractMesh).morphTargetManager.influences);
  307. }
  308. public static BindLogDepth(defines: MaterialDefines, effect: Effect, scene: Scene): void {
  309. if (defines["LOGARITHMICDEPTH"]) {
  310. effect.setFloat("logarithmicDepthConstant", 2.0 / (Math.log(scene.activeCamera.maxZ + 1.0) / Math.LN2));
  311. }
  312. }
  313. public static BindClipPlane(effect: Effect, scene: Scene): void {
  314. if (scene.clipPlane) {
  315. var clipPlane = scene.clipPlane;
  316. effect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
  317. }
  318. }
  319. }
  320. }