renderingGroup.ts 17 KB

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