boundingBoxRenderer.ts 12 KB

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