effectLayer.js 35 KB

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