babylon.material.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. module BABYLON {
  2. export class MaterialDefines {
  3. _keys: string[];
  4. _isDirty = true;
  5. _trackIsDirty = false;
  6. public _renderId: number;
  7. public _areLightsDirty = true;
  8. public _areAttributesDirty = true;
  9. public _needNormals = false;
  10. public _needUVs = false;
  11. constructor(trackIsDirty?: boolean) {
  12. this._trackIsDirty = trackIsDirty;
  13. }
  14. private _reBind(key: string): void {
  15. this["_" + key] = this[key];
  16. Object.defineProperty(this, key, {
  17. get: function () {
  18. return this["_" + key];
  19. },
  20. set: function (value) {
  21. if (this["_" + key] === value) {
  22. return;
  23. }
  24. this["_" + key] = value;
  25. this._isDirty = true;
  26. },
  27. enumerable: true,
  28. configurable: true
  29. });
  30. }
  31. public rebuild() {
  32. if (this._keys) {
  33. delete this._keys;
  34. }
  35. this._keys = [];
  36. for (var key of Object.keys(this)) {
  37. if (key[0] === "_") {
  38. continue;
  39. }
  40. this._keys.push(key);
  41. if (!this._trackIsDirty) {
  42. continue;
  43. }
  44. if (Object.getOwnPropertyDescriptor(this, key).get) {
  45. continue;
  46. }
  47. this._reBind(key);
  48. }
  49. }
  50. public isEqual(other: MaterialDefines): boolean {
  51. if (this._keys.length !== other._keys.length) {
  52. return false;
  53. }
  54. for (var index = 0; index < this._keys.length; index++) {
  55. var prop = this._keys[index];
  56. if (this[prop] !== other[prop]) {
  57. return false;
  58. }
  59. }
  60. return true;
  61. }
  62. public cloneTo(other: MaterialDefines): void {
  63. if (this._keys.length !== other._keys.length) {
  64. other._keys = this._keys.slice(0);
  65. }
  66. for (var index = 0; index < this._keys.length; index++) {
  67. var prop = this._keys[index];
  68. other[prop] = this[prop];
  69. }
  70. }
  71. public reset(): void {
  72. for (var index = 0; index < this._keys.length; index++) {
  73. var prop = this._keys[index];
  74. if (typeof (this[prop]) === "number") {
  75. this[prop] = 0;
  76. } else {
  77. this[prop] = false;
  78. }
  79. }
  80. }
  81. public toString(): string {
  82. var result = "";
  83. for (var index = 0; index < this._keys.length; index++) {
  84. var prop = this._keys[index];
  85. var value = this[prop];
  86. if (typeof (value) === "number") {
  87. result += "#define " + prop + " " + this[prop] + "\n";
  88. } else if (value) {
  89. result += "#define " + prop + "\n";
  90. }
  91. }
  92. return result;
  93. }
  94. }
  95. export class Material {
  96. private static _TriangleFillMode = 0;
  97. private static _WireFrameFillMode = 1;
  98. private static _PointFillMode = 2;
  99. public static get TriangleFillMode(): number {
  100. return Material._TriangleFillMode;
  101. }
  102. public static get WireFrameFillMode(): number {
  103. return Material._WireFrameFillMode;
  104. }
  105. public static get PointFillMode(): number {
  106. return Material._PointFillMode;
  107. }
  108. private static _ClockWiseSideOrientation = 0;
  109. private static _CounterClockWiseSideOrientation = 1;
  110. public static get ClockWiseSideOrientation(): number {
  111. return Material._ClockWiseSideOrientation;
  112. }
  113. public static get CounterClockWiseSideOrientation(): number {
  114. return Material._CounterClockWiseSideOrientation;
  115. }
  116. private static _TextureDirtyFlag = 0;
  117. private static _LightDirtyFlag = 1;
  118. private static _FresnelDirtyFlag = 2;
  119. private static _AttributesDirtyFlag = 4;
  120. private static _MiscDirtyFlag = 8;
  121. public static get TextureDirtyFlag(): number {
  122. return Material._TextureDirtyFlag;
  123. }
  124. public static get LightDirtyFlag(): number {
  125. return Material._LightDirtyFlag;
  126. }
  127. public static get FresnelDirtyFlag(): number {
  128. return Material._FresnelDirtyFlag;
  129. }
  130. public static get AttributesDirtyFlag(): number {
  131. return Material._AttributesDirtyFlag;
  132. }
  133. public static get MiscDirtyFlag(): number {
  134. return Material._MiscDirtyFlag;
  135. }
  136. @serialize()
  137. public id: string;
  138. @serialize()
  139. public name: string;
  140. @serialize()
  141. public checkReadyOnEveryCall = false;
  142. @serialize()
  143. public checkReadyOnlyOnce = false;
  144. @serialize()
  145. public state = "";
  146. @serialize()
  147. public alpha = 1.0;
  148. @serialize("backFaceCulling")
  149. protected _backFaceCulling = true;
  150. public set backFaceCulling(value : boolean) {
  151. if (this._backFaceCulling === value) {
  152. return;
  153. }
  154. this._backFaceCulling = value;
  155. this.markAsDirty(Material.TextureDirtyFlag);
  156. }
  157. public get backFaceCulling(): boolean {
  158. return this._backFaceCulling;
  159. }
  160. @serialize()
  161. public sideOrientation: number;
  162. public onCompiled: (effect: Effect) => void;
  163. public onError: (effect: Effect, errors: string) => void;
  164. public getRenderTargetTextures: () => SmartArray<RenderTargetTexture>;
  165. public doNotSerialize = false;
  166. public storeEffectOnSubMeshes = false;
  167. /**
  168. * An event triggered when the material is disposed.
  169. * @type {BABYLON.Observable}
  170. */
  171. public onDisposeObservable = new Observable<Material>();
  172. private _onDisposeObserver: Observer<Material>;
  173. public set onDispose(callback: () => void) {
  174. if (this._onDisposeObserver) {
  175. this.onDisposeObservable.remove(this._onDisposeObserver);
  176. }
  177. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  178. }
  179. /**
  180. * An event triggered when the material is bound.
  181. * @type {BABYLON.Observable}
  182. */
  183. public onBindObservable = new Observable<AbstractMesh>();
  184. private _onBindObserver: Observer<AbstractMesh>;
  185. public set onBind(callback: (Mesh: AbstractMesh) => void) {
  186. if (this._onBindObserver) {
  187. this.onBindObservable.remove(this._onBindObserver);
  188. }
  189. this._onBindObserver = this.onBindObservable.add(callback);
  190. }
  191. /**
  192. * An event triggered when the material is unbound.
  193. * @type {BABYLON.Observable}
  194. */
  195. public onUnBindObservable = new Observable<Material>();
  196. @serialize()
  197. public alphaMode = Engine.ALPHA_COMBINE;
  198. @serialize()
  199. public disableDepthWrite = false;
  200. @serialize("fogEnabled")
  201. private _fogEnabled = true;
  202. public set fogEnabled(value : boolean) {
  203. if (this._fogEnabled === value) {
  204. return;
  205. }
  206. this._fogEnabled = value;
  207. this.markAsDirty(Material.MiscDirtyFlag);
  208. }
  209. public get fogEnabled(): boolean {
  210. return this._fogEnabled;
  211. }
  212. @serialize()
  213. public pointSize = 1.0;
  214. @serialize()
  215. public zOffset = 0;
  216. @serialize()
  217. public get wireframe(): boolean {
  218. return this._fillMode === Material.WireFrameFillMode;
  219. }
  220. public set wireframe(value: boolean) {
  221. this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
  222. }
  223. @serialize()
  224. public get pointsCloud(): boolean {
  225. return this._fillMode === Material.PointFillMode;
  226. }
  227. public set pointsCloud(value: boolean) {
  228. this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
  229. }
  230. @serialize()
  231. public get fillMode(): number {
  232. return this._fillMode;
  233. }
  234. public set fillMode(value: number) {
  235. if (this._fillMode === value) {
  236. return;
  237. }
  238. this._fillMode = value;
  239. this.markAsDirty(Material.MiscDirtyFlag);
  240. }
  241. public _effect: Effect;
  242. public _wasPreviouslyReady = false;
  243. private _scene: Scene;
  244. private _fillMode = Material.TriangleFillMode;
  245. private _cachedDepthWriteState: boolean;
  246. constructor(name: string, scene: Scene, doNotAdd?: boolean) {
  247. this.name = name;
  248. this.id = name;
  249. this._scene = scene || Engine.LastCreatedScene;
  250. if (this._scene.useRightHandedSystem) {
  251. this.sideOrientation = Material.ClockWiseSideOrientation;
  252. } else {
  253. this.sideOrientation = Material.CounterClockWiseSideOrientation;
  254. }
  255. if (!doNotAdd) {
  256. this._scene.materials.push(this);
  257. }
  258. }
  259. /**
  260. * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
  261. * subclasses should override adding information pertainent to themselves
  262. */
  263. public toString(fullDetails? : boolean) : string {
  264. var ret = "Name: " + this.name;
  265. if (fullDetails){
  266. }
  267. return ret;
  268. }
  269. /**
  270. * Child classes can use it to update shaders
  271. */
  272. public markAsDirty(flag: number): void {
  273. }
  274. public getClassName(): string {
  275. return "Material";
  276. }
  277. public get isFrozen(): boolean {
  278. return this.checkReadyOnlyOnce;
  279. }
  280. public freeze(): void {
  281. this.checkReadyOnlyOnce = true;
  282. }
  283. public unfreeze(): void {
  284. this.checkReadyOnlyOnce = false;
  285. }
  286. public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
  287. return true;
  288. }
  289. public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {
  290. return false;
  291. }
  292. public getEffect(): Effect {
  293. return this._effect;
  294. }
  295. public getScene(): Scene {
  296. return this._scene;
  297. }
  298. public needAlphaBlending(): boolean {
  299. return (this.alpha < 1.0);
  300. }
  301. public needAlphaTesting(): boolean {
  302. return false;
  303. }
  304. public getAlphaTestTexture(): BaseTexture {
  305. return null;
  306. }
  307. public markDirty(): void {
  308. this._wasPreviouslyReady = false;
  309. }
  310. public _preBind(effect?: Effect): void {
  311. var engine = this._scene.getEngine();
  312. var reverse = this.sideOrientation === Material.ClockWiseSideOrientation;
  313. engine.enableEffect(effect ? effect : this._effect);
  314. engine.setState(this.backFaceCulling, this.zOffset, false, reverse);
  315. }
  316. public bind(world: Matrix, mesh?: Mesh): void {
  317. this._scene._cachedMaterial = this;
  318. this.onBindObservable.notifyObservers(mesh);
  319. if (this.disableDepthWrite) {
  320. var engine = this._scene.getEngine();
  321. this._cachedDepthWriteState = engine.getDepthWrite();
  322. engine.setDepthWrite(false);
  323. }
  324. }
  325. public bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void {
  326. }
  327. public bindOnlyWorldMatrix(world: Matrix): void {
  328. }
  329. public unbind(): void {
  330. this.onUnBindObservable.notifyObservers(this);
  331. if (this.disableDepthWrite) {
  332. var engine = this._scene.getEngine();
  333. engine.setDepthWrite(this._cachedDepthWriteState);
  334. }
  335. }
  336. public clone(name: string): Material {
  337. return null;
  338. }
  339. public getBindedMeshes(): AbstractMesh[] {
  340. var result = new Array<AbstractMesh>();
  341. for (var index = 0; index < this._scene.meshes.length; index++) {
  342. var mesh = this._scene.meshes[index];
  343. if (mesh.material === this) {
  344. result.push(mesh);
  345. }
  346. }
  347. return result;
  348. }
  349. public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
  350. // Animations
  351. this.getScene().stopAnimation(this);
  352. // Remove from scene
  353. var index = this._scene.materials.indexOf(this);
  354. if (index >= 0) {
  355. this._scene.materials.splice(index, 1);
  356. }
  357. // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
  358. if (forceDisposeEffect && this._effect) {
  359. this._scene.getEngine()._releaseEffect(this._effect);
  360. this._effect = null;
  361. }
  362. // Remove from meshes
  363. for (index = 0; index < this._scene.meshes.length; index++) {
  364. var mesh = this._scene.meshes[index];
  365. if (mesh.material === this) {
  366. mesh.material = null;
  367. }
  368. }
  369. // Callback
  370. this.onDisposeObservable.notifyObservers(this);
  371. this.onDisposeObservable.clear();
  372. this.onBindObservable.clear();
  373. this.onUnBindObservable.clear();
  374. }
  375. public serialize(): any {
  376. return SerializationHelper.Serialize(this);
  377. }
  378. public static ParseMultiMaterial(parsedMultiMaterial: any, scene: Scene): MultiMaterial {
  379. var multiMaterial = new BABYLON.MultiMaterial(parsedMultiMaterial.name, scene);
  380. multiMaterial.id = parsedMultiMaterial.id;
  381. Tags.AddTagsTo(multiMaterial, parsedMultiMaterial.tags);
  382. for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
  383. var subMatId = parsedMultiMaterial.materials[matIndex];
  384. if (subMatId) {
  385. multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
  386. } else {
  387. multiMaterial.subMaterials.push(null);
  388. }
  389. }
  390. return multiMaterial;
  391. }
  392. public static Parse(parsedMaterial: any, scene: Scene, rootUrl: string) {
  393. if (!parsedMaterial.customType) {
  394. return StandardMaterial.Parse(parsedMaterial, scene, rootUrl);
  395. }
  396. var materialType = Tools.Instantiate(parsedMaterial.customType);
  397. return materialType.Parse(parsedMaterial, scene, rootUrl);;
  398. }
  399. }
  400. }