boundingBoxRenderer.ts 12 KB

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