babylon.material.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. module BABYLON {
  2. export class MaterialDefines {
  3. private _keys: string[];
  4. private _isDirty = true;
  5. public _renderId: number;
  6. public _areLightsDirty = true;
  7. public _areAttributesDirty = true;
  8. public _areTexturesDirty = true;
  9. public _areFresnelDirty = true;
  10. public _areMiscDirty = true;
  11. public _normals = false;
  12. public _uvs = false;
  13. public _needNormals = false;
  14. public _needUVs = false;
  15. public get isDirty(): boolean {
  16. return this._isDirty;
  17. }
  18. public markAsProcessed() {
  19. this._isDirty = false;
  20. this._areAttributesDirty = false;
  21. this._areTexturesDirty = false;
  22. this._areFresnelDirty = false;
  23. this._areLightsDirty = false;
  24. this._areMiscDirty = false;
  25. }
  26. public markAsUnprocessed() {
  27. this._isDirty = true;
  28. }
  29. public markAllAsDirty() {
  30. this._areTexturesDirty = true;
  31. this._areAttributesDirty = true;
  32. this._areLightsDirty = true;
  33. this._areFresnelDirty = true;
  34. this._areMiscDirty = true;
  35. this._isDirty = true;
  36. }
  37. public markAsLightDirty() {
  38. this._areLightsDirty = true;
  39. this._isDirty = true;
  40. }
  41. public markAsAttributesDirty() {
  42. this._areAttributesDirty = true;
  43. this._isDirty = true;
  44. }
  45. public markAsTexturesDirty() {
  46. this._areTexturesDirty = true;
  47. this._isDirty = true;
  48. }
  49. public markAsFresnelDirty() {
  50. this._areFresnelDirty = true;
  51. this._isDirty = true;
  52. }
  53. public markAsMiscDirty() {
  54. this._areMiscDirty = true;
  55. this._isDirty = true;
  56. }
  57. public rebuild() {
  58. if (this._keys) {
  59. delete this._keys;
  60. }
  61. this._keys = [];
  62. for (var key of Object.keys(this)) {
  63. if (key[0] === "_") {
  64. continue;
  65. }
  66. this._keys.push(key);
  67. }
  68. }
  69. public isEqual(other: MaterialDefines): boolean {
  70. if (this._keys.length !== other._keys.length) {
  71. return false;
  72. }
  73. for (var index = 0; index < this._keys.length; index++) {
  74. var prop = this._keys[index];
  75. if (this[prop] !== other[prop]) {
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. public cloneTo(other: MaterialDefines): void {
  82. if (this._keys.length !== other._keys.length) {
  83. other._keys = this._keys.slice(0);
  84. }
  85. for (var index = 0; index < this._keys.length; index++) {
  86. var prop = this._keys[index];
  87. other[prop] = this[prop];
  88. }
  89. }
  90. public reset(): void {
  91. for (var index = 0; index < this._keys.length; index++) {
  92. var prop = this._keys[index];
  93. if (typeof (this[prop]) === "number") {
  94. this[prop] = 0;
  95. } else {
  96. this[prop] = false;
  97. }
  98. }
  99. }
  100. public toString(): string {
  101. var result = "";
  102. for (var index = 0; index < this._keys.length; index++) {
  103. var prop = this._keys[index];
  104. var value = this[prop];
  105. if (typeof (value) === "number") {
  106. result += "#define " + prop + " " + this[prop] + "\n";
  107. } else if (value) {
  108. result += "#define " + prop + "\n";
  109. }
  110. }
  111. return result;
  112. }
  113. }
  114. export class Material {
  115. private static _TriangleFillMode = 0;
  116. private static _WireFrameFillMode = 1;
  117. private static _PointFillMode = 2;
  118. public static get TriangleFillMode(): number {
  119. return Material._TriangleFillMode;
  120. }
  121. public static get WireFrameFillMode(): number {
  122. return Material._WireFrameFillMode;
  123. }
  124. public static get PointFillMode(): number {
  125. return Material._PointFillMode;
  126. }
  127. private static _ClockWiseSideOrientation = 0;
  128. private static _CounterClockWiseSideOrientation = 1;
  129. public static get ClockWiseSideOrientation(): number {
  130. return Material._ClockWiseSideOrientation;
  131. }
  132. public static get CounterClockWiseSideOrientation(): number {
  133. return Material._CounterClockWiseSideOrientation;
  134. }
  135. private static _TextureDirtyFlag = 1;
  136. private static _LightDirtyFlag = 2;
  137. private static _FresnelDirtyFlag = 4;
  138. private static _AttributesDirtyFlag = 8;
  139. private static _MiscDirtyFlag = 16;
  140. public static get TextureDirtyFlag(): number {
  141. return Material._TextureDirtyFlag;
  142. }
  143. public static get LightDirtyFlag(): number {
  144. return Material._LightDirtyFlag;
  145. }
  146. public static get FresnelDirtyFlag(): number {
  147. return Material._FresnelDirtyFlag;
  148. }
  149. public static get AttributesDirtyFlag(): number {
  150. return Material._AttributesDirtyFlag;
  151. }
  152. public static get MiscDirtyFlag(): number {
  153. return Material._MiscDirtyFlag;
  154. }
  155. @serialize()
  156. public id: string;
  157. @serialize()
  158. public name: string;
  159. @serialize()
  160. public checkReadyOnEveryCall = false;
  161. @serialize()
  162. public checkReadyOnlyOnce = false;
  163. @serialize()
  164. public state = "";
  165. @serialize()
  166. public alpha = 1.0;
  167. @serialize("backFaceCulling")
  168. protected _backFaceCulling = true;
  169. public set backFaceCulling(value : boolean) {
  170. if (this._backFaceCulling === value) {
  171. return;
  172. }
  173. this._backFaceCulling = value;
  174. this.markAsDirty(Material.TextureDirtyFlag);
  175. }
  176. public get backFaceCulling(): boolean {
  177. return this._backFaceCulling;
  178. }
  179. @serialize()
  180. public sideOrientation: number;
  181. public onCompiled: (effect: Effect) => void;
  182. public onError: (effect: Effect, errors: string) => void;
  183. public getRenderTargetTextures: () => SmartArray<RenderTargetTexture>;
  184. public doNotSerialize = false;
  185. public storeEffectOnSubMeshes = false;
  186. /**
  187. * An event triggered when the material is disposed.
  188. * @type {BABYLON.Observable}
  189. */
  190. public onDisposeObservable = new Observable<Material>();
  191. private _onDisposeObserver: Observer<Material>;
  192. public set onDispose(callback: () => void) {
  193. if (this._onDisposeObserver) {
  194. this.onDisposeObservable.remove(this._onDisposeObserver);
  195. }
  196. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  197. }
  198. /**
  199. * An event triggered when the material is bound.
  200. * @type {BABYLON.Observable}
  201. */
  202. public onBindObservable = new Observable<AbstractMesh>();
  203. private _onBindObserver: Observer<AbstractMesh>;
  204. public set onBind(callback: (Mesh: AbstractMesh) => void) {
  205. if (this._onBindObserver) {
  206. this.onBindObservable.remove(this._onBindObserver);
  207. }
  208. this._onBindObserver = this.onBindObservable.add(callback);
  209. }
  210. /**
  211. * An event triggered when the material is unbound.
  212. * @type {BABYLON.Observable}
  213. */
  214. public onUnBindObservable = new Observable<Material>();
  215. @serialize()
  216. public alphaMode = Engine.ALPHA_COMBINE;
  217. @serialize()
  218. public disableDepthWrite = false;
  219. @serialize("fogEnabled")
  220. private _fogEnabled = true;
  221. public set fogEnabled(value : boolean) {
  222. if (this._fogEnabled === value) {
  223. return;
  224. }
  225. this._fogEnabled = value;
  226. this.markAsDirty(Material.MiscDirtyFlag);
  227. }
  228. public get fogEnabled(): boolean {
  229. return this._fogEnabled;
  230. }
  231. @serialize()
  232. public pointSize = 1.0;
  233. @serialize()
  234. public zOffset = 0;
  235. @serialize()
  236. public get wireframe(): boolean {
  237. return this._fillMode === Material.WireFrameFillMode;
  238. }
  239. public set wireframe(value: boolean) {
  240. this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
  241. }
  242. @serialize()
  243. public get pointsCloud(): boolean {
  244. return this._fillMode === Material.PointFillMode;
  245. }
  246. public set pointsCloud(value: boolean) {
  247. this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
  248. }
  249. @serialize()
  250. public get fillMode(): number {
  251. return this._fillMode;
  252. }
  253. public set fillMode(value: number) {
  254. if (this._fillMode === value) {
  255. return;
  256. }
  257. this._fillMode = value;
  258. this.markAsDirty(Material.MiscDirtyFlag);
  259. }
  260. public _effect: Effect;
  261. public _wasPreviouslyReady = false;
  262. private _useUBO: boolean;
  263. private _scene: Scene;
  264. private _fillMode = Material.TriangleFillMode;
  265. private _cachedDepthWriteState: boolean;
  266. protected _uniformBuffer: UniformBuffer;
  267. constructor(name: string, scene: Scene, doNotAdd?: boolean) {
  268. this.name = name;
  269. this.id = name;
  270. this._scene = scene || Engine.LastCreatedScene;
  271. if (this._scene.useRightHandedSystem) {
  272. this.sideOrientation = Material.ClockWiseSideOrientation;
  273. } else {
  274. this.sideOrientation = Material.CounterClockWiseSideOrientation;
  275. }
  276. this._uniformBuffer = new UniformBuffer(this._scene.getEngine());
  277. this._useUBO = this.getScene().getEngine().webGLVersion > 1;
  278. if (!doNotAdd) {
  279. this._scene.materials.push(this);
  280. }
  281. }
  282. /**
  283. * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
  284. * subclasses should override adding information pertainent to themselves
  285. */
  286. public toString(fullDetails? : boolean) : string {
  287. var ret = "Name: " + this.name;
  288. if (fullDetails){
  289. }
  290. return ret;
  291. }
  292. /**
  293. * Child classes can use it to update shaders
  294. */
  295. public getClassName(): string {
  296. return "Material";
  297. }
  298. public get isFrozen(): boolean {
  299. return this.checkReadyOnlyOnce;
  300. }
  301. public freeze(): void {
  302. this.checkReadyOnlyOnce = true;
  303. }
  304. public unfreeze(): void {
  305. this.checkReadyOnlyOnce = false;
  306. }
  307. public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
  308. return true;
  309. }
  310. public isReadyForSubMesh(mesh: AbstractMesh, subMesh: BaseSubMesh, useInstances?: boolean): boolean {
  311. return false;
  312. }
  313. public getEffect(): Effect {
  314. return this._effect;
  315. }
  316. public getScene(): Scene {
  317. return this._scene;
  318. }
  319. public needAlphaBlending(): boolean {
  320. return (this.alpha < 1.0);
  321. }
  322. public needAlphaTesting(): boolean {
  323. return false;
  324. }
  325. public getAlphaTestTexture(): BaseTexture {
  326. return null;
  327. }
  328. public markDirty(): void {
  329. this._wasPreviouslyReady = false;
  330. }
  331. public _preBind(effect?: Effect): void {
  332. var engine = this._scene.getEngine();
  333. var reverse = this.sideOrientation === Material.ClockWiseSideOrientation;
  334. engine.enableEffect(effect ? effect : this._effect);
  335. engine.setState(this.backFaceCulling, this.zOffset, false, reverse);
  336. }
  337. public bind(world: Matrix, mesh?: Mesh): void {
  338. }
  339. public bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void {
  340. }
  341. public bindOnlyWorldMatrix(world: Matrix): void {
  342. }
  343. public bindSceneUniformBuffer(effect: Effect, sceneUbo: UniformBuffer): void {
  344. sceneUbo.bindToEffect(effect, "Scene");
  345. }
  346. public bindView(effect: Effect): void {
  347. if (!this._useUBO) {
  348. effect.setMatrix("view", this.getScene().getViewMatrix());
  349. } else {
  350. this.bindSceneUniformBuffer(effect, this.getScene().getSceneUniformBuffer());
  351. }
  352. }
  353. public bindViewProjection(effect: Effect): void {
  354. if (!this._useUBO) {
  355. effect.setMatrix("viewProjection", this.getScene().getTransformMatrix());
  356. } else {
  357. this.bindSceneUniformBuffer(effect, this.getScene().getSceneUniformBuffer());
  358. }
  359. }
  360. protected _afterBind(mesh: Mesh): void {
  361. this._scene._cachedMaterial = this;
  362. this.onBindObservable.notifyObservers(mesh);
  363. if (this.disableDepthWrite) {
  364. var engine = this._scene.getEngine();
  365. this._cachedDepthWriteState = engine.getDepthWrite();
  366. engine.setDepthWrite(false);
  367. }
  368. }
  369. public unbind(): void {
  370. this.onUnBindObservable.notifyObservers(this);
  371. if (this.disableDepthWrite) {
  372. var engine = this._scene.getEngine();
  373. engine.setDepthWrite(this._cachedDepthWriteState);
  374. }
  375. }
  376. public getActiveTextures(): BaseTexture[] {
  377. return [];
  378. }
  379. public clone(name: string): Material {
  380. return null;
  381. }
  382. public getBindedMeshes(): AbstractMesh[] {
  383. var result = new Array<AbstractMesh>();
  384. for (var index = 0; index < this._scene.meshes.length; index++) {
  385. var mesh = this._scene.meshes[index];
  386. if (mesh.material === this) {
  387. result.push(mesh);
  388. }
  389. }
  390. return result;
  391. }
  392. // Force shader compilation including textures ready check
  393. public forceCompilation(mesh: AbstractMesh, onCompiled: (material: Material) => void, options?: { alphaTest: boolean }): void {
  394. var subMesh = new BaseSubMesh();
  395. var scene = this.getScene();
  396. var engine = scene.getEngine();
  397. var beforeRenderCallback = () => {
  398. if (subMesh._materialDefines) {
  399. subMesh._materialDefines._renderId = -1;
  400. }
  401. var alphaTestState = engine.getAlphaTesting();
  402. engine.setAlphaTesting(options ? options.alphaTest : this.needAlphaTesting());
  403. if (this.isReadyForSubMesh(mesh, subMesh)) {
  404. scene.unregisterBeforeRender(beforeRenderCallback);
  405. if (onCompiled) {
  406. onCompiled(this);
  407. }
  408. }
  409. engine.setAlphaTesting(alphaTestState);
  410. };
  411. scene.registerBeforeRender(beforeRenderCallback);
  412. }
  413. public markAsDirty(flag: number): void {
  414. if (flag & Material.TextureDirtyFlag) {
  415. this._markAllSubMeshesAsTexturesDirty();
  416. }
  417. if (flag & Material.LightDirtyFlag) {
  418. this._markAllSubMeshesAsLightsDirty();
  419. }
  420. if (flag & Material.FresnelDirtyFlag) {
  421. this._markAllSubMeshesAsFresnelDirty();
  422. }
  423. if (flag & Material.AttributesDirtyFlag) {
  424. this._markAllSubMeshesAsAttributesDirty();
  425. }
  426. if (flag & Material.MiscDirtyFlag) {
  427. this._markAllSubMeshesAsMiscDirty();
  428. }
  429. this.getScene().resetCachedMaterial();
  430. }
  431. protected _markAllSubMeshesAsDirty(func: (defines: MaterialDefines) => void) {
  432. for (var mesh of this.getScene().meshes) {
  433. if (!mesh.subMeshes) {
  434. continue;
  435. }
  436. for (var subMesh of mesh.subMeshes) {
  437. if (subMesh.getMaterial() !== this) {
  438. continue;
  439. }
  440. if (!subMesh._materialDefines) {
  441. return;
  442. }
  443. func(subMesh._materialDefines);
  444. }
  445. }
  446. }
  447. protected _markAllSubMeshesAsTexturesDirty() {
  448. this._markAllSubMeshesAsDirty(defines => defines.markAsTexturesDirty());
  449. }
  450. protected _markAllSubMeshesAsFresnelDirty() {
  451. this._markAllSubMeshesAsDirty(defines => defines.markAsFresnelDirty());
  452. }
  453. protected _markAllSubMeshesAsLightsDirty() {
  454. this._markAllSubMeshesAsDirty(defines => defines.markAsLightDirty());
  455. }
  456. protected _markAllSubMeshesAsAttributesDirty() {
  457. this._markAllSubMeshesAsDirty(defines => defines.markAsAttributesDirty());
  458. }
  459. protected _markAllSubMeshesAsMiscDirty() {
  460. this._markAllSubMeshesAsDirty(defines => defines.markAsMiscDirty());
  461. }
  462. public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
  463. // Animations
  464. this.getScene().stopAnimation(this);
  465. // Remove from scene
  466. var index = this._scene.materials.indexOf(this);
  467. if (index >= 0) {
  468. this._scene.materials.splice(index, 1);
  469. }
  470. // Remove from meshes
  471. for (index = 0; index < this._scene.meshes.length; index++) {
  472. var mesh = this._scene.meshes[index];
  473. if (mesh.material === this) {
  474. mesh.material = null;
  475. if ((<Mesh>mesh).geometry) {
  476. var geometry = (<Mesh>mesh).geometry;
  477. if (this.storeEffectOnSubMeshes) {
  478. for (var subMesh of mesh.subMeshes) {
  479. geometry._releaseVertexArrayObject(subMesh._materialEffect);
  480. }
  481. } else {
  482. geometry._releaseVertexArrayObject(this._effect)
  483. }
  484. }
  485. }
  486. }
  487. this._uniformBuffer.dispose();
  488. // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
  489. if (forceDisposeEffect && this._effect) {
  490. if (this.storeEffectOnSubMeshes) {
  491. for (var subMesh of mesh.subMeshes) {
  492. this._scene.getEngine()._releaseEffect(subMesh._materialEffect);
  493. }
  494. } else {
  495. this._scene.getEngine()._releaseEffect(this._effect);
  496. }
  497. this._effect = null;
  498. }
  499. // Callback
  500. this.onDisposeObservable.notifyObservers(this);
  501. this.onDisposeObservable.clear();
  502. this.onBindObservable.clear();
  503. this.onUnBindObservable.clear();
  504. }
  505. public serialize(): any {
  506. return SerializationHelper.Serialize(this);
  507. }
  508. public static ParseMultiMaterial(parsedMultiMaterial: any, scene: Scene): MultiMaterial {
  509. var multiMaterial = new BABYLON.MultiMaterial(parsedMultiMaterial.name, scene);
  510. multiMaterial.id = parsedMultiMaterial.id;
  511. if (Tags) {
  512. Tags.AddTagsTo(multiMaterial, parsedMultiMaterial.tags);
  513. }
  514. for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
  515. var subMatId = parsedMultiMaterial.materials[matIndex];
  516. if (subMatId) {
  517. multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
  518. } else {
  519. multiMaterial.subMaterials.push(null);
  520. }
  521. }
  522. return multiMaterial;
  523. }
  524. public static Parse(parsedMaterial: any, scene: Scene, rootUrl: string) {
  525. if (!parsedMaterial.customType) {
  526. return StandardMaterial.Parse(parsedMaterial, scene, rootUrl);
  527. }
  528. if (parsedMaterial.customType === "BABYLON.PBRMaterial" && parsedMaterial.overloadedAlbedo) {
  529. parsedMaterial.customType = "BABYLON.LegacyPBRMaterial";
  530. if (!(<any>BABYLON).LegacyPBRMaterial) {
  531. BABYLON.Tools.Error("Your scene is trying to load a legacy version of the PBRMaterial, please, include it from the materials library.");
  532. return;
  533. }
  534. }
  535. var materialType = Tools.Instantiate(parsedMaterial.customType);
  536. return materialType.Parse(parsedMaterial, scene, rootUrl);;
  537. }
  538. }
  539. }