babylon.multiMaterial.ts 7.4 KB

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