multiMaterial.ts 8.9 KB

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