boundingBoxRenderer.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. import { Scene } from "scene";
  2. import { VertexBuffer } from "Meshes/buffer";
  3. import { SubMesh } from "Meshes/subMesh";
  4. import { AbstractMesh } from "Meshes/abstractMesh";
  5. import { VertexData } from "Meshes/mesh.vertexData";
  6. import { Color3, Matrix } from "Maths/math";
  7. import { SmartArray } from "Misc/smartArray";
  8. import { Nullable, FloatArray } from "types";
  9. import { ISceneComponent, SceneComponentConstants } from "sceneComponent";
  10. import { BoundingBox } from "Culling/boundingBox";
  11. import { Effect } from "Materials/effect";
  12. import { Material } from "Materials/material";
  13. import { ShaderMaterial } from "Materials/shaderMaterial";
  14. import "Shaders/color.fragment";
  15. import "Shaders/color.vertex";
  16. declare module "scene" {
  17. export interface Scene {
  18. /** @hidden (Backing field) */
  19. _boundingBoxRenderer: BoundingBoxRenderer;
  20. /** @hidden (Backing field) */
  21. _forceShowBoundingBoxes: boolean;
  22. /**
  23. * Gets or sets a boolean indicating if all bounding boxes must be rendered
  24. */
  25. forceShowBoundingBoxes: boolean;
  26. /**
  27. * Gets the bounding box renderer associated with the scene
  28. * @returns a BoundingBoxRenderer
  29. */
  30. getBoundingBoxRenderer(): BoundingBoxRenderer;
  31. }
  32. }
  33. Object.defineProperty(Scene.prototype, "forceShowBoundingBoxes", {
  34. get: function(this: Scene) {
  35. return this._forceShowBoundingBoxes || false;
  36. },
  37. set: function(this: Scene, value: boolean) {
  38. this._forceShowBoundingBoxes = value;
  39. // Lazyly creates a BB renderer if needed.
  40. if (value) {
  41. this.getBoundingBoxRenderer();
  42. }
  43. },
  44. enumerable: true,
  45. configurable: true
  46. });
  47. Scene.prototype.getBoundingBoxRenderer = function(): BoundingBoxRenderer {
  48. if (!this._boundingBoxRenderer) {
  49. this._boundingBoxRenderer = new BoundingBoxRenderer(this);
  50. }
  51. return this._boundingBoxRenderer;
  52. };
  53. declare module "Meshes/abstractMesh" {
  54. export interface AbstractMesh {
  55. /** @hidden (Backing field) */
  56. _showBoundingBox: boolean;
  57. /**
  58. * Gets or sets a boolean indicating if the bounding box must be rendered as well (false by default)
  59. */
  60. showBoundingBox: boolean;
  61. }
  62. }
  63. Object.defineProperty(AbstractMesh.prototype, "showBoundingBox", {
  64. get: function(this: AbstractMesh) {
  65. return this._showBoundingBox || false;
  66. },
  67. set: function(this: AbstractMesh, value: boolean) {
  68. this._showBoundingBox = value;
  69. // Lazyly creates a BB renderer if needed.
  70. if (value) {
  71. this.getScene().getBoundingBoxRenderer();
  72. }
  73. },
  74. enumerable: true,
  75. configurable: true
  76. });
  77. /**
  78. * Component responsible of rendering the bounding box of the meshes in a scene.
  79. * This is usually used through the mesh.showBoundingBox or the scene.forceShowBoundingBoxes properties
  80. */
  81. export class BoundingBoxRenderer implements ISceneComponent {
  82. /**
  83. * The component name helpfull to identify the component in the list of scene components.
  84. */
  85. public readonly name = SceneComponentConstants.NAME_BOUNDINGBOXRENDERER;
  86. /**
  87. * The scene the component belongs to.
  88. */
  89. public scene: Scene;
  90. /**
  91. * Color of the bounding box lines placed in front of an object
  92. */
  93. public frontColor = new Color3(1, 1, 1);
  94. /**
  95. * Color of the bounding box lines placed behind an object
  96. */
  97. public backColor = new Color3(0.1, 0.1, 0.1);
  98. /**
  99. * Defines if the renderer should show the back lines or not
  100. */
  101. public showBackLines = true;
  102. /**
  103. * @hidden
  104. */
  105. public renderList = new SmartArray<BoundingBox>(32);
  106. private _colorShader: ShaderMaterial;
  107. private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
  108. private _indexBuffer: WebGLBuffer;
  109. /**
  110. * Instantiates a new bounding box renderer in a scene.
  111. * @param scene the scene the renderer renders in
  112. */
  113. constructor(scene: Scene) {
  114. this.scene = scene;
  115. scene._addComponent(this);
  116. }
  117. /**
  118. * Registers the component in a given scene
  119. */
  120. public register(): void {
  121. this.scene._beforeEvaluateActiveMeshStage.registerStep(SceneComponentConstants.STEP_BEFOREEVALUATEACTIVEMESH_BOUNDINGBOXRENDERER, this, this.reset);
  122. this.scene._activeMeshStage.registerStep(SceneComponentConstants.STEP_ACTIVEMESH_BOUNDINGBOXRENDERER, this, this._activeMesh);
  123. this.scene._evaluateSubMeshStage.registerStep(SceneComponentConstants.STEP_EVALUATESUBMESH_BOUNDINGBOXRENDERER, this, this._evaluateSubMesh);
  124. this.scene._afterRenderingGroupDrawStage.registerStep(SceneComponentConstants.STEP_AFTERRENDERINGGROUPDRAW_BOUNDINGBOXRENDERER, this, this.render);
  125. }
  126. private _evaluateSubMesh(mesh: AbstractMesh, subMesh: SubMesh): void {
  127. if (mesh.showSubMeshesBoundingBox) {
  128. const boundingInfo = subMesh.getBoundingInfo();
  129. if (boundingInfo !== null && boundingInfo !== undefined) {
  130. boundingInfo.boundingBox._tag = mesh.renderingGroupId;
  131. this.renderList.push(boundingInfo.boundingBox);
  132. }
  133. }
  134. }
  135. private _activeMesh(sourceMesh: AbstractMesh, mesh: AbstractMesh): void {
  136. if (sourceMesh.showBoundingBox || this.scene.forceShowBoundingBoxes) {
  137. let boundingInfo = sourceMesh.getBoundingInfo();
  138. boundingInfo.boundingBox._tag = mesh.renderingGroupId;
  139. this.renderList.push(boundingInfo.boundingBox);
  140. }
  141. }
  142. private _prepareRessources(): void {
  143. if (this._colorShader) {
  144. return;
  145. }
  146. this._colorShader = new ShaderMaterial("colorShader", this.scene, "color",
  147. {
  148. attributes: [VertexBuffer.PositionKind],
  149. uniforms: ["world", "viewProjection", "color"]
  150. });
  151. var engine = this.scene.getEngine();
  152. var boxdata = VertexData.CreateBox({ size: 1.0 });
  153. this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, <FloatArray>boxdata.positions, VertexBuffer.PositionKind, false);
  154. this._createIndexBuffer();
  155. }
  156. private _createIndexBuffer(): void {
  157. var engine = this.scene.getEngine();
  158. this._indexBuffer = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
  159. }
  160. /**
  161. * Rebuilds the elements related to this component in case of
  162. * context lost for instance.
  163. */
  164. public rebuild(): void {
  165. let vb = this._vertexBuffers[VertexBuffer.PositionKind];
  166. if (vb) {
  167. vb._rebuild();
  168. }
  169. this._createIndexBuffer();
  170. }
  171. /**
  172. * @hidden
  173. */
  174. public reset(): void {
  175. this.renderList.reset();
  176. }
  177. /**
  178. * Render the bounding boxes of a specific rendering group
  179. * @param renderingGroupId defines the rendering group to render
  180. */
  181. public render(renderingGroupId: number): void {
  182. if (this.renderList.length === 0) {
  183. return;
  184. }
  185. this._prepareRessources();
  186. if (!this._colorShader.isReady()) {
  187. return;
  188. }
  189. var engine = this.scene.getEngine();
  190. engine.setDepthWrite(false);
  191. this._colorShader._preBind();
  192. for (var boundingBoxIndex = 0; boundingBoxIndex < this.renderList.length; boundingBoxIndex++) {
  193. var boundingBox = this.renderList.data[boundingBoxIndex];
  194. if (boundingBox._tag !== renderingGroupId) {
  195. continue;
  196. }
  197. var min = boundingBox.minimum;
  198. var max = boundingBox.maximum;
  199. var diff = max.subtract(min);
  200. var median = min.add(diff.scale(0.5));
  201. var worldMatrix = Matrix.Scaling(diff.x, diff.y, diff.z)
  202. .multiply(Matrix.Translation(median.x, median.y, median.z))
  203. .multiply(boundingBox.getWorldMatrix());
  204. // VBOs
  205. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, <Effect>this._colorShader.getEffect());
  206. if (this.showBackLines) {
  207. // Back
  208. engine.setDepthFunctionToGreaterOrEqual();
  209. this.scene.resetCachedMaterial();
  210. this._colorShader.setColor4("color", this.backColor.toColor4());
  211. this._colorShader.bind(worldMatrix);
  212. // Draw order
  213. engine.drawElementsType(Material.LineListDrawMode, 0, 24);
  214. }
  215. // Front
  216. engine.setDepthFunctionToLess();
  217. this.scene.resetCachedMaterial();
  218. this._colorShader.setColor4("color", this.frontColor.toColor4());
  219. this._colorShader.bind(worldMatrix);
  220. // Draw order
  221. engine.drawElementsType(Material.LineListDrawMode, 0, 24);
  222. }
  223. this._colorShader.unbind();
  224. engine.setDepthFunctionToLessOrEqual();
  225. engine.setDepthWrite(true);
  226. }
  227. /**
  228. * In case of occlusion queries, we can render the occlusion bounding box through this method
  229. * @param mesh Define the mesh to render the occlusion bounding box for
  230. */
  231. public renderOcclusionBoundingBox(mesh: AbstractMesh): void {
  232. this._prepareRessources();
  233. if (!this._colorShader.isReady() || !mesh._boundingInfo) {
  234. return;
  235. }
  236. var engine = this.scene.getEngine();
  237. engine.setDepthWrite(false);
  238. engine.setColorWrite(false);
  239. this._colorShader._preBind();
  240. var boundingBox = mesh._boundingInfo.boundingBox;
  241. var min = boundingBox.minimum;
  242. var max = boundingBox.maximum;
  243. var diff = max.subtract(min);
  244. var median = min.add(diff.scale(0.5));
  245. var worldMatrix = Matrix.Scaling(diff.x, diff.y, diff.z)
  246. .multiply(Matrix.Translation(median.x, median.y, median.z))
  247. .multiply(boundingBox.getWorldMatrix());
  248. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, <Effect>this._colorShader.getEffect());
  249. engine.setDepthFunctionToLess();
  250. this.scene.resetCachedMaterial();
  251. this._colorShader.bind(worldMatrix);
  252. engine.drawElementsType(Material.LineListDrawMode, 0, 24);
  253. this._colorShader.unbind();
  254. engine.setDepthFunctionToLessOrEqual();
  255. engine.setDepthWrite(true);
  256. engine.setColorWrite(true);
  257. }
  258. /**
  259. * Dispose and release the resources attached to this renderer.
  260. */
  261. public dispose(): void {
  262. if (!this._colorShader) {
  263. return;
  264. }
  265. this.renderList.dispose();
  266. this._colorShader.dispose();
  267. var buffer = this._vertexBuffers[VertexBuffer.PositionKind];
  268. if (buffer) {
  269. buffer.dispose();
  270. this._vertexBuffers[VertexBuffer.PositionKind] = null;
  271. }
  272. this.scene.getEngine()._releaseBuffer(this._indexBuffer);
  273. }
  274. }