renderingGroup.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import { SmartArray, SmartArrayNoDuplicate } from "../Misc/smartArray";
  2. import { SubMesh } from "../Meshes/subMesh";
  3. import { AbstractMesh } from "../Meshes/abstractMesh";
  4. import { Nullable, DeepImmutable } from "../types";
  5. import { Vector3 } from "../Maths/math.vector";
  6. import { IParticleSystem } from "../Particles/IParticleSystem";
  7. import { IEdgesRenderer } from "./edgesRenderer";
  8. import { ISpriteManager } from "../Sprites/spriteManager";
  9. import { Constants } from "../Engines/constants";
  10. import { Material } from "../Materials/material";
  11. import { Scene } from "../scene";
  12. import { Camera } from "../Cameras/camera";
  13. /**
  14. * This represents the object necessary to create a rendering group.
  15. * This is exclusively used and created by the rendering manager.
  16. * To modify the behavior, you use the available helpers in your scene or meshes.
  17. * @hidden
  18. */
  19. export class RenderingGroup {
  20. private static _zeroVector: DeepImmutable<Vector3> = Vector3.Zero();
  21. private _scene: Scene;
  22. private _opaqueSubMeshes = new SmartArray<SubMesh>(256);
  23. private _transparentSubMeshes = new SmartArray<SubMesh>(256);
  24. private _alphaTestSubMeshes = new SmartArray<SubMesh>(256);
  25. private _depthOnlySubMeshes = new SmartArray<SubMesh>(256);
  26. private _particleSystems = new SmartArray<IParticleSystem>(256);
  27. private _spriteManagers = new SmartArray<ISpriteManager>(256);
  28. private _opaqueSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
  29. private _alphaTestSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
  30. private _transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number;
  31. private _renderOpaque: (subMeshes: SmartArray<SubMesh>) => void;
  32. private _renderAlphaTest: (subMeshes: SmartArray<SubMesh>) => void;
  33. private _renderTransparent: (subMeshes: SmartArray<SubMesh>) => void;
  34. /** @hidden */
  35. public _edgesRenderers = new SmartArrayNoDuplicate<IEdgesRenderer>(16);
  36. public onBeforeTransparentRendering: () => void;
  37. /**
  38. * Set the opaque sort comparison function.
  39. * If null the sub meshes will be render in the order they were created
  40. */
  41. public set opaqueSortCompareFn(value: Nullable<(a: SubMesh, b: SubMesh) => number>) {
  42. this._opaqueSortCompareFn = value;
  43. if (value) {
  44. this._renderOpaque = this.renderOpaqueSorted;
  45. }
  46. else {
  47. this._renderOpaque = RenderingGroup.renderUnsorted;
  48. }
  49. }
  50. /**
  51. * Set the alpha test sort comparison function.
  52. * If null the sub meshes will be render in the order they were created
  53. */
  54. public set alphaTestSortCompareFn(value: Nullable<(a: SubMesh, b: SubMesh) => number>) {
  55. this._alphaTestSortCompareFn = value;
  56. if (value) {
  57. this._renderAlphaTest = this.renderAlphaTestSorted;
  58. }
  59. else {
  60. this._renderAlphaTest = RenderingGroup.renderUnsorted;
  61. }
  62. }
  63. /**
  64. * Set the transparent sort comparison function.
  65. * If null the sub meshes will be render in the order they were created
  66. */
  67. public set transparentSortCompareFn(value: Nullable<(a: SubMesh, b: SubMesh) => number>) {
  68. if (value) {
  69. this._transparentSortCompareFn = value;
  70. }
  71. else {
  72. this._transparentSortCompareFn = RenderingGroup.defaultTransparentSortCompare;
  73. }
  74. this._renderTransparent = this.renderTransparentSorted;
  75. }
  76. /**
  77. * Creates a new rendering group.
  78. * @param index The rendering group index
  79. * @param opaqueSortCompareFn The opaque sort comparison function. If null no order is applied
  80. * @param alphaTestSortCompareFn The alpha test sort comparison function. If null no order is applied
  81. * @param transparentSortCompareFn The transparent sort comparison function. If null back to front + alpha index sort is applied
  82. */
  83. constructor(public index: number, scene: Scene,
  84. opaqueSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number> = null,
  85. alphaTestSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number> = null,
  86. transparentSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number> = null) {
  87. this._scene = scene;
  88. this.opaqueSortCompareFn = opaqueSortCompareFn;
  89. this.alphaTestSortCompareFn = alphaTestSortCompareFn;
  90. this.transparentSortCompareFn = transparentSortCompareFn;
  91. }
  92. /**
  93. * Render all the sub meshes contained in the group.
  94. * @param customRenderFunction Used to override the default render behaviour of the group.
  95. * @returns true if rendered some submeshes.
  96. */
  97. public render(customRenderFunction: Nullable<(opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>) => void>, renderSprites: boolean, renderParticles: boolean, activeMeshes: Nullable<AbstractMesh[]>): void {
  98. if (customRenderFunction) {
  99. customRenderFunction(this._opaqueSubMeshes, this._alphaTestSubMeshes, this._transparentSubMeshes, this._depthOnlySubMeshes);
  100. return;
  101. }
  102. var engine = this._scene.getEngine();
  103. // Depth only
  104. if (this._depthOnlySubMeshes.length !== 0) {
  105. engine.setColorWrite(false);
  106. this._renderAlphaTest(this._depthOnlySubMeshes);
  107. engine.setColorWrite(true);
  108. }
  109. // Opaque
  110. if (this._opaqueSubMeshes.length !== 0) {
  111. this._renderOpaque(this._opaqueSubMeshes);
  112. }
  113. // Alpha test
  114. if (this._alphaTestSubMeshes.length !== 0) {
  115. this._renderAlphaTest(this._alphaTestSubMeshes);
  116. }
  117. var stencilState = engine.getStencilBuffer();
  118. engine.setStencilBuffer(false);
  119. // Sprites
  120. if (renderSprites) {
  121. this._renderSprites();
  122. }
  123. // Particles
  124. if (renderParticles) {
  125. this._renderParticles(activeMeshes);
  126. }
  127. if (this.onBeforeTransparentRendering) {
  128. this.onBeforeTransparentRendering();
  129. }
  130. // Transparent
  131. if (this._transparentSubMeshes.length !== 0) {
  132. this._renderTransparent(this._transparentSubMeshes);
  133. engine.setAlphaMode(Constants.ALPHA_DISABLE);
  134. }
  135. // Set back stencil to false in case it changes before the edge renderer.
  136. engine.setStencilBuffer(false);
  137. // Edges
  138. if (this._edgesRenderers.length) {
  139. for (var edgesRendererIndex = 0; edgesRendererIndex < this._edgesRenderers.length; edgesRendererIndex++) {
  140. this._edgesRenderers.data[edgesRendererIndex].render();
  141. }
  142. engine.setAlphaMode(Constants.ALPHA_DISABLE);
  143. }
  144. // Restore Stencil state.
  145. engine.setStencilBuffer(stencilState);
  146. }
  147. /**
  148. * Renders the opaque submeshes in the order from the opaqueSortCompareFn.
  149. * @param subMeshes The submeshes to render
  150. */
  151. private renderOpaqueSorted(subMeshes: SmartArray<SubMesh>): void {
  152. return RenderingGroup.renderSorted(subMeshes, this._opaqueSortCompareFn, this._scene.activeCamera, false);
  153. }
  154. /**
  155. * Renders the opaque submeshes in the order from the alphatestSortCompareFn.
  156. * @param subMeshes The submeshes to render
  157. */
  158. private renderAlphaTestSorted(subMeshes: SmartArray<SubMesh>): void {
  159. return RenderingGroup.renderSorted(subMeshes, this._alphaTestSortCompareFn, this._scene.activeCamera, false);
  160. }
  161. /**
  162. * Renders the opaque submeshes in the order from the transparentSortCompareFn.
  163. * @param subMeshes The submeshes to render
  164. */
  165. private renderTransparentSorted(subMeshes: SmartArray<SubMesh>): void {
  166. return RenderingGroup.renderSorted(subMeshes, this._transparentSortCompareFn, this._scene.activeCamera, true);
  167. }
  168. /**
  169. * Renders the submeshes in a specified order.
  170. * @param subMeshes The submeshes to sort before render
  171. * @param sortCompareFn The comparison function use to sort
  172. * @param cameraPosition The camera position use to preprocess the submeshes to help sorting
  173. * @param transparent Specifies to activate blending if true
  174. */
  175. private static renderSorted(subMeshes: SmartArray<SubMesh>, sortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>, camera: Nullable<Camera>, transparent: boolean): void {
  176. let subIndex = 0;
  177. let subMesh: SubMesh;
  178. let cameraPosition = camera ? camera.globalPosition : RenderingGroup._zeroVector;
  179. for (; subIndex < subMeshes.length; subIndex++) {
  180. subMesh = subMeshes.data[subIndex];
  181. subMesh._alphaIndex = subMesh.getMesh().alphaIndex;
  182. subMesh._distanceToCamera = Vector3.Distance(subMesh.getBoundingInfo().boundingSphere.centerWorld, cameraPosition);
  183. }
  184. let sortedArray = subMeshes.data.slice(0, subMeshes.length);
  185. if (sortCompareFn) {
  186. sortedArray.sort(sortCompareFn);
  187. }
  188. for (subIndex = 0; subIndex < sortedArray.length; subIndex++) {
  189. subMesh = sortedArray[subIndex];
  190. if (transparent) {
  191. let material = subMesh.getMaterial();
  192. if (material && material.needDepthPrePass) {
  193. let engine = material.getScene().getEngine();
  194. engine.setColorWrite(false);
  195. engine.setAlphaMode(Constants.ALPHA_DISABLE);
  196. subMesh.render(false);
  197. engine.setColorWrite(true);
  198. }
  199. }
  200. subMesh.render(transparent);
  201. }
  202. }
  203. /**
  204. * Renders the submeshes in the order they were dispatched (no sort applied).
  205. * @param subMeshes The submeshes to render
  206. */
  207. private static renderUnsorted(subMeshes: SmartArray<SubMesh>): void {
  208. for (var subIndex = 0; subIndex < subMeshes.length; subIndex++) {
  209. let submesh = subMeshes.data[subIndex];
  210. submesh.render(false);
  211. }
  212. }
  213. /**
  214. * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
  215. * are rendered back to front if in the same alpha index.
  216. *
  217. * @param a The first submesh
  218. * @param b The second submesh
  219. * @returns The result of the comparison
  220. */
  221. public static defaultTransparentSortCompare(a: SubMesh, b: SubMesh): number {
  222. // Alpha index first
  223. if (a._alphaIndex > b._alphaIndex) {
  224. return 1;
  225. }
  226. if (a._alphaIndex < b._alphaIndex) {
  227. return -1;
  228. }
  229. // Then distance to camera
  230. return RenderingGroup.backToFrontSortCompare(a, b);
  231. }
  232. /**
  233. * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
  234. * are rendered back to front.
  235. *
  236. * @param a The first submesh
  237. * @param b The second submesh
  238. * @returns The result of the comparison
  239. */
  240. public static backToFrontSortCompare(a: SubMesh, b: SubMesh): number {
  241. // Then distance to camera
  242. if (a._distanceToCamera < b._distanceToCamera) {
  243. return 1;
  244. }
  245. if (a._distanceToCamera > b._distanceToCamera) {
  246. return -1;
  247. }
  248. return 0;
  249. }
  250. /**
  251. * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
  252. * are rendered front to back (prevent overdraw).
  253. *
  254. * @param a The first submesh
  255. * @param b The second submesh
  256. * @returns The result of the comparison
  257. */
  258. public static frontToBackSortCompare(a: SubMesh, b: SubMesh): number {
  259. // Then distance to camera
  260. if (a._distanceToCamera < b._distanceToCamera) {
  261. return -1;
  262. }
  263. if (a._distanceToCamera > b._distanceToCamera) {
  264. return 1;
  265. }
  266. return 0;
  267. }
  268. /**
  269. * Resets the different lists of submeshes to prepare a new frame.
  270. */
  271. public prepare(): void {
  272. this._opaqueSubMeshes.reset();
  273. this._transparentSubMeshes.reset();
  274. this._alphaTestSubMeshes.reset();
  275. this._depthOnlySubMeshes.reset();
  276. this._particleSystems.reset();
  277. this._spriteManagers.reset();
  278. this._edgesRenderers.reset();
  279. }
  280. public dispose(): void {
  281. this._opaqueSubMeshes.dispose();
  282. this._transparentSubMeshes.dispose();
  283. this._alphaTestSubMeshes.dispose();
  284. this._depthOnlySubMeshes.dispose();
  285. this._particleSystems.dispose();
  286. this._spriteManagers.dispose();
  287. this._edgesRenderers.dispose();
  288. }
  289. /**
  290. * Inserts the submesh in its correct queue depending on its material.
  291. * @param subMesh The submesh to dispatch
  292. * @param [mesh] Optional reference to the submeshes's mesh. Provide if you have an exiting reference to improve performance.
  293. * @param [material] Optional reference to the submeshes's material. Provide if you have an exiting reference to improve performance.
  294. */
  295. public dispatch(subMesh: SubMesh, mesh?: AbstractMesh, material?: Nullable<Material>): void {
  296. // Get mesh and materials if not provided
  297. if (mesh === undefined) {
  298. mesh = subMesh.getMesh();
  299. }
  300. if (material === undefined) {
  301. material = subMesh.getMaterial();
  302. }
  303. if (material === null || material === undefined) {
  304. return;
  305. }
  306. if (material.needAlphaBlendingForMesh(mesh)) { // Transparent
  307. this._transparentSubMeshes.push(subMesh);
  308. } else if (material.needAlphaTesting()) { // Alpha test
  309. if (material.needDepthPrePass) {
  310. this._depthOnlySubMeshes.push(subMesh);
  311. }
  312. this._alphaTestSubMeshes.push(subMesh);
  313. } else {
  314. if (material.needDepthPrePass) {
  315. this._depthOnlySubMeshes.push(subMesh);
  316. }
  317. this._opaqueSubMeshes.push(subMesh); // Opaque
  318. }
  319. mesh._renderingGroup = this;
  320. if (mesh._edgesRenderer && mesh._edgesRenderer.isEnabled) {
  321. this._edgesRenderers.pushNoDuplicate(mesh._edgesRenderer);
  322. }
  323. }
  324. public dispatchSprites(spriteManager: ISpriteManager) {
  325. this._spriteManagers.push(spriteManager);
  326. }
  327. public dispatchParticles(particleSystem: IParticleSystem) {
  328. this._particleSystems.push(particleSystem);
  329. }
  330. private _renderParticles(activeMeshes: Nullable<AbstractMesh[]>): void {
  331. if (this._particleSystems.length === 0) {
  332. return;
  333. }
  334. // Particles
  335. var activeCamera = this._scene.activeCamera;
  336. this._scene.onBeforeParticlesRenderingObservable.notifyObservers(this._scene);
  337. for (var particleIndex = 0; particleIndex < this._particleSystems.length; particleIndex++) {
  338. var particleSystem = this._particleSystems.data[particleIndex];
  339. if ((activeCamera && activeCamera.layerMask & particleSystem.layerMask) === 0) {
  340. continue;
  341. }
  342. let emitter: any = particleSystem.emitter;
  343. if (!emitter.position || !activeMeshes || activeMeshes.indexOf(emitter) !== -1) {
  344. this._scene._activeParticles.addCount(particleSystem.render(), false);
  345. }
  346. }
  347. this._scene.onAfterParticlesRenderingObservable.notifyObservers(this._scene);
  348. }
  349. private _renderSprites(): void {
  350. if (!this._scene.spritesEnabled || this._spriteManagers.length === 0) {
  351. return;
  352. }
  353. // Sprites
  354. var activeCamera = this._scene.activeCamera;
  355. this._scene.onBeforeSpritesRenderingObservable.notifyObservers(this._scene);
  356. for (var id = 0; id < this._spriteManagers.length; id++) {
  357. var spriteManager = this._spriteManagers.data[id];
  358. if (((activeCamera && activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
  359. spriteManager.render();
  360. }
  361. }
  362. this._scene.onAfterSpritesRenderingObservable.notifyObservers(this._scene);
  363. }
  364. }