babylon.debugLayer.ts 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. module BABYLON {
  2. export class DebugLayer {
  3. private _scene: Scene;
  4. private _camera: Camera;
  5. private _transformationMatrix = Matrix.Identity();
  6. private _enabled: boolean = false;
  7. private _labelsEnabled: boolean = false;
  8. private _displayStatistics = true;
  9. private _displayTree = false;
  10. private _displayLogs = false;
  11. private _globalDiv: HTMLDivElement;
  12. private _statsDiv: HTMLDivElement;
  13. private _statsSubsetDiv: HTMLDivElement;
  14. private _optionsDiv: HTMLDivElement;
  15. private _optionsSubsetDiv: HTMLDivElement;
  16. private _logDiv: HTMLDivElement;
  17. private _logSubsetDiv: HTMLDivElement;
  18. private _treeDiv: HTMLDivElement;
  19. private _treeSubsetDiv: HTMLDivElement;
  20. private _drawingCanvas: HTMLCanvasElement;
  21. private _drawingContext: CanvasRenderingContext2D;
  22. private _rootElement: HTMLElement;
  23. public _syncPositions: () => void;
  24. private _syncData: () => void;
  25. private _syncUI: () => void;
  26. private _onCanvasClick: (evt: MouseEvent) => void;
  27. private _clickPosition: any;
  28. private _ratio: number;
  29. private _identityMatrix = Matrix.Identity();
  30. private _showUI: boolean;
  31. private _needToRefreshMeshesTree: boolean;
  32. public shouldDisplayLabel: (node: Node) => boolean;
  33. public shouldDisplayAxis: (mesh: Mesh) => boolean;
  34. public axisRatio = 0.02;
  35. public accentColor = "orange";
  36. public customStatsFunction: () => string;
  37. constructor(scene: Scene) {
  38. this._scene = scene;
  39. this._syncPositions = (): void => {
  40. var engine = this._scene.getEngine();
  41. var canvasRect = engine.getRenderingCanvasClientRect();
  42. if (this._showUI) {
  43. this._statsDiv.style.left = (canvasRect.width - 410) + "px";
  44. this._statsDiv.style.top = (canvasRect.height - 290) + "px";
  45. this._statsDiv.style.width = "400px";
  46. this._statsDiv.style.height = "auto";
  47. this._statsSubsetDiv.style.maxHeight = "240px";
  48. this._optionsDiv.style.left = "0px";
  49. this._optionsDiv.style.top = "10px";
  50. this._optionsDiv.style.width = "200px";
  51. this._optionsDiv.style.height = "auto";
  52. this._optionsSubsetDiv.style.maxHeight = (canvasRect.height - 225) + "px";
  53. this._logDiv.style.left = "0px";
  54. this._logDiv.style.top = (canvasRect.height - 170) + "px";
  55. this._logDiv.style.width = "600px";
  56. this._logDiv.style.height = "160px";
  57. this._treeDiv.style.left = (canvasRect.width - 310) + "px";
  58. this._treeDiv.style.top = "10px";
  59. this._treeDiv.style.width = "300px";
  60. this._treeDiv.style.height = "auto";
  61. this._treeSubsetDiv.style.maxHeight = (canvasRect.height - 340) + "px";
  62. }
  63. this._globalDiv.style.left = canvasRect.left + "px";
  64. this._globalDiv.style.top = canvasRect.top + "px";
  65. this._drawingCanvas.style.left = "0px";
  66. this._drawingCanvas.style.top = "0px";
  67. this._drawingCanvas.style.width = engine.getRenderWidth() + "px";
  68. this._drawingCanvas.style.height = engine.getRenderHeight() + "px";
  69. var devicePixelRatio = window.devicePixelRatio || 1;
  70. var context = <any>this._drawingContext;
  71. var backingStoreRatio = context.webkitBackingStorePixelRatio ||
  72. context.mozBackingStorePixelRatio ||
  73. context.msBackingStorePixelRatio ||
  74. context.oBackingStorePixelRatio ||
  75. context.backingStorePixelRatio || 1;
  76. this._ratio = devicePixelRatio / backingStoreRatio;
  77. this._drawingCanvas.width = engine.getRenderWidth() * this._ratio;
  78. this._drawingCanvas.height = engine.getRenderHeight() * this._ratio;
  79. }
  80. this._onCanvasClick = (evt: MouseEvent): void => {
  81. this._clickPosition = {
  82. x: evt.clientX * this._ratio,
  83. y: evt.clientY * this._ratio
  84. };
  85. }
  86. this._syncUI = (): void => {
  87. if (this._showUI) {
  88. if (this._displayStatistics) {
  89. this._displayStats();
  90. this._statsDiv.style.display = "";
  91. } else {
  92. this._statsDiv.style.display = "none";
  93. }
  94. if (this._displayLogs) {
  95. this._logDiv.style.display = "";
  96. } else {
  97. this._logDiv.style.display = "none";
  98. }
  99. if (this._displayTree) {
  100. this._treeDiv.style.display = "";
  101. if (this._needToRefreshMeshesTree) {
  102. this._needToRefreshMeshesTree = false;
  103. this._refreshMeshesTreeContent();
  104. }
  105. } else {
  106. this._treeDiv.style.display = "none";
  107. }
  108. }
  109. }
  110. this._syncData = (): void => {
  111. if (this._labelsEnabled || !this._showUI) {
  112. this._camera.getViewMatrix().multiplyToRef(this._camera.getProjectionMatrix(), this._transformationMatrix);
  113. this._drawingContext.clearRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);
  114. var engine = this._scene.getEngine();
  115. var viewport = this._camera.viewport;
  116. var globalViewport = viewport.toGlobal(engine);
  117. // Meshes
  118. var meshes = this._camera.getActiveMeshes();
  119. var index: number;
  120. var projectedPosition: Vector3;
  121. for (index = 0; index < meshes.length; index++) {
  122. var mesh = meshes.data[index];
  123. var position = mesh.getBoundingInfo().boundingSphere.center;
  124. projectedPosition = Vector3.Project(position, mesh.getWorldMatrix(), this._transformationMatrix, globalViewport);
  125. if (mesh.renderOverlay || this.shouldDisplayAxis && this.shouldDisplayAxis(mesh)) {
  126. this._renderAxis(projectedPosition, mesh, globalViewport);
  127. }
  128. if (!this.shouldDisplayLabel || this.shouldDisplayLabel(mesh)) {
  129. this._renderLabel(mesh.name, projectedPosition, 12,
  130. () => { mesh.renderOverlay = !mesh.renderOverlay },
  131. () => { return mesh.renderOverlay ? 'red' : 'black'; });
  132. }
  133. }
  134. // Cameras
  135. var cameras = this._scene.cameras;
  136. for (index = 0; index < cameras.length; index++) {
  137. var camera = cameras[index];
  138. if (camera === this._camera) {
  139. continue;
  140. }
  141. projectedPosition = Vector3.Project(Vector3.Zero(), camera.getWorldMatrix(), this._transformationMatrix, globalViewport);
  142. if (!this.shouldDisplayLabel || this.shouldDisplayLabel(camera)) {
  143. this._renderLabel(camera.name, projectedPosition, 12,
  144. () => {
  145. this._camera.detachControl(engine.getRenderingCanvas());
  146. this._camera = camera;
  147. this._camera.attachControl(engine.getRenderingCanvas());
  148. },
  149. () => { return "purple"; });
  150. }
  151. }
  152. // Lights
  153. var lights = this._scene.lights;
  154. for (index = 0; index < lights.length; index++) {
  155. var light = <any>lights[index];
  156. if (light.position) {
  157. projectedPosition = Vector3.Project(light.getAbsolutePosition(), this._identityMatrix, this._transformationMatrix, globalViewport);
  158. if (!this.shouldDisplayLabel || this.shouldDisplayLabel(light)) {
  159. this._renderLabel(light.name, projectedPosition, -20,
  160. () => {
  161. light.setEnabled(!light.isEnabled());
  162. },
  163. () => { return light.isEnabled() ? "orange" : "gray"; });
  164. }
  165. }
  166. }
  167. }
  168. this._clickPosition = undefined;
  169. }
  170. }
  171. private _refreshMeshesTreeContent(): void {
  172. while (this._treeSubsetDiv.hasChildNodes()) {
  173. this._treeSubsetDiv.removeChild(this._treeSubsetDiv.lastChild);
  174. }
  175. // Add meshes
  176. var sortedArray = this._scene.meshes.slice(0, this._scene.meshes.length);
  177. sortedArray.sort((a, b) => {
  178. if (a.name === b.name) {
  179. return 0;
  180. }
  181. return (a.name > b.name) ? 1 : -1;
  182. });
  183. for (var index = 0; index < sortedArray.length; index++) {
  184. var mesh = sortedArray[index];
  185. if (!mesh.isEnabled()) {
  186. continue;
  187. }
  188. this._generateAdvancedCheckBox(this._treeSubsetDiv, mesh.name, mesh.getTotalVertices() + " verts", mesh.isVisible, (element, m) => {
  189. m.isVisible = element.checked;
  190. }, mesh);
  191. }
  192. }
  193. private _renderSingleAxis(zero: Vector3, unit: Vector3, unitText: Vector3, label: string, color: string) {
  194. this._drawingContext.beginPath();
  195. this._drawingContext.moveTo(zero.x, zero.y);
  196. this._drawingContext.lineTo(unit.x, unit.y);
  197. this._drawingContext.strokeStyle = color;
  198. this._drawingContext.lineWidth = 4;
  199. this._drawingContext.stroke();
  200. this._drawingContext.font = "normal 14px Segoe UI";
  201. this._drawingContext.fillStyle = color;
  202. this._drawingContext.fillText(label, unitText.x, unitText.y);
  203. }
  204. private _renderAxis(projectedPosition: Vector3, mesh: Mesh, globalViewport: Viewport) {
  205. var position = mesh.getBoundingInfo().boundingSphere.center;
  206. var worldMatrix = mesh.getWorldMatrix();
  207. var unprojectedVector = Vector3.UnprojectFromTransform(projectedPosition.add(new Vector3(this._drawingCanvas.width * this.axisRatio, 0, 0)), globalViewport.width, globalViewport.height, worldMatrix, this._transformationMatrix);
  208. var unit = (unprojectedVector.subtract(position)).length();
  209. var xAxis = Vector3.Project(position.add(new Vector3(unit, 0, 0)), worldMatrix, this._transformationMatrix, globalViewport);
  210. var xAxisText = Vector3.Project(position.add(new Vector3(unit * 1.5, 0, 0)), worldMatrix, this._transformationMatrix, globalViewport);
  211. this._renderSingleAxis(projectedPosition, xAxis, xAxisText, "x", "#FF0000");
  212. var yAxis = Vector3.Project(position.add(new Vector3(0, unit, 0)), worldMatrix, this._transformationMatrix, globalViewport);
  213. var yAxisText = Vector3.Project(position.add(new Vector3(0, unit * 1.5, 0)), worldMatrix, this._transformationMatrix, globalViewport);
  214. this._renderSingleAxis(projectedPosition, yAxis, yAxisText, "y", "#00FF00");
  215. var zAxis = Vector3.Project(position.add(new Vector3(0, 0, unit)), worldMatrix, this._transformationMatrix, globalViewport);
  216. var zAxisText = Vector3.Project(position.add(new Vector3(0, 0, unit * 1.5)), worldMatrix, this._transformationMatrix, globalViewport);
  217. this._renderSingleAxis(projectedPosition, zAxis, zAxisText, "z", "#0000FF");
  218. }
  219. private _renderLabel(text: string, projectedPosition: Vector3, labelOffset: number, onClick: () => void, getFillStyle: () => string): void {
  220. if (projectedPosition.z > 0 && projectedPosition.z < 1.0) {
  221. this._drawingContext.font = "normal 12px Segoe UI";
  222. var textMetrics = this._drawingContext.measureText(text);
  223. var centerX = projectedPosition.x - textMetrics.width / 2;
  224. var centerY = projectedPosition.y;
  225. var clientRect = this._drawingCanvas.getBoundingClientRect();
  226. if (this._showUI && this._isClickInsideRect(clientRect.left * this._ratio + centerX - 5, clientRect.top * this._ratio + centerY - labelOffset - 12, textMetrics.width + 10, 17)) {
  227. onClick();
  228. }
  229. this._drawingContext.beginPath();
  230. this._drawingContext.rect(centerX - 5, centerY - labelOffset - 12, textMetrics.width + 10, 17);
  231. this._drawingContext.fillStyle = getFillStyle();
  232. this._drawingContext.globalAlpha = 0.5;
  233. this._drawingContext.fill();
  234. this._drawingContext.globalAlpha = 1.0;
  235. this._drawingContext.strokeStyle = '#FFFFFF';
  236. this._drawingContext.lineWidth = 1;
  237. this._drawingContext.stroke();
  238. this._drawingContext.fillStyle = "#FFFFFF";
  239. this._drawingContext.fillText(text, centerX, centerY - labelOffset);
  240. this._drawingContext.beginPath();
  241. this._drawingContext.arc(projectedPosition.x, centerY, 5, 0, 2 * Math.PI, false);
  242. this._drawingContext.fill();
  243. }
  244. }
  245. private _isClickInsideRect(x: number, y: number, width: number, height: number): boolean {
  246. if (!this._clickPosition) {
  247. return false;
  248. }
  249. if (this._clickPosition.x < x || this._clickPosition.x > x + width) {
  250. return false;
  251. }
  252. if (this._clickPosition.y < y || this._clickPosition.y > y + height) {
  253. return false;
  254. }
  255. return true;
  256. }
  257. public isVisible(): boolean {
  258. return this._enabled;
  259. }
  260. public hide() {
  261. if (!this._enabled) {
  262. return;
  263. }
  264. this._enabled = false;
  265. var engine = this._scene.getEngine();
  266. this._scene.unregisterBeforeRender(this._syncData);
  267. this._scene.unregisterAfterRender(this._syncUI);
  268. this._rootElement.removeChild(this._globalDiv);
  269. this._scene.forceShowBoundingBoxes = false;
  270. this._scene.forceWireframe = false;
  271. StandardMaterial.DiffuseTextureEnabled = true;
  272. StandardMaterial.AmbientTextureEnabled = true;
  273. StandardMaterial.SpecularTextureEnabled = true;
  274. StandardMaterial.EmissiveTextureEnabled = true;
  275. StandardMaterial.BumpTextureEnabled = true;
  276. StandardMaterial.OpacityTextureEnabled = true;
  277. StandardMaterial.ReflectionTextureEnabled = true;
  278. StandardMaterial.LightmapEnabled = true;
  279. this._scene.shadowsEnabled = true;
  280. this._scene.particlesEnabled = true;
  281. this._scene.postProcessesEnabled = true;
  282. this._scene.collisionsEnabled = true;
  283. this._scene.lightsEnabled = true;
  284. this._scene.texturesEnabled = true;
  285. this._scene.lensFlaresEnabled = true;
  286. this._scene.proceduralTexturesEnabled = true;
  287. this._scene.renderTargetsEnabled = true;
  288. this._scene.probesEnabled = true;
  289. engine.getRenderingCanvas().removeEventListener("click", this._onCanvasClick);
  290. }
  291. public show(showUI: boolean = true, camera: Camera = null, rootElement: HTMLElement = null) {
  292. if (this._enabled) {
  293. return;
  294. }
  295. this._enabled = true;
  296. if (camera) {
  297. this._camera = camera;
  298. } else {
  299. this._camera = this._scene.activeCamera;
  300. }
  301. this._showUI = showUI;
  302. var engine = this._scene.getEngine();
  303. this._globalDiv = document.createElement("div");
  304. this._rootElement = rootElement || document.body;
  305. this._rootElement.appendChild(this._globalDiv);
  306. this._generateDOMelements();
  307. engine.getRenderingCanvas().addEventListener("click", this._onCanvasClick);
  308. this._syncPositions();
  309. this._scene.registerBeforeRender(this._syncData);
  310. this._scene.registerAfterRender(this._syncUI);
  311. }
  312. private _clearLabels(): void {
  313. this._drawingContext.clearRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);
  314. for (var index = 0; index < this._scene.meshes.length; index++) {
  315. var mesh = this._scene.meshes[index];
  316. mesh.renderOverlay = false;
  317. }
  318. }
  319. private _generateheader(root: HTMLDivElement, text: string): void {
  320. var header = document.createElement("div");
  321. header.innerHTML = text + "&nbsp;";
  322. header.style.textAlign = "right";
  323. header.style.width = "100%";
  324. header.style.color = "white";
  325. header.style.backgroundColor = "Black";
  326. header.style.padding = "5px 5px 4px 0px";
  327. header.style.marginLeft = "-5px";
  328. header.style.fontWeight = "bold";
  329. root.appendChild(header);
  330. }
  331. private _generateTexBox(root: HTMLDivElement, title: string, color: string): void {
  332. var label = document.createElement("label");
  333. label.innerHTML = title;
  334. label.style.color = color;
  335. root.appendChild(label);
  336. root.appendChild(document.createElement("br"));
  337. }
  338. private _generateAdvancedCheckBox(root: HTMLDivElement, leftTitle: string, rightTitle: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
  339. var label = document.createElement("label");
  340. var boundingBoxesCheckbox = document.createElement("input");
  341. boundingBoxesCheckbox.type = "checkbox";
  342. boundingBoxesCheckbox.checked = initialState;
  343. boundingBoxesCheckbox.addEventListener("change", (evt: Event) => {
  344. task(evt.target, tag);
  345. });
  346. label.appendChild(boundingBoxesCheckbox);
  347. var container = document.createElement("span");
  348. var leftPart = document.createElement("span");
  349. var rightPart = document.createElement("span");
  350. rightPart.style.cssFloat = "right";
  351. leftPart.innerHTML = leftTitle;
  352. rightPart.innerHTML = rightTitle;
  353. rightPart.style.fontSize = "12px";
  354. rightPart.style.maxWidth = "200px";
  355. container.appendChild(leftPart);
  356. container.appendChild(rightPart);
  357. label.appendChild(container);
  358. root.appendChild(label);
  359. root.appendChild(document.createElement("br"));
  360. }
  361. private _generateCheckBox(root: HTMLDivElement, title: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
  362. var label = document.createElement("label");
  363. var checkBox = document.createElement("input");
  364. checkBox.type = "checkbox";
  365. checkBox.checked = initialState;
  366. checkBox.addEventListener("change", (evt: Event) => {
  367. task(evt.target, tag);
  368. });
  369. label.appendChild(checkBox);
  370. label.appendChild(document.createTextNode(title));
  371. root.appendChild(label);
  372. root.appendChild(document.createElement("br"));
  373. }
  374. private _generateButton(root: HTMLDivElement, title: string, task: (element, tag) => void, tag: any = null): void {
  375. var button = document.createElement("button");
  376. button.innerHTML = title;
  377. button.style.height = "24px";
  378. button.style.width = "150px";
  379. button.style.marginBottom = "5px";
  380. button.style.color = "#444444";
  381. button.style.border = "1px solid white";
  382. button.className = "debugLayerButton";
  383. button.addEventListener("click", (evt: Event) => {
  384. task(evt.target, tag);
  385. });
  386. root.appendChild(button);
  387. root.appendChild(document.createElement("br"));
  388. }
  389. private _generateRadio(root: HTMLDivElement, title: string, name: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
  390. var label = document.createElement("label");
  391. var boundingBoxesRadio = document.createElement("input");
  392. boundingBoxesRadio.type = "radio";
  393. boundingBoxesRadio.name = name;
  394. boundingBoxesRadio.checked = initialState;
  395. boundingBoxesRadio.addEventListener("change", (evt: Event) => {
  396. task(evt.target, tag);
  397. });
  398. label.appendChild(boundingBoxesRadio);
  399. label.appendChild(document.createTextNode(title));
  400. root.appendChild(label);
  401. root.appendChild(document.createElement("br"));
  402. }
  403. private _generateDOMelements(): void {
  404. this._globalDiv.id = "DebugLayer";
  405. this._globalDiv.style.position = "absolute";
  406. this._globalDiv.style.fontFamily = "Segoe UI, Arial";
  407. this._globalDiv.style.fontSize = "14px";
  408. this._globalDiv.style.color = "white";
  409. // Drawing canvas
  410. this._drawingCanvas = document.createElement("canvas");
  411. this._drawingCanvas.id = "DebugLayerDrawingCanvas";
  412. this._drawingCanvas.style.position = "absolute";
  413. this._drawingCanvas.style.pointerEvents = "none";
  414. this._drawingCanvas.style.backgroundColor = "transparent";
  415. this._drawingContext = this._drawingCanvas.getContext("2d");
  416. this._globalDiv.appendChild(this._drawingCanvas);
  417. if (this._showUI) {
  418. var background = "rgba(128, 128, 128, 0.4)";
  419. var border = "rgb(180, 180, 180) solid 1px";
  420. // Stats
  421. this._statsDiv = document.createElement("div");
  422. this._statsDiv.id = "DebugLayerStats";
  423. this._statsDiv.style.border = border;
  424. this._statsDiv.style.position = "absolute";
  425. this._statsDiv.style.background = background;
  426. this._statsDiv.style.padding = "0px 0px 0px 5px";
  427. this._generateheader(this._statsDiv, "STATISTICS");
  428. this._statsSubsetDiv = document.createElement("div");
  429. this._statsSubsetDiv.style.paddingTop = "5px";
  430. this._statsSubsetDiv.style.paddingBottom = "5px";
  431. this._statsSubsetDiv.style.overflowY = "auto";
  432. this._statsDiv.appendChild(this._statsSubsetDiv);
  433. // Tree
  434. this._treeDiv = document.createElement("div");
  435. this._treeDiv.id = "DebugLayerTree";
  436. this._treeDiv.style.border = border;
  437. this._treeDiv.style.position = "absolute";
  438. this._treeDiv.style.background = background;
  439. this._treeDiv.style.padding = "0px 0px 0px 5px";
  440. this._treeDiv.style.display = "none";
  441. this._generateheader(this._treeDiv, "MESHES TREE");
  442. this._treeSubsetDiv = document.createElement("div");
  443. this._treeSubsetDiv.style.paddingTop = "5px";
  444. this._treeSubsetDiv.style.paddingRight = "5px";
  445. this._treeSubsetDiv.style.overflowY = "auto";
  446. this._treeSubsetDiv.style.maxHeight = "300px";
  447. this._treeDiv.appendChild(this._treeSubsetDiv);
  448. this._needToRefreshMeshesTree = true;
  449. // Logs
  450. this._logDiv = document.createElement("div");
  451. this._logDiv.style.border = border;
  452. this._logDiv.id = "DebugLayerLogs";
  453. this._logDiv.style.position = "absolute";
  454. this._logDiv.style.background = background;
  455. this._logDiv.style.padding = "0px 0px 0px 5px";
  456. this._logDiv.style.display = "none";
  457. this._generateheader(this._logDiv, "LOGS");
  458. this._logSubsetDiv = document.createElement("div");
  459. this._logSubsetDiv.style.height = "127px";
  460. this._logSubsetDiv.style.paddingTop = "5px";
  461. this._logSubsetDiv.style.overflowY = "auto";
  462. this._logSubsetDiv.style.fontSize = "12px";
  463. this._logSubsetDiv.style.fontFamily = "consolas";
  464. this._logSubsetDiv.innerHTML = Tools.LogCache;
  465. this._logDiv.appendChild(this._logSubsetDiv);
  466. Tools.OnNewCacheEntry = (entry: string) => {
  467. this._logSubsetDiv.innerHTML = entry + this._logSubsetDiv.innerHTML;
  468. }
  469. // Options
  470. this._optionsDiv = document.createElement("div");
  471. this._optionsDiv.id = "DebugLayerOptions";
  472. this._optionsDiv.style.border = border;
  473. this._optionsDiv.style.position = "absolute";
  474. this._optionsDiv.style.background = background;
  475. this._optionsDiv.style.padding = "0px 0px 0px 5px";
  476. this._optionsDiv.style.overflowY = "auto";
  477. this._generateheader(this._optionsDiv, "OPTIONS");
  478. this._optionsSubsetDiv = document.createElement("div");
  479. this._optionsSubsetDiv.style.paddingTop = "5px";
  480. this._optionsSubsetDiv.style.paddingBottom = "5px";
  481. this._optionsSubsetDiv.style.overflowY = "auto";
  482. this._optionsSubsetDiv.style.maxHeight = "200px";
  483. this._optionsDiv.appendChild(this._optionsSubsetDiv);
  484. this._generateTexBox(this._optionsSubsetDiv, "<b>Windows:</b>", this.accentColor);
  485. this._generateCheckBox(this._optionsSubsetDiv, "Statistics", this._displayStatistics, (element) => { this._displayStatistics = element.checked });
  486. this._generateCheckBox(this._optionsSubsetDiv, "Logs", this._displayLogs, (element) => { this._displayLogs = element.checked });
  487. this._generateCheckBox(this._optionsSubsetDiv, "Meshes tree", this._displayTree, (element) => {
  488. this._displayTree = element.checked;
  489. this._needToRefreshMeshesTree = true;
  490. });
  491. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  492. this._generateTexBox(this._optionsSubsetDiv, "<b>General:</b>", this.accentColor);
  493. this._generateCheckBox(this._optionsSubsetDiv, "Bounding boxes", this._scene.forceShowBoundingBoxes, (element) => { this._scene.forceShowBoundingBoxes = element.checked });
  494. this._generateCheckBox(this._optionsSubsetDiv, "Clickable labels", this._labelsEnabled, (element) => {
  495. this._labelsEnabled = element.checked;
  496. if (!this._labelsEnabled) {
  497. this._clearLabels();
  498. }
  499. });
  500. this._generateCheckBox(this._optionsSubsetDiv, "Generate user marks (F12)", Tools.PerformanceLogLevel === Tools.PerformanceUserMarkLogLevel,
  501. (element) => {
  502. if (element.checked) {
  503. Tools.PerformanceLogLevel = Tools.PerformanceUserMarkLogLevel;
  504. } else {
  505. Tools.PerformanceLogLevel = Tools.PerformanceNoneLogLevel;
  506. }
  507. });
  508. ;
  509. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  510. this._generateTexBox(this._optionsSubsetDiv, "<b>Rendering mode:</b>", this.accentColor);
  511. this._generateRadio(this._optionsSubsetDiv, "Solid", "renderMode", !this._scene.forceWireframe && !this._scene.forcePointsCloud, (element) => {
  512. if (element.checked) {
  513. this._scene.forceWireframe = false;
  514. this._scene.forcePointsCloud = false;
  515. }
  516. });
  517. this._generateRadio(this._optionsSubsetDiv, "Wireframe", "renderMode", this._scene.forceWireframe, (element) => {
  518. if (element.checked) {
  519. this._scene.forceWireframe = true;
  520. this._scene.forcePointsCloud = false;
  521. }
  522. });
  523. this._generateRadio(this._optionsSubsetDiv, "Point", "renderMode", this._scene.forcePointsCloud, (element) => {
  524. if (element.checked) {
  525. this._scene.forceWireframe = false;
  526. this._scene.forcePointsCloud = true;
  527. }
  528. });
  529. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  530. this._generateTexBox(this._optionsSubsetDiv, "<b>Texture channels:</b>", this.accentColor);
  531. this._generateCheckBox(this._optionsSubsetDiv, "Diffuse", StandardMaterial.DiffuseTextureEnabled, (element) => { StandardMaterial.DiffuseTextureEnabled = element.checked });
  532. this._generateCheckBox(this._optionsSubsetDiv, "Ambient", StandardMaterial.AmbientTextureEnabled, (element) => { StandardMaterial.AmbientTextureEnabled = element.checked });
  533. this._generateCheckBox(this._optionsSubsetDiv, "Specular", StandardMaterial.SpecularTextureEnabled, (element) => { StandardMaterial.SpecularTextureEnabled = element.checked });
  534. this._generateCheckBox(this._optionsSubsetDiv, "Emissive", StandardMaterial.EmissiveTextureEnabled, (element) => { StandardMaterial.EmissiveTextureEnabled = element.checked });
  535. this._generateCheckBox(this._optionsSubsetDiv, "Bump", StandardMaterial.BumpTextureEnabled, (element) => { StandardMaterial.BumpTextureEnabled = element.checked });
  536. this._generateCheckBox(this._optionsSubsetDiv, "Opacity", StandardMaterial.OpacityTextureEnabled, (element) => { StandardMaterial.OpacityTextureEnabled = element.checked });
  537. this._generateCheckBox(this._optionsSubsetDiv, "Reflection", StandardMaterial.ReflectionTextureEnabled, (element) => { StandardMaterial.ReflectionTextureEnabled = element.checked });
  538. this._generateCheckBox(this._optionsSubsetDiv, "Fresnel", StandardMaterial.FresnelEnabled, (element) => { StandardMaterial.FresnelEnabled = element.checked });
  539. this._generateCheckBox(this._optionsSubsetDiv, "Lightmap", StandardMaterial.LightmapEnabled, (element) => { StandardMaterial.LightmapEnabled = element.checked });
  540. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  541. this._generateTexBox(this._optionsSubsetDiv, "<b>Options:</b>", this.accentColor);
  542. this._generateCheckBox(this._optionsSubsetDiv, "Animations", this._scene.animationsEnabled, (element) => { this._scene.animationsEnabled = element.checked });
  543. this._generateCheckBox(this._optionsSubsetDiv, "Collisions", this._scene.collisionsEnabled, (element) => { this._scene.collisionsEnabled = element.checked });
  544. this._generateCheckBox(this._optionsSubsetDiv, "Fog", this._scene.fogEnabled, (element) => { this._scene.fogEnabled = element.checked });
  545. this._generateCheckBox(this._optionsSubsetDiv, "Lens flares", this._scene.lensFlaresEnabled, (element) => { this._scene.lensFlaresEnabled = element.checked });
  546. this._generateCheckBox(this._optionsSubsetDiv, "Lights", this._scene.lightsEnabled, (element) => { this._scene.lightsEnabled = element.checked });
  547. this._generateCheckBox(this._optionsSubsetDiv, "Particles", this._scene.particlesEnabled, (element) => { this._scene.particlesEnabled = element.checked });
  548. this._generateCheckBox(this._optionsSubsetDiv, "Post-processes", this._scene.postProcessesEnabled, (element) => { this._scene.postProcessesEnabled = element.checked });
  549. this._generateCheckBox(this._optionsSubsetDiv, "Probes", this._scene.probesEnabled, (element) => { this._scene.probesEnabled = element.checked });
  550. this._generateCheckBox(this._optionsSubsetDiv, "Procedural textures", this._scene.proceduralTexturesEnabled, (element) => { this._scene.proceduralTexturesEnabled = element.checked });
  551. this._generateCheckBox(this._optionsSubsetDiv, "Render targets", this._scene.renderTargetsEnabled, (element) => { this._scene.renderTargetsEnabled = element.checked });
  552. this._generateCheckBox(this._optionsSubsetDiv, "Shadows", this._scene.shadowsEnabled, (element) => { this._scene.shadowsEnabled = element.checked });
  553. this._generateCheckBox(this._optionsSubsetDiv, "Skeletons", this._scene.skeletonsEnabled, (element) => { this._scene.skeletonsEnabled = element.checked });
  554. this._generateCheckBox(this._optionsSubsetDiv, "Sprites", this._scene.spritesEnabled, (element) => { this._scene.spritesEnabled = element.checked });
  555. this._generateCheckBox(this._optionsSubsetDiv, "Textures", this._scene.texturesEnabled, (element) => { this._scene.texturesEnabled = element.checked });
  556. if (AudioEngine && Engine.audioEngine.canUseWebAudio) {
  557. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  558. this._generateTexBox(this._optionsSubsetDiv, "<b>Audio:</b>", this.accentColor);
  559. this._generateRadio(this._optionsSubsetDiv, "Headphones", "panningModel", this._scene.headphone, (element) => {
  560. if (element.checked) {
  561. this._scene.headphone = true;
  562. }
  563. });
  564. this._generateRadio(this._optionsSubsetDiv, "Normal Speakers", "panningModel", !this._scene.headphone, (element) => {
  565. if (element.checked) {
  566. this._scene.headphone = false;
  567. }
  568. });
  569. this._generateCheckBox(this._optionsSubsetDiv, "Disable audio", !this._scene.audioEnabled, (element) => {
  570. this._scene.audioEnabled = !element.checked;
  571. });
  572. }
  573. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  574. this._generateTexBox(this._optionsSubsetDiv, "<b>Tools:</b>", this.accentColor);
  575. this._generateButton(this._optionsSubsetDiv, "Dump rendertargets", (element) => { this._scene.dumpNextRenderTargets = true; });
  576. this._generateButton(this._optionsSubsetDiv, "Run SceneOptimizer", (element) => { SceneOptimizer.OptimizeAsync(this._scene); });
  577. this._generateButton(this._optionsSubsetDiv, "Log camera object", (element) => {
  578. if (this._camera) {
  579. console.log(this._camera);
  580. } else {
  581. console.warn("No camera defined, or debug layer created before camera creation!");
  582. }
  583. });
  584. this._optionsSubsetDiv.appendChild(document.createElement("br"));
  585. this._globalDiv.appendChild(this._statsDiv);
  586. this._globalDiv.appendChild(this._logDiv);
  587. this._globalDiv.appendChild(this._optionsDiv);
  588. this._globalDiv.appendChild(this._treeDiv);
  589. }
  590. }
  591. private _displayStats() {
  592. var scene = this._scene;
  593. var engine = scene.getEngine();
  594. var glInfo = engine.getGlInfo();
  595. this._statsSubsetDiv.innerHTML = "Babylon.js v" + Engine.Version + " - <b>" + Tools.Format(engine.getFps(), 0) + " fps</b><br><br>"
  596. + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
  597. + "<b>Count</b><br>"
  598. + "Total meshes: " + scene.meshes.length + "<br>"
  599. + "Total vertices: " + scene.getTotalVertices() + "<br>"
  600. + "Total materials: " + scene.materials.length + "<br>"
  601. + "Total textures: " + scene.textures.length + "<br>"
  602. + "Active meshes: " + scene.getActiveMeshes().length + "<br>"
  603. + "Active indices: " + scene.getActiveIndices() + "<br>"
  604. + "Active bones: " + scene.getActiveBones() + "<br>"
  605. + "Active particles: " + scene.getActiveParticles() + "<br>"
  606. + "<b>Draw calls: " + engine.drawCalls + "</b><br><br><br>"
  607. + "<b>Duration</b><br>"
  608. + "Meshes selection:</i> " + Tools.Format(scene.getEvaluateActiveMeshesDuration()) + " ms<br>"
  609. + "Render Targets: " + Tools.Format(scene.getRenderTargetsDuration()) + " ms<br>"
  610. + "Particles: " + Tools.Format(scene.getParticlesDuration()) + " ms<br>"
  611. + "Sprites: " + Tools.Format(scene.getSpritesDuration()) + " ms<br><br>"
  612. + "Render: <b>" + Tools.Format(scene.getRenderDuration()) + " ms</b><br>"
  613. + "Frame: " + Tools.Format(scene.getLastFrameDuration()) + " ms<br>"
  614. + "Potential FPS: " + Tools.Format(1000.0 / scene.getLastFrameDuration(), 0) + "<br>"
  615. + "Resolution: " + engine.getRenderWidth() + "x" + engine.getRenderHeight() + "<br>"
  616. + "</div>"
  617. + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
  618. + "<b>Extensions</b><br>"
  619. + "Std derivatives: " + (engine.getCaps().standardDerivatives ? "Yes" : "No") + "<br>"
  620. + "Compressed textures: " + (engine.getCaps().s3tc ? "Yes" : "No") + "<br>"
  621. + "Hardware instances: " + (engine.getCaps().instancedArrays ? "Yes" : "No") + "<br>"
  622. + "Texture float: " + (engine.getCaps().textureFloat ? "Yes" : "No") + "<br><br>"
  623. + "32bits indices: " + (engine.getCaps().uintIndices ? "Yes" : "No") + "<br>"
  624. + "Fragment depth: " + (engine.getCaps().fragmentDepthSupported ? "Yes" : "No") + "<br>"
  625. + "High precision shaders: " + (engine.getCaps().highPrecisionShaderSupported ? "Yes" : "No") + "<br>"
  626. + "Draw buffers: " + (engine.getCaps().drawBuffersExtension ? "Yes" : "No") + "<br>"
  627. + "</div><br>"
  628. + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
  629. + "<b>Caps.</b><br>"
  630. + "Max textures units: " + engine.getCaps().maxTexturesImageUnits + "<br>"
  631. + "Max textures size: " + engine.getCaps().maxTextureSize + "<br>"
  632. + "Max anisotropy: " + engine.getCaps().maxAnisotropy + "<br>"
  633. + "<b>Info</b><br>"
  634. + "WebGL feature level: " + engine.webGLVersion + "<br>"
  635. + glInfo.version + "<br>"
  636. + "</div><br>"
  637. + glInfo.renderer + "<br>";
  638. if (this.customStatsFunction) {
  639. this._statsSubsetDiv.innerHTML += this._statsSubsetDiv.innerHTML;
  640. }
  641. }
  642. }
  643. }