effectLayer.ts 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. import { serialize, serializeAsColor4, serializeAsCameraReference } from "../Misc/decorators";
  2. import { Tools } from "../Misc/tools";
  3. import { SmartArray } from "../Misc/smartArray";
  4. import { Observable } from "../Misc/observable";
  5. import { Nullable } from "../types";
  6. import { Camera } from "../Cameras/camera";
  7. import { Scene } from "../scene";
  8. import { ISize } from "../Maths/math.size";
  9. import { Color4 } from '../Maths/math.color';
  10. import { Engine } from "../Engines/engine";
  11. import { EngineStore } from "../Engines/engineStore";
  12. import { VertexBuffer } from "../Meshes/buffer";
  13. import { SubMesh } from "../Meshes/subMesh";
  14. import { AbstractMesh } from "../Meshes/abstractMesh";
  15. import { Mesh } from "../Meshes/mesh";
  16. import { PostProcess } from "../PostProcesses/postProcess";
  17. import { BaseTexture } from "../Materials/Textures/baseTexture";
  18. import { Texture } from "../Materials/Textures/texture";
  19. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture";
  20. import { Effect } from "../Materials/effect";
  21. import { Material } from "../Materials/material";
  22. import { MaterialHelper } from "../Materials/materialHelper";
  23. import { Constants } from "../Engines/constants";
  24. import "../Shaders/glowMapGeneration.fragment";
  25. import "../Shaders/glowMapGeneration.vertex";
  26. import { _DevTools } from '../Misc/devTools';
  27. import { DataBuffer } from '../Meshes/dataBuffer';
  28. import { EffectFallbacks } from '../Materials/effectFallbacks';
  29. /**
  30. * Effect layer options. This helps customizing the behaviour
  31. * of the effect layer.
  32. */
  33. export interface IEffectLayerOptions {
  34. /**
  35. * Multiplication factor apply to the canvas size to compute the render target size
  36. * used to generated the objects (the smaller the faster).
  37. */
  38. mainTextureRatio: number;
  39. /**
  40. * Enforces a fixed size texture to ensure effect stability across devices.
  41. */
  42. mainTextureFixedSize?: number;
  43. /**
  44. * Alpha blending mode used to apply the blur. Default depends of the implementation.
  45. */
  46. alphaBlendingMode: number;
  47. /**
  48. * The camera attached to the layer.
  49. */
  50. camera: Nullable<Camera>;
  51. /**
  52. * The rendering group to draw the layer in.
  53. */
  54. renderingGroupId: number;
  55. }
  56. /**
  57. * The effect layer Helps adding post process effect blended with the main pass.
  58. *
  59. * This can be for instance use to generate glow or higlight effects on the scene.
  60. *
  61. * The effect layer class can not be used directly and is intented to inherited from to be
  62. * customized per effects.
  63. */
  64. export abstract class EffectLayer {
  65. private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
  66. private _indexBuffer: Nullable<DataBuffer>;
  67. private _cachedDefines: string;
  68. private _effectLayerMapGenerationEffect: Effect;
  69. private _effectLayerOptions: IEffectLayerOptions;
  70. private _mergeEffect: Effect;
  71. protected _scene: Scene;
  72. protected _engine: Engine;
  73. protected _maxSize: number = 0;
  74. protected _mainTextureDesiredSize: ISize = { width: 0, height: 0 };
  75. protected _mainTexture: RenderTargetTexture;
  76. protected _shouldRender = true;
  77. protected _postProcesses: PostProcess[] = [];
  78. protected _textures: BaseTexture[] = [];
  79. protected _emissiveTextureAndColor: { texture: Nullable<BaseTexture>, color: Color4 } = { texture: null, color: new Color4() };
  80. /**
  81. * The name of the layer
  82. */
  83. @serialize()
  84. public name: string;
  85. /**
  86. * The clear color of the texture used to generate the glow map.
  87. */
  88. @serializeAsColor4()
  89. public neutralColor: Color4 = new Color4();
  90. /**
  91. * Specifies whether the highlight layer is enabled or not.
  92. */
  93. @serialize()
  94. public isEnabled: boolean = true;
  95. /**
  96. * Gets the camera attached to the layer.
  97. */
  98. @serializeAsCameraReference()
  99. public get camera(): Nullable<Camera> {
  100. return this._effectLayerOptions.camera;
  101. }
  102. /**
  103. * Gets the rendering group id the layer should render in.
  104. */
  105. @serialize()
  106. public get renderingGroupId(): number {
  107. return this._effectLayerOptions.renderingGroupId;
  108. }
  109. public set renderingGroupId(renderingGroupId: number) {
  110. this._effectLayerOptions.renderingGroupId = renderingGroupId;
  111. }
  112. /**
  113. * Specifies if the bounding boxes should be rendered normally or if they should undergo the effect of the layer
  114. */
  115. @serialize()
  116. public disableBoundingBoxesFromEffectLayer = false;
  117. /**
  118. * An event triggered when the effect layer has been disposed.
  119. */
  120. public onDisposeObservable = new Observable<EffectLayer>();
  121. /**
  122. * An event triggered when the effect layer is about rendering the main texture with the glowy parts.
  123. */
  124. public onBeforeRenderMainTextureObservable = new Observable<EffectLayer>();
  125. /**
  126. * An event triggered when the generated texture is being merged in the scene.
  127. */
  128. public onBeforeComposeObservable = new Observable<EffectLayer>();
  129. /**
  130. * An event triggered when the mesh is rendered into the effect render target.
  131. */
  132. public onBeforeRenderMeshToEffect = new Observable<AbstractMesh>();
  133. /**
  134. * An event triggered after the mesh has been rendered into the effect render target.
  135. */
  136. public onAfterRenderMeshToEffect = new Observable<AbstractMesh>();
  137. /**
  138. * An event triggered when the generated texture has been merged in the scene.
  139. */
  140. public onAfterComposeObservable = new Observable<EffectLayer>();
  141. /**
  142. * An event triggered when the efffect layer changes its size.
  143. */
  144. public onSizeChangedObservable = new Observable<EffectLayer>();
  145. /** @hidden */
  146. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  147. throw _DevTools.WarnImport("EffectLayerSceneComponent");
  148. }
  149. /**
  150. * Instantiates a new effect Layer and references it in the scene.
  151. * @param name The name of the layer
  152. * @param scene The scene to use the layer in
  153. */
  154. constructor(
  155. /** The Friendly of the effect in the scene */
  156. name: string,
  157. scene: Scene) {
  158. this.name = name;
  159. this._scene = scene || EngineStore.LastCreatedScene;
  160. EffectLayer._SceneComponentInitialization(this._scene);
  161. this._engine = this._scene.getEngine();
  162. this._maxSize = this._engine.getCaps().maxTextureSize;
  163. this._scene.effectLayers.push(this);
  164. // Generate Buffers
  165. this._generateIndexBuffer();
  166. this._generateVertexBuffer();
  167. }
  168. /**
  169. * Get the effect name of the layer.
  170. * @return The effect name
  171. */
  172. public abstract getEffectName(): string;
  173. /**
  174. * Checks for the readiness of the element composing the layer.
  175. * @param subMesh the mesh to check for
  176. * @param useInstances specify whether or not to use instances to render the mesh
  177. * @return true if ready otherwise, false
  178. */
  179. public abstract isReady(subMesh: SubMesh, useInstances: boolean): boolean;
  180. /**
  181. * Returns whether or nood the layer needs stencil enabled during the mesh rendering.
  182. * @returns true if the effect requires stencil during the main canvas render pass.
  183. */
  184. public abstract needStencil(): boolean;
  185. /**
  186. * Create the merge effect. This is the shader use to blit the information back
  187. * to the main canvas at the end of the scene rendering.
  188. * @returns The effect containing the shader used to merge the effect on the main canvas
  189. */
  190. protected abstract _createMergeEffect(): Effect;
  191. /**
  192. * Creates the render target textures and post processes used in the effect layer.
  193. */
  194. protected abstract _createTextureAndPostProcesses(): void;
  195. /**
  196. * Implementation specific of rendering the generating effect on the main canvas.
  197. * @param effect The effect used to render through
  198. */
  199. protected abstract _internalRender(effect: Effect): void;
  200. /**
  201. * Sets the required values for both the emissive texture and and the main color.
  202. */
  203. protected abstract _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void;
  204. /**
  205. * Free any resources and references associated to a mesh.
  206. * Internal use
  207. * @param mesh The mesh to free.
  208. */
  209. public abstract _disposeMesh(mesh: Mesh): void;
  210. /**
  211. * Serializes this layer (Glow or Highlight for example)
  212. * @returns a serialized layer object
  213. */
  214. public abstract serialize?(): any;
  215. /**
  216. * Initializes the effect layer with the required options.
  217. * @param options Sets of none mandatory options to use with the layer (see IEffectLayerOptions for more information)
  218. */
  219. protected _init(options: Partial<IEffectLayerOptions>): void {
  220. // Adapt options
  221. this._effectLayerOptions = {
  222. mainTextureRatio: 0.5,
  223. alphaBlendingMode: Constants.ALPHA_COMBINE,
  224. camera: null,
  225. renderingGroupId: -1,
  226. ...options,
  227. };
  228. this._setMainTextureSize();
  229. this._createMainTexture();
  230. this._createTextureAndPostProcesses();
  231. this._mergeEffect = this._createMergeEffect();
  232. }
  233. /**
  234. * Generates the index buffer of the full screen quad blending to the main canvas.
  235. */
  236. private _generateIndexBuffer(): void {
  237. // Indices
  238. var indices = [];
  239. indices.push(0);
  240. indices.push(1);
  241. indices.push(2);
  242. indices.push(0);
  243. indices.push(2);
  244. indices.push(3);
  245. this._indexBuffer = this._engine.createIndexBuffer(indices);
  246. }
  247. /**
  248. * Generates the vertex buffer of the full screen quad blending to the main canvas.
  249. */
  250. private _generateVertexBuffer(): void {
  251. // VBO
  252. var vertices = [];
  253. vertices.push(1, 1);
  254. vertices.push(-1, 1);
  255. vertices.push(-1, -1);
  256. vertices.push(1, -1);
  257. var vertexBuffer = new VertexBuffer(this._engine, vertices, VertexBuffer.PositionKind, false, false, 2);
  258. this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
  259. }
  260. /**
  261. * Sets the main texture desired size which is the closest power of two
  262. * of the engine canvas size.
  263. */
  264. private _setMainTextureSize(): void {
  265. if (this._effectLayerOptions.mainTextureFixedSize) {
  266. this._mainTextureDesiredSize.width = this._effectLayerOptions.mainTextureFixedSize;
  267. this._mainTextureDesiredSize.height = this._effectLayerOptions.mainTextureFixedSize;
  268. }
  269. else {
  270. this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._effectLayerOptions.mainTextureRatio;
  271. this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._effectLayerOptions.mainTextureRatio;
  272. this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Engine.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
  273. this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Engine.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;
  274. }
  275. this._mainTextureDesiredSize.width = Math.floor(this._mainTextureDesiredSize.width);
  276. this._mainTextureDesiredSize.height = Math.floor(this._mainTextureDesiredSize.height);
  277. }
  278. /**
  279. * Creates the main texture for the effect layer.
  280. */
  281. protected _createMainTexture(): void {
  282. this._mainTexture = new RenderTargetTexture("HighlightLayerMainRTT",
  283. {
  284. width: this._mainTextureDesiredSize.width,
  285. height: this._mainTextureDesiredSize.height
  286. },
  287. this._scene,
  288. false,
  289. true,
  290. Constants.TEXTURETYPE_UNSIGNED_INT);
  291. this._mainTexture.activeCamera = this._effectLayerOptions.camera;
  292. this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
  293. this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
  294. this._mainTexture.anisotropicFilteringLevel = 1;
  295. this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  296. this._mainTexture.renderParticles = false;
  297. this._mainTexture.renderList = null;
  298. this._mainTexture.ignoreCameraViewport = true;
  299. // Custom render function
  300. this._mainTexture.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
  301. this.onBeforeRenderMainTextureObservable.notifyObservers(this);
  302. var index: number;
  303. let engine = this._scene.getEngine();
  304. if (depthOnlySubMeshes.length) {
  305. engine.setColorWrite(false);
  306. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  307. this._renderSubMesh(depthOnlySubMeshes.data[index]);
  308. }
  309. engine.setColorWrite(true);
  310. }
  311. for (index = 0; index < opaqueSubMeshes.length; index++) {
  312. this._renderSubMesh(opaqueSubMeshes.data[index]);
  313. }
  314. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  315. this._renderSubMesh(alphaTestSubMeshes.data[index]);
  316. }
  317. const previousAlphaMode = engine.getAlphaMode();
  318. for (index = 0; index < transparentSubMeshes.length; index++) {
  319. this._renderSubMesh(transparentSubMeshes.data[index], true);
  320. }
  321. engine.setAlphaMode(previousAlphaMode);
  322. };
  323. this._mainTexture.onClearObservable.add((engine: Engine) => {
  324. engine.clear(this.neutralColor, true, true, true);
  325. });
  326. const boundingBoxRendererDisabled = this._scene.getBoundingBoxRenderer().disabled;
  327. this._mainTexture.onBeforeBindObservable.add(() => {
  328. this._scene.getBoundingBoxRenderer().disabled = this.disableBoundingBoxesFromEffectLayer || boundingBoxRendererDisabled;
  329. });
  330. this._mainTexture.onAfterUnbindObservable.add(() => {
  331. this._scene.getBoundingBoxRenderer().disabled = boundingBoxRendererDisabled;
  332. });
  333. }
  334. /**
  335. * Adds specific effects defines.
  336. * @param defines The defines to add specifics to.
  337. */
  338. protected _addCustomEffectDefines(defines: string[]): void {
  339. // Nothing to add by default.
  340. }
  341. /**
  342. * Checks for the readiness of the element composing the layer.
  343. * @param subMesh the mesh to check for
  344. * @param useInstances specify whether or not to use instances to render the mesh
  345. * @param emissiveTexture the associated emissive texture used to generate the glow
  346. * @return true if ready otherwise, false
  347. */
  348. protected _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<BaseTexture>): boolean {
  349. let material = subMesh.getMaterial();
  350. if (!material) {
  351. return false;
  352. }
  353. if (!material.isReadyForSubMesh(subMesh.getMesh(), subMesh, useInstances)) {
  354. return false;
  355. }
  356. var defines: string[] = [];
  357. var attribs = [VertexBuffer.PositionKind];
  358. var mesh = subMesh.getMesh();
  359. var uv1 = false;
  360. var uv2 = false;
  361. // Diffuse
  362. if (material) {
  363. const needAlphaTest = material.needAlphaTesting();
  364. const diffuseTexture = material.getAlphaTestTexture();
  365. const needAlphaBlendFromDiffuse = diffuseTexture && diffuseTexture.hasAlpha &&
  366. ((material as any).useAlphaFromDiffuseTexture || (material as any)._useAlphaFromAlbedoTexture);
  367. if (diffuseTexture && (needAlphaTest || needAlphaBlendFromDiffuse)) {
  368. defines.push("#define DIFFUSE");
  369. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
  370. diffuseTexture.coordinatesIndex === 1) {
  371. defines.push("#define DIFFUSEUV2");
  372. uv2 = true;
  373. }
  374. else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  375. defines.push("#define DIFFUSEUV1");
  376. uv1 = true;
  377. }
  378. if (needAlphaTest) {
  379. defines.push("#define ALPHATEST");
  380. defines.push("#define ALPHATESTVALUE 0.4");
  381. }
  382. }
  383. var opacityTexture = (material as any).opacityTexture;
  384. if (opacityTexture) {
  385. defines.push("#define OPACITY");
  386. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
  387. opacityTexture.coordinatesIndex === 1) {
  388. defines.push("#define OPACITYUV2");
  389. uv2 = true;
  390. }
  391. else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  392. defines.push("#define OPACITYUV1");
  393. uv1 = true;
  394. }
  395. }
  396. }
  397. // Emissive
  398. if (emissiveTexture) {
  399. defines.push("#define EMISSIVE");
  400. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
  401. emissiveTexture.coordinatesIndex === 1) {
  402. defines.push("#define EMISSIVEUV2");
  403. uv2 = true;
  404. }
  405. else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  406. defines.push("#define EMISSIVEUV1");
  407. uv1 = true;
  408. }
  409. }
  410. // Vertex
  411. if (mesh.isVerticesDataPresent(VertexBuffer.ColorKind) && mesh.hasVertexAlpha) {
  412. attribs.push(VertexBuffer.ColorKind);
  413. defines.push("#define VERTEXALPHA");
  414. }
  415. if (uv1) {
  416. attribs.push(VertexBuffer.UVKind);
  417. defines.push("#define UV1");
  418. }
  419. if (uv2) {
  420. attribs.push(VertexBuffer.UV2Kind);
  421. defines.push("#define UV2");
  422. }
  423. // Bones
  424. const fallbacks = new EffectFallbacks();
  425. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  426. attribs.push(VertexBuffer.MatricesIndicesKind);
  427. attribs.push(VertexBuffer.MatricesWeightsKind);
  428. if (mesh.numBoneInfluencers > 4) {
  429. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  430. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  431. }
  432. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  433. let skeleton = mesh.skeleton;
  434. if (skeleton && skeleton.isUsingTextureForMatrices) {
  435. defines.push("#define BONETEXTURE");
  436. } else {
  437. defines.push("#define BonesPerMesh " + (skeleton ? (skeleton.bones.length + 1) : 0));
  438. }
  439. if (mesh.numBoneInfluencers > 0) {
  440. fallbacks.addCPUSkinningFallback(0, mesh);
  441. }
  442. } else {
  443. defines.push("#define NUM_BONE_INFLUENCERS 0");
  444. }
  445. // Morph targets
  446. var manager = (<Mesh>mesh).morphTargetManager;
  447. let morphInfluencers = 0;
  448. if (manager) {
  449. if (manager.numInfluencers > 0) {
  450. defines.push("#define MORPHTARGETS");
  451. morphInfluencers = manager.numInfluencers;
  452. defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
  453. MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, morphInfluencers);
  454. }
  455. }
  456. // Instances
  457. if (useInstances) {
  458. defines.push("#define INSTANCES");
  459. MaterialHelper.PushAttributesForInstances(attribs);
  460. if (subMesh.getRenderingMesh().hasThinInstances) {
  461. defines.push("#define THIN_INSTANCES");
  462. }
  463. }
  464. this._addCustomEffectDefines(defines);
  465. // Get correct effect
  466. var join = defines.join("\n");
  467. if (this._cachedDefines !== join) {
  468. this._cachedDefines = join;
  469. this._effectLayerMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
  470. attribs,
  471. ["world", "mBones", "viewProjection",
  472. "glowColor", "morphTargetInfluences", "boneTextureWidth",
  473. "diffuseMatrix", "emissiveMatrix", "opacityMatrix", "opacityIntensity"],
  474. ["diffuseSampler", "emissiveSampler", "opacitySampler", "boneSampler"], join,
  475. fallbacks, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
  476. }
  477. return this._effectLayerMapGenerationEffect.isReady();
  478. }
  479. /**
  480. * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene.
  481. */
  482. public render(): void {
  483. var currentEffect = this._mergeEffect;
  484. // Check
  485. if (!currentEffect.isReady()) {
  486. return;
  487. }
  488. for (var i = 0; i < this._postProcesses.length; i++) {
  489. if (!this._postProcesses[i].isReady()) {
  490. return;
  491. }
  492. }
  493. var engine = this._scene.getEngine();
  494. this.onBeforeComposeObservable.notifyObservers(this);
  495. // Render
  496. engine.enableEffect(currentEffect);
  497. engine.setState(false);
  498. // VBOs
  499. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
  500. // Cache
  501. var previousAlphaMode = engine.getAlphaMode();
  502. // Go Blend.
  503. engine.setAlphaMode(this._effectLayerOptions.alphaBlendingMode);
  504. // Blends the map on the main canvas.
  505. this._internalRender(currentEffect);
  506. // Restore Alpha
  507. engine.setAlphaMode(previousAlphaMode);
  508. this.onAfterComposeObservable.notifyObservers(this);
  509. // Handle size changes.
  510. var size = this._mainTexture.getSize();
  511. this._setMainTextureSize();
  512. if (size.width !== this._mainTextureDesiredSize.width || size.height !== this._mainTextureDesiredSize.height) {
  513. // Recreate RTT and post processes on size change.
  514. this.onSizeChangedObservable.notifyObservers(this);
  515. this._disposeTextureAndPostProcesses();
  516. this._createMainTexture();
  517. this._createTextureAndPostProcesses();
  518. }
  519. }
  520. /**
  521. * Determine if a given mesh will be used in the current effect.
  522. * @param mesh mesh to test
  523. * @returns true if the mesh will be used
  524. */
  525. public hasMesh(mesh: AbstractMesh): boolean {
  526. if (this.renderingGroupId === -1 || mesh.renderingGroupId === this.renderingGroupId) {
  527. return true;
  528. }
  529. return false;
  530. }
  531. /**
  532. * Returns true if the layer contains information to display, otherwise false.
  533. * @returns true if the glow layer should be rendered
  534. */
  535. public shouldRender(): boolean {
  536. return this.isEnabled && this._shouldRender;
  537. }
  538. /**
  539. * Returns true if the mesh should render, otherwise false.
  540. * @param mesh The mesh to render
  541. * @returns true if it should render otherwise false
  542. */
  543. protected _shouldRenderMesh(mesh: AbstractMesh): boolean {
  544. return true;
  545. }
  546. /**
  547. * Returns true if the mesh can be rendered, otherwise false.
  548. * @param mesh The mesh to render
  549. * @param material The material used on the mesh
  550. * @returns true if it can be rendered otherwise false
  551. */
  552. protected _canRenderMesh(mesh: AbstractMesh, material: Material): boolean {
  553. return !material.needAlphaBlendingForMesh(mesh);
  554. }
  555. /**
  556. * Returns true if the mesh should render, otherwise false.
  557. * @param mesh The mesh to render
  558. * @returns true if it should render otherwise false
  559. */
  560. protected _shouldRenderEmissiveTextureForMesh(): boolean {
  561. return true;
  562. }
  563. /**
  564. * Renders the submesh passed in parameter to the generation map.
  565. */
  566. protected _renderSubMesh(subMesh: SubMesh, enableAlphaMode: boolean = false): void {
  567. if (!this.shouldRender()) {
  568. return;
  569. }
  570. var material = subMesh.getMaterial();
  571. var ownerMesh = subMesh.getMesh();
  572. var replacementMesh = subMesh.getReplacementMesh();
  573. var renderingMesh = subMesh.getRenderingMesh();
  574. var effectiveMesh = subMesh.getEffectiveMesh();
  575. var scene = this._scene;
  576. var engine = scene.getEngine();
  577. effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
  578. if (!material) {
  579. return;
  580. }
  581. // Do not block in blend mode.
  582. if (!this._canRenderMesh(renderingMesh, material)) {
  583. return;
  584. }
  585. // Culling
  586. let sideOrientation = renderingMesh.overrideMaterialSideOrientation ?? material.sideOrientation;
  587. const mainDeterminant = renderingMesh._getWorldMatrixDeterminant();
  588. if (mainDeterminant < 0) {
  589. sideOrientation = (sideOrientation === Material.ClockWiseSideOrientation ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation);
  590. }
  591. const reverse = sideOrientation === Material.ClockWiseSideOrientation;
  592. engine.setState(material.backFaceCulling, material.zOffset, undefined, reverse);
  593. // Managing instances
  594. var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!replacementMesh);
  595. if (batch.mustReturn) {
  596. return;
  597. }
  598. // Early Exit per mesh
  599. if (!this._shouldRenderMesh(renderingMesh)) {
  600. return;
  601. }
  602. var hardwareInstancedRendering = batch.hardwareInstancedRendering[subMesh._id] || renderingMesh.hasThinInstances;
  603. this._setEmissiveTextureAndColor(renderingMesh, subMesh, material);
  604. this.onBeforeRenderMeshToEffect.notifyObservers(ownerMesh);
  605. if (this._useMeshMaterial(renderingMesh)) {
  606. renderingMesh.render(subMesh, hardwareInstancedRendering, replacementMesh || undefined);
  607. }
  608. else if (this._isReady(subMesh, hardwareInstancedRendering, this._emissiveTextureAndColor.texture)) {
  609. engine.enableEffect(this._effectLayerMapGenerationEffect);
  610. renderingMesh._bind(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode);
  611. this._effectLayerMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
  612. this._effectLayerMapGenerationEffect.setMatrix("world", effectiveMesh.getWorldMatrix());
  613. this._effectLayerMapGenerationEffect.setFloat4("glowColor",
  614. this._emissiveTextureAndColor.color.r,
  615. this._emissiveTextureAndColor.color.g,
  616. this._emissiveTextureAndColor.color.b,
  617. this._emissiveTextureAndColor.color.a);
  618. const needAlphaTest = material.needAlphaTesting();
  619. const diffuseTexture = material.getAlphaTestTexture();
  620. const needAlphaBlendFromDiffuse = diffuseTexture && diffuseTexture.hasAlpha &&
  621. ((material as any).useAlphaFromDiffuseTexture || (material as any)._useAlphaFromAlbedoTexture);
  622. if (diffuseTexture && (needAlphaTest || needAlphaBlendFromDiffuse)) {
  623. this._effectLayerMapGenerationEffect.setTexture("diffuseSampler", diffuseTexture);
  624. const textureMatrix = diffuseTexture.getTextureMatrix();
  625. if (textureMatrix) {
  626. this._effectLayerMapGenerationEffect.setMatrix("diffuseMatrix", textureMatrix);
  627. }
  628. }
  629. const opacityTexture = (material as any).opacityTexture;
  630. if (opacityTexture) {
  631. this._effectLayerMapGenerationEffect.setTexture("opacitySampler", opacityTexture);
  632. this._effectLayerMapGenerationEffect.setFloat("opacityIntensity", opacityTexture.level);
  633. const textureMatrix = opacityTexture.getTextureMatrix();
  634. if (textureMatrix) {
  635. this._effectLayerMapGenerationEffect.setMatrix("opacityMatrix", textureMatrix);
  636. }
  637. }
  638. // Glow emissive only
  639. if (this._emissiveTextureAndColor.texture) {
  640. this._effectLayerMapGenerationEffect.setTexture("emissiveSampler", this._emissiveTextureAndColor.texture);
  641. this._effectLayerMapGenerationEffect.setMatrix("emissiveMatrix", this._emissiveTextureAndColor.texture.getTextureMatrix());
  642. }
  643. // Bones
  644. if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
  645. const skeleton = renderingMesh.skeleton;
  646. if (skeleton.isUsingTextureForMatrices) {
  647. const boneTexture = skeleton.getTransformMatrixTexture(renderingMesh);
  648. if (!boneTexture) {
  649. return;
  650. }
  651. this._effectLayerMapGenerationEffect.setTexture("boneSampler", boneTexture);
  652. this._effectLayerMapGenerationEffect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
  653. } else {
  654. this._effectLayerMapGenerationEffect.setMatrices("mBones", skeleton.getTransformMatrices((renderingMesh)));
  655. }
  656. }
  657. // Morph targets
  658. MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effectLayerMapGenerationEffect);
  659. // Alpha mode
  660. if (enableAlphaMode) {
  661. engine.setAlphaMode(material.alphaMode);
  662. }
  663. // Draw
  664. renderingMesh._processRendering(effectiveMesh, subMesh, this._effectLayerMapGenerationEffect, material.fillMode, batch, hardwareInstancedRendering,
  665. (isInstance, world) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
  666. } else {
  667. // Need to reset refresh rate of the main map
  668. this._mainTexture.resetRefreshCounter();
  669. }
  670. this.onAfterRenderMeshToEffect.notifyObservers(ownerMesh);
  671. }
  672. /**
  673. * Defines whether the current material of the mesh should be use to render the effect.
  674. * @param mesh defines the current mesh to render
  675. */
  676. protected _useMeshMaterial(mesh: AbstractMesh): boolean {
  677. return false;
  678. }
  679. /**
  680. * Rebuild the required buffers.
  681. * @hidden Internal use only.
  682. */
  683. public _rebuild(): void {
  684. let vb = this._vertexBuffers[VertexBuffer.PositionKind];
  685. if (vb) {
  686. vb._rebuild();
  687. }
  688. this._generateIndexBuffer();
  689. }
  690. /**
  691. * Dispose only the render target textures and post process.
  692. */
  693. private _disposeTextureAndPostProcesses(): void {
  694. this._mainTexture.dispose();
  695. for (var i = 0; i < this._postProcesses.length; i++) {
  696. if (this._postProcesses[i]) {
  697. this._postProcesses[i].dispose();
  698. }
  699. }
  700. this._postProcesses = [];
  701. for (var i = 0; i < this._textures.length; i++) {
  702. if (this._textures[i]) {
  703. this._textures[i].dispose();
  704. }
  705. }
  706. this._textures = [];
  707. }
  708. /**
  709. * Dispose the highlight layer and free resources.
  710. */
  711. public dispose(): void {
  712. var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
  713. if (vertexBuffer) {
  714. vertexBuffer.dispose();
  715. this._vertexBuffers[VertexBuffer.PositionKind] = null;
  716. }
  717. if (this._indexBuffer) {
  718. this._scene.getEngine()._releaseBuffer(this._indexBuffer);
  719. this._indexBuffer = null;
  720. }
  721. // Clean textures and post processes
  722. this._disposeTextureAndPostProcesses();
  723. // Remove from scene
  724. var index = this._scene.effectLayers.indexOf(this, 0);
  725. if (index > -1) {
  726. this._scene.effectLayers.splice(index, 1);
  727. }
  728. // Callback
  729. this.onDisposeObservable.notifyObservers(this);
  730. this.onDisposeObservable.clear();
  731. this.onBeforeRenderMainTextureObservable.clear();
  732. this.onBeforeComposeObservable.clear();
  733. this.onBeforeRenderMeshToEffect.clear();
  734. this.onAfterRenderMeshToEffect.clear();
  735. this.onAfterComposeObservable.clear();
  736. this.onSizeChangedObservable.clear();
  737. }
  738. /**
  739. * Gets the class name of the effect layer
  740. * @returns the string with the class name of the effect layer
  741. */
  742. public getClassName(): string {
  743. return "EffectLayer";
  744. }
  745. /**
  746. * Creates an effect layer from parsed effect layer data
  747. * @param parsedEffectLayer defines effect layer data
  748. * @param scene defines the current scene
  749. * @param rootUrl defines the root URL containing the effect layer information
  750. * @returns a parsed effect Layer
  751. */
  752. public static Parse(parsedEffectLayer: any, scene: Scene, rootUrl: string): EffectLayer {
  753. var effectLayerType = Tools.Instantiate(parsedEffectLayer.customType);
  754. return effectLayerType.Parse(parsedEffectLayer, scene, rootUrl);
  755. }
  756. }