babylon.renderingGroup.ts 17 KB

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