babylon.material.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. module BABYLON {
  2. export class MaterialDefines {
  3. _keys: string[];
  4. public rebuild() {
  5. if (this._keys) {
  6. delete this._keys;
  7. }
  8. this._keys = Object.keys(this);
  9. }
  10. public isEqual(other: MaterialDefines): boolean {
  11. if (this._keys.length !== other._keys.length) {
  12. return false;
  13. }
  14. for (var index = 0; index < this._keys.length; index++) {
  15. var prop = this._keys[index];
  16. if (this[prop] !== other[prop]) {
  17. return false;
  18. }
  19. }
  20. return true;
  21. }
  22. public cloneTo(other: MaterialDefines): void {
  23. if (this._keys.length !== other._keys.length) {
  24. other._keys = this._keys.slice(0);
  25. }
  26. for (var index = 0; index < this._keys.length; index++) {
  27. var prop = this._keys[index];
  28. other[prop] = this[prop];
  29. }
  30. }
  31. public reset(): void {
  32. for (var index = 0; index < this._keys.length; index++) {
  33. var prop = this._keys[index];
  34. if (typeof (this[prop]) === "number") {
  35. this[prop] = 0;
  36. } else {
  37. this[prop] = false;
  38. }
  39. }
  40. }
  41. public toString(): string {
  42. var result = "";
  43. for (var index = 0; index < this._keys.length; index++) {
  44. var prop = this._keys[index];
  45. if (typeof (this[prop]) === "number") {
  46. result += "#define " + prop + " " + this[prop] + "\n";
  47. } else if (this[prop]) {
  48. result += "#define " + prop + "\n";
  49. }
  50. }
  51. return result;
  52. }
  53. }
  54. export class Material {
  55. private static _TriangleFillMode = 0;
  56. private static _WireFrameFillMode = 1;
  57. private static _PointFillMode = 2;
  58. public static get TriangleFillMode(): number {
  59. return Material._TriangleFillMode;
  60. }
  61. public static get WireFrameFillMode(): number {
  62. return Material._WireFrameFillMode;
  63. }
  64. public static get PointFillMode(): number {
  65. return Material._PointFillMode;
  66. }
  67. private static _ClockWiseSideOrientation = 0;
  68. private static _CounterClockWiseSideOrientation = 1;
  69. public static get ClockWiseSideOrientation(): number {
  70. return Material._ClockWiseSideOrientation;
  71. }
  72. public static get CounterClockWiseSideOrientation(): number {
  73. return Material._CounterClockWiseSideOrientation;
  74. }
  75. @serialize()
  76. public id: string;
  77. @serialize()
  78. public name: string;
  79. @serialize()
  80. public checkReadyOnEveryCall = false;
  81. @serialize()
  82. public checkReadyOnlyOnce = false;
  83. @serialize()
  84. public state = "";
  85. @serialize()
  86. public alpha = 1.0;
  87. @serialize()
  88. public backFaceCulling = true;
  89. @serialize()
  90. public sideOrientation: number;
  91. public onCompiled: (effect: Effect) => void;
  92. public onError: (effect: Effect, errors: string) => void;
  93. public getRenderTargetTextures: () => SmartArray<RenderTargetTexture>;
  94. public doNotSerialize = false;
  95. /**
  96. * An event triggered when the material is disposed.
  97. * @type {BABYLON.Observable}
  98. */
  99. public onDisposeObservable = new Observable<Material>();
  100. private _onDisposeObserver: Observer<Material>;
  101. public set onDispose(callback: () => void) {
  102. if (this._onDisposeObserver) {
  103. this.onDisposeObservable.remove(this._onDisposeObserver);
  104. }
  105. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  106. }
  107. /**
  108. * An event triggered when the material is bound.
  109. * @type {BABYLON.Observable}
  110. */
  111. public onBindObservable = new Observable<AbstractMesh>();
  112. private _onBindObserver: Observer<AbstractMesh>;
  113. public set onBind(callback: (Mesh: AbstractMesh) => void) {
  114. if (this._onBindObserver) {
  115. this.onBindObservable.remove(this._onBindObserver);
  116. }
  117. this._onBindObserver = this.onBindObservable.add(callback);
  118. }
  119. /**
  120. * An event triggered when the material is unbound.
  121. * @type {BABYLON.Observable}
  122. */
  123. public onUnBindObservable = new Observable<Material>();
  124. @serialize()
  125. public alphaMode = Engine.ALPHA_COMBINE;
  126. @serialize()
  127. public disableDepthWrite = false;
  128. @serialize()
  129. public fogEnabled = true;
  130. @serialize()
  131. public pointSize = 1.0;
  132. @serialize()
  133. public zOffset = 0;
  134. @serialize()
  135. public get wireframe(): boolean {
  136. return this._fillMode === Material.WireFrameFillMode;
  137. }
  138. public set wireframe(value: boolean) {
  139. this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
  140. }
  141. @serialize()
  142. public get pointsCloud(): boolean {
  143. return this._fillMode === Material.PointFillMode;
  144. }
  145. public set pointsCloud(value: boolean) {
  146. this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
  147. }
  148. @serialize()
  149. public get fillMode(): number {
  150. return this._fillMode;
  151. }
  152. public set fillMode(value: number) {
  153. this._fillMode = value;
  154. }
  155. public _effect: Effect;
  156. public _wasPreviouslyReady = false;
  157. private _scene: Scene;
  158. private _fillMode = Material.TriangleFillMode;
  159. private _cachedDepthWriteState: boolean;
  160. constructor(name: string, scene: Scene, doNotAdd?: boolean) {
  161. this.name = name;
  162. this.id = name;
  163. this._scene = scene || Engine.LastCreatedScene;
  164. if (scene.useRightHandedSystem) {
  165. this.sideOrientation = Material.ClockWiseSideOrientation;
  166. } else {
  167. this.sideOrientation = Material.CounterClockWiseSideOrientation;
  168. }
  169. if (!doNotAdd) {
  170. this._scene.materials.push(this);
  171. }
  172. }
  173. /**
  174. * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
  175. * subclasses should override adding information pertainent to themselves
  176. */
  177. public toString(fullDetails? : boolean) : string {
  178. var ret = "Name: " + this.name;
  179. if (fullDetails){
  180. }
  181. return ret;
  182. }
  183. public getClassName(): string {
  184. return "Material";
  185. }
  186. public get isFrozen(): boolean {
  187. return this.checkReadyOnlyOnce;
  188. }
  189. public freeze(): void {
  190. this.checkReadyOnlyOnce = true;
  191. }
  192. public unfreeze(): void {
  193. this.checkReadyOnlyOnce = false;
  194. }
  195. public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
  196. return true;
  197. }
  198. public getEffect(): Effect {
  199. return this._effect;
  200. }
  201. public getScene(): Scene {
  202. return this._scene;
  203. }
  204. public needAlphaBlending(): boolean {
  205. return (this.alpha < 1.0);
  206. }
  207. public needAlphaTesting(): boolean {
  208. return false;
  209. }
  210. public getAlphaTestTexture(): BaseTexture {
  211. return null;
  212. }
  213. public markDirty(): void {
  214. this._wasPreviouslyReady = false;
  215. }
  216. public _preBind(): void {
  217. var engine = this._scene.getEngine();
  218. var reverse = this.sideOrientation === Material.ClockWiseSideOrientation;
  219. engine.enableEffect(this._effect);
  220. engine.setState(this.backFaceCulling, this.zOffset, false, reverse);
  221. }
  222. public bind(world: Matrix, mesh?: Mesh): void {
  223. this._scene._cachedMaterial = this;
  224. this.onBindObservable.notifyObservers(mesh);
  225. if (this.disableDepthWrite) {
  226. var engine = this._scene.getEngine();
  227. this._cachedDepthWriteState = engine.getDepthWrite();
  228. engine.setDepthWrite(false);
  229. }
  230. }
  231. public bindOnlyWorldMatrix(world: Matrix): void {
  232. }
  233. public unbind(): void {
  234. this.onUnBindObservable.notifyObservers(this);
  235. if (this.disableDepthWrite) {
  236. var engine = this._scene.getEngine();
  237. engine.setDepthWrite(this._cachedDepthWriteState);
  238. }
  239. }
  240. public clone(name: string): Material {
  241. return null;
  242. }
  243. public getBindedMeshes(): AbstractMesh[] {
  244. var result = new Array<AbstractMesh>();
  245. for (var index = 0; index < this._scene.meshes.length; index++) {
  246. var mesh = this._scene.meshes[index];
  247. if (mesh.material === this) {
  248. result.push(mesh);
  249. }
  250. }
  251. return result;
  252. }
  253. public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
  254. // Animations
  255. this.getScene().stopAnimation(this);
  256. // Remove from scene
  257. var index = this._scene.materials.indexOf(this);
  258. if (index >= 0) {
  259. this._scene.materials.splice(index, 1);
  260. }
  261. // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
  262. if (forceDisposeEffect && this._effect) {
  263. this._scene.getEngine()._releaseEffect(this._effect);
  264. this._effect = null;
  265. }
  266. // Remove from meshes
  267. for (index = 0; index < this._scene.meshes.length; index++) {
  268. var mesh = this._scene.meshes[index];
  269. if (mesh.material === this) {
  270. mesh.material = null;
  271. }
  272. }
  273. // Callback
  274. this.onDisposeObservable.notifyObservers(this);
  275. this.onDisposeObservable.clear();
  276. this.onBindObservable.clear();
  277. this.onUnBindObservable.clear();
  278. }
  279. public serialize(): any {
  280. return SerializationHelper.Serialize(this);
  281. }
  282. public static ParseMultiMaterial(parsedMultiMaterial: any, scene: Scene): MultiMaterial {
  283. var multiMaterial = new BABYLON.MultiMaterial(parsedMultiMaterial.name, scene);
  284. multiMaterial.id = parsedMultiMaterial.id;
  285. Tags.AddTagsTo(multiMaterial, parsedMultiMaterial.tags);
  286. for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
  287. var subMatId = parsedMultiMaterial.materials[matIndex];
  288. if (subMatId) {
  289. multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
  290. } else {
  291. multiMaterial.subMaterials.push(null);
  292. }
  293. }
  294. return multiMaterial;
  295. }
  296. public static Parse(parsedMaterial: any, scene: Scene, rootUrl: string) {
  297. if (!parsedMaterial.customType) {
  298. return StandardMaterial.Parse(parsedMaterial, scene, rootUrl);
  299. }
  300. var materialType = Tools.Instantiate(parsedMaterial.customType);
  301. return materialType.Parse(parsedMaterial, scene, rootUrl);;
  302. }
  303. }
  304. }