renderingGroup.ts 17 KB

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