babylon.boundingBoxRenderer.ts 11 KB

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