multiMaterial.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { Material } from "../Materials/material.js";
  2. import { Tags } from "../Misc/tags.js";
  3. import { RegisterClass } from "../Misc/typeStore.js";
  4. /**
  5. * A multi-material is used to apply different materials to different parts of the same object without the need of
  6. * separate meshes. This can be use to improve performances.
  7. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/multiMaterials
  8. */
  9. export class MultiMaterial extends Material {
  10. /**
  11. * Gets or Sets the list of Materials used within the multi material.
  12. * They need to be ordered according to the submeshes order in the associated mesh
  13. */
  14. get subMaterials() {
  15. return this._subMaterials;
  16. }
  17. set subMaterials(value) {
  18. this._subMaterials = value;
  19. this._hookArray(value);
  20. }
  21. /**
  22. * Function used to align with Node.getChildren()
  23. * @returns the list of Materials used within the multi material
  24. */
  25. getChildren() {
  26. return this.subMaterials;
  27. }
  28. /**
  29. * Instantiates a new Multi Material
  30. * A multi-material is used to apply different materials to different parts of the same object without the need of
  31. * separate meshes. This can be use to improve performances.
  32. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/multiMaterials
  33. * @param name Define the name in the scene
  34. * @param scene Define the scene the material belongs to
  35. */
  36. constructor(name, scene) {
  37. super(name, scene, true);
  38. /** @internal */
  39. this._waitingSubMaterialsUniqueIds = [];
  40. this.getScene().addMultiMaterial(this);
  41. this.subMaterials = [];
  42. this._storeEffectOnSubMeshes = true; // multimaterial is considered like a push material
  43. }
  44. _hookArray(array) {
  45. const oldPush = array.push;
  46. array.push = (...items) => {
  47. const result = oldPush.apply(array, items);
  48. this._markAllSubMeshesAsTexturesDirty();
  49. return result;
  50. };
  51. const oldSplice = array.splice;
  52. array.splice = (index, deleteCount) => {
  53. const deleted = oldSplice.apply(array, [index, deleteCount]);
  54. this._markAllSubMeshesAsTexturesDirty();
  55. return deleted;
  56. };
  57. }
  58. /**
  59. * Get one of the submaterial by its index in the submaterials array
  60. * @param index The index to look the sub material at
  61. * @returns The Material if the index has been defined
  62. */
  63. getSubMaterial(index) {
  64. if (index < 0 || index >= this.subMaterials.length) {
  65. return this.getScene().defaultMaterial;
  66. }
  67. return this.subMaterials[index];
  68. }
  69. /**
  70. * Get the list of active textures for the whole sub materials list.
  71. * @returns All the textures that will be used during the rendering
  72. */
  73. getActiveTextures() {
  74. return super.getActiveTextures().concat(...this.subMaterials.map((subMaterial) => {
  75. if (subMaterial) {
  76. return subMaterial.getActiveTextures();
  77. }
  78. else {
  79. return [];
  80. }
  81. }));
  82. }
  83. /**
  84. * Specifies if any sub-materials of this multi-material use a given texture.
  85. * @param texture Defines the texture to check against this multi-material's sub-materials.
  86. * @returns A boolean specifying if any sub-material of this multi-material uses the texture.
  87. */
  88. hasTexture(texture) {
  89. if (super.hasTexture(texture)) {
  90. return true;
  91. }
  92. for (let i = 0; i < this.subMaterials.length; i++) {
  93. if (this.subMaterials[i]?.hasTexture(texture)) {
  94. return true;
  95. }
  96. }
  97. return false;
  98. }
  99. /**
  100. * Gets the current class name of the material e.g. "MultiMaterial"
  101. * Mainly use in serialization.
  102. * @returns the class name
  103. */
  104. getClassName() {
  105. return "MultiMaterial";
  106. }
  107. /**
  108. * Checks if the material is ready to render the requested sub mesh
  109. * @param mesh Define the mesh the submesh belongs to
  110. * @param subMesh Define the sub mesh to look readiness for
  111. * @param useInstances Define whether or not the material is used with instances
  112. * @returns true if ready, otherwise false
  113. */
  114. isReadyForSubMesh(mesh, subMesh, useInstances) {
  115. for (let index = 0; index < this.subMaterials.length; index++) {
  116. const subMaterial = this.subMaterials[index];
  117. if (subMaterial) {
  118. if (subMaterial._storeEffectOnSubMeshes) {
  119. if (!subMaterial.isReadyForSubMesh(mesh, subMesh, useInstances)) {
  120. return false;
  121. }
  122. continue;
  123. }
  124. if (!subMaterial.isReady(mesh)) {
  125. return false;
  126. }
  127. }
  128. }
  129. return true;
  130. }
  131. /**
  132. * Clones the current material and its related sub materials
  133. * @param name Define the name of the newly cloned material
  134. * @param cloneChildren Define if submaterial will be cloned or shared with the parent instance
  135. * @returns the cloned material
  136. */
  137. clone(name, cloneChildren) {
  138. const newMultiMaterial = new MultiMaterial(name, this.getScene());
  139. for (let index = 0; index < this.subMaterials.length; index++) {
  140. let subMaterial = null;
  141. const current = this.subMaterials[index];
  142. if (cloneChildren && current) {
  143. subMaterial = current.clone(name + "-" + current.name);
  144. }
  145. else {
  146. subMaterial = this.subMaterials[index];
  147. }
  148. newMultiMaterial.subMaterials.push(subMaterial);
  149. }
  150. return newMultiMaterial;
  151. }
  152. /**
  153. * Serializes the materials into a JSON representation.
  154. * @returns the JSON representation
  155. */
  156. serialize() {
  157. const serializationObject = {};
  158. serializationObject.name = this.name;
  159. serializationObject.id = this.id;
  160. serializationObject.uniqueId = this.uniqueId;
  161. if (Tags) {
  162. serializationObject.tags = Tags.GetTags(this);
  163. }
  164. serializationObject.materialsUniqueIds = [];
  165. serializationObject.materials = [];
  166. for (let matIndex = 0; matIndex < this.subMaterials.length; matIndex++) {
  167. const subMat = this.subMaterials[matIndex];
  168. if (subMat) {
  169. serializationObject.materialsUniqueIds.push(subMat.uniqueId);
  170. serializationObject.materials.push(subMat.id);
  171. }
  172. else {
  173. serializationObject.materialsUniqueIds.push(null);
  174. serializationObject.materials.push(null);
  175. }
  176. }
  177. return serializationObject;
  178. }
  179. /**
  180. * Dispose the material and release its associated resources
  181. * @param forceDisposeEffect Define if we want to force disposing the associated effect (if false the shader is not released and could be reuse later on)
  182. * @param forceDisposeTextures Define if we want to force disposing the associated textures (if false, they will not be disposed and can still be use elsewhere in the app)
  183. * @param forceDisposeChildren Define if we want to force disposing the associated submaterials (if false, they will not be disposed and can still be use elsewhere in the app)
  184. */
  185. dispose(forceDisposeEffect, forceDisposeTextures, forceDisposeChildren) {
  186. const scene = this.getScene();
  187. if (!scene) {
  188. return;
  189. }
  190. if (forceDisposeChildren) {
  191. for (let index = 0; index < this.subMaterials.length; index++) {
  192. const subMaterial = this.subMaterials[index];
  193. if (subMaterial) {
  194. subMaterial.dispose(forceDisposeEffect, forceDisposeTextures);
  195. }
  196. }
  197. }
  198. const index = scene.multiMaterials.indexOf(this);
  199. if (index >= 0) {
  200. scene.multiMaterials.splice(index, 1);
  201. }
  202. super.dispose(forceDisposeEffect, forceDisposeTextures);
  203. }
  204. /**
  205. * Creates a MultiMaterial from parsed MultiMaterial data.
  206. * @param parsedMultiMaterial defines parsed MultiMaterial data.
  207. * @param scene defines the hosting scene
  208. * @returns a new MultiMaterial
  209. */
  210. static ParseMultiMaterial(parsedMultiMaterial, scene) {
  211. const multiMaterial = new MultiMaterial(parsedMultiMaterial.name, scene);
  212. multiMaterial.id = parsedMultiMaterial.id;
  213. multiMaterial._loadedUniqueId = parsedMultiMaterial.uniqueId;
  214. if (Tags) {
  215. Tags.AddTagsTo(multiMaterial, parsedMultiMaterial.tags);
  216. }
  217. if (parsedMultiMaterial.materialsUniqueIds) {
  218. multiMaterial._waitingSubMaterialsUniqueIds = parsedMultiMaterial.materialsUniqueIds;
  219. }
  220. else {
  221. parsedMultiMaterial.materials.forEach((subMatId) => multiMaterial.subMaterials.push(scene.getLastMaterialById(subMatId)));
  222. }
  223. return multiMaterial;
  224. }
  225. }
  226. RegisterClass("BABYLON.MultiMaterial", MultiMaterial);
  227. //# sourceMappingURL=multiMaterial.js.map