boundingBoxRenderer.ts 11 KB

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