Source: extern/debug.js

  1. import b4w from "../util/b4w.js";
  2. import register from "../util/register.js";
  3. import m_assert_fact from "../util/assert.js";
  4. import m_batch_fact from "../intern/batch.js";
  5. import m_cfg_fact from "../intern/config.js";
  6. import m_context_fact from "../intern/context.js";
  7. import m_compat_fact from "../intern/compat.js";
  8. import m_ctl_fact from "../intern/controls.js";
  9. import m_cont_fact from "../intern/container.js";
  10. import m_data_fact from "../intern/data.js";
  11. import m_debug_fact from "../intern/debug.js";
  12. import m_debug_batch_fact from "../intern/debug/batch.js";
  13. import m_debug_check_fact from "../intern/debug/check.js";
  14. import m_debug_subscene_fact from "../intern/debug/subscene.js";
  15. import m_debug_telemetry_fact from "../intern/debug/telemetry.js";
  16. import m_debug_render_time_fact from "../intern/debug/render_time.js";
  17. import m_ext_fact from "../intern/extensions.js";
  18. import m_geom_fact from "../intern/geometry.js";
  19. import m_load_fact from "../intern/loader.js";
  20. import m_obj_fact from "../intern/objects.js";
  21. import m_obj_util_fact from "../intern/obj_util.js";
  22. import m_phy_fact from "../intern/physics.js";
  23. import m_print_fact from "../intern/print.js";
  24. import m_render_fact from "../intern/renderer.js";
  25. import m_scenes_fact from "../intern/scenes.js";
  26. import m_scgraph_fact from "../intern/scenegraph.js";
  27. import m_sfx_fact from "../intern/sfx.js";
  28. import m_shaders_fact from "../intern/shaders.js";
  29. import m_subs_fact from "../intern/subscene.js";
  30. import m_textures_fact from "../intern/textures.js";
  31. import m_time_fact from "../intern/time.js";
  32. import m_trans_fact from "../intern/transform.js";
  33. import * as m_tsr from "../intern/tsr.js";
  34. import * as m_util from "../intern/util.js";
  35. import * as m_vec3 from "../libs/gl_matrix/vec3.js";
  36. /**
  37. * Engine debugging API.
  38. * @module debug
  39. * @local DebugViewMode
  40. * @local StageloadCallback
  41. * @local LoadedCallback
  42. * @local CodeTestCallback
  43. * @local EqualsFunction
  44. * @local OKFunction
  45. */
  46. function Debug(ns, exports) {
  47. var m_assert = m_assert_fact(ns);
  48. var m_batch = m_batch_fact(ns);
  49. var m_cfg = m_cfg_fact(ns);
  50. var m_compat = m_compat_fact(ns);
  51. var m_ctl = m_ctl_fact(ns);
  52. var m_cont = m_cont_fact(ns);
  53. var m_context = m_context_fact(ns);
  54. var m_data = m_data_fact(ns);
  55. var m_debug = m_debug_fact(ns);
  56. var m_debug_batch = m_debug_batch_fact(ns);
  57. var m_debug_check = m_debug_check_fact(ns);
  58. var m_debug_subscene = m_debug_subscene_fact(ns);
  59. var m_debug_telemetry = m_debug_telemetry_fact(ns);
  60. var m_debug_render_time = m_debug_render_time_fact(ns);
  61. var m_ext = m_ext_fact(ns);
  62. var m_geom = m_geom_fact(ns);
  63. var m_load = m_load_fact(ns);
  64. var m_obj = m_obj_fact(ns);
  65. var m_obj_util = m_obj_util_fact(ns);
  66. var m_phy = m_phy_fact(ns);
  67. var m_print = m_print_fact(ns);
  68. var m_render = m_render_fact(ns);
  69. var m_scenes = m_scenes_fact(ns);
  70. var m_scgraph = m_scgraph_fact(ns);
  71. var m_sfx = m_sfx_fact(ns);
  72. var m_shaders = m_shaders_fact(ns);
  73. var m_subs = m_subs_fact(ns);
  74. var m_time = m_time_fact(ns);
  75. var m_textures = m_textures_fact(ns);
  76. var m_trans = m_trans_fact(ns);
  77. var _tsr_tmp = m_tsr.create();
  78. var _vec2_tmp = new Float32Array(2);
  79. var _vec3_tmp = m_vec3.create();
  80. var _vec3_tmp2 = m_vec3.create();
  81. var _normal_line = null;
  82. var cfg_def = m_cfg.defaults;
  83. var FAKE_LOAD_INTERVAL = 5000;
  84. var FAKE_LOAD_START_PERCENTAGE = 0;
  85. var FAKE_LOAD_END_PERCENTAGE = 100;
  86. var PERF_NUM_CALLS = 5;
  87. var EPS = 0.000001;
  88. var _called_funcs = [];
  89. var _last_warn_message = "";
  90. var _last_err_message = "";
  91. var _warn_got = false;
  92. var _err_got = false;
  93. var _test_result = true;
  94. var _pixel = new Uint8Array(4);
  95. /**
  96. * Debug view mode.
  97. * @typedef {number} DebugViewMode
  98. */
  99. /**
  100. * Data loaded callback.
  101. * @callback LoadedCallback
  102. */
  103. /**
  104. * Loading stage callback.
  105. * @callback StageloadCallback
  106. * @param {number} percentage Loading progress (0-100).
  107. */
  108. /**
  109. * Code test callback.
  110. * @callback CodeTestCallback
  111. * @param {EqualsFunction} equals Comparison function.
  112. * @param {OKFunction} ok Code test function.
  113. */
  114. /**
  115. * Return the comparison result of the given parameters.
  116. * @callback EqualsFunction
  117. * @param {*} result Real function result.
  118. * @param {*} exp_result Expected result.
  119. */
  120. /**
  121. * Check code crash.
  122. * @callback OKFunction
  123. * @param {*} result Real function result.
  124. */
  125. /**
  126. * Debug view mode: turn off debug view.
  127. * @const {DebugViewMode} module:debug.DV_NONE
  128. */
  129. exports.DV_NONE = m_debug_subscene.DV_NONE;
  130. /**
  131. * Debug view mode: turn on the black-and-white wireframe view.
  132. * @const {DebugViewMode} module:debug.DV_OPAQUE_WIREFRAME
  133. */
  134. exports.DV_OPAQUE_WIREFRAME = m_debug_subscene.DV_OPAQUE_WIREFRAME;
  135. /**
  136. * Debug view mode: turn on the transparent (superimposed on the source color) wireframe view.
  137. * @const {DebugViewMode} module:debug.DV_TRANSPARENT_WIREFRAME
  138. */
  139. exports.DV_TRANSPARENT_WIREFRAME = m_debug_subscene.DV_TRANSPARENT_WIREFRAME;
  140. /**
  141. * Debug view mode: turn on the wireframe view with the front/back faces coloration.
  142. * @const {DebugViewMode} module:debug.DV_FRONT_BACK_VIEW
  143. */
  144. exports.DV_FRONT_BACK_VIEW = m_debug_subscene.DV_FRONT_BACK_VIEW;
  145. /**
  146. * Debug view mode: turn on the debug spheres view.
  147. * @const {DebugViewMode} module:debug.DV_BOUNDINGS
  148. */
  149. exports.DV_BOUNDINGS = m_debug_subscene.DV_BOUNDINGS;
  150. /**
  151. * Debug view mode: turn on the clusters view.
  152. * @const {DebugViewMode} module:debug.DV_CLUSTERS_VIEW
  153. */
  154. exports.DV_CLUSTERS_VIEW = m_debug_subscene.DV_CLUSTERS_VIEW;
  155. /**
  156. * Debug view mode: turn on the batches view.
  157. * @const {DebugViewMode} module:debug.DV_BATCHES_VIEW
  158. */
  159. exports.DV_BATCHES_VIEW = m_debug_subscene.DV_BATCHES_VIEW;
  160. /**
  161. * Debug view mode: turn on the render time view.
  162. * @const {DebugViewMode} module:debug.DV_RENDER_TIME
  163. */
  164. exports.DV_RENDER_TIME = m_debug_subscene.DV_RENDER_TIME;
  165. /**
  166. * Print info about the physics worker.
  167. * @method module:debug.physics_stats
  168. */
  169. exports.physics_stats = function() {
  170. m_phy.debug_workers();
  171. }
  172. /**
  173. * Print object info by physics ID.
  174. * @method module:debug.physics_id
  175. * @param {number} id Physics ID
  176. */
  177. exports.physics_id = function(id) {
  178. m_print.log("O", m_phy.find_obj_by_body_id(id))
  179. var act_phy_scene = m_phy.get_active_scene();
  180. if (!act_phy_scene) {
  181. m_print.error("No active physics scene.");
  182. return;
  183. }
  184. var bundles = act_phy_scene._physics.bundles;
  185. for (var i = 0; i < bundles.length; i++) {
  186. var bundle = bundles[i];
  187. var phy = bundle.physics;
  188. if (phy.body_id == id)
  189. m_print.log("B", bundle);
  190. }
  191. }
  192. /**
  193. * Print names and info for objects inside the view frustum.
  194. * @method module:debug.visible_objects
  195. */
  196. exports.visible_objects = function() {
  197. var scene = m_scenes.get_active();
  198. var objs = m_obj.get_scene_objs(scene, "MESH", m_obj.DATA_ID_ALL);
  199. var main_subscenes = m_scenes.subs_array(scene, [m_subs.MAIN_OPAQUE,
  200. m_subs.MAIN_BLEND,
  201. m_subs.MAIN_GLOW]);
  202. for (var i = 0; i < main_subscenes.length; i++) {
  203. var subs_main = main_subscenes[i];
  204. var draw_data = subs_main.draw_data;
  205. if (!draw_data.length)
  206. continue;
  207. print_objs(subs_main, draw_data, objs, "DYNAMIC");
  208. print_objs(subs_main, draw_data, objs, "STATIC");
  209. }
  210. }
  211. function print_objs(subs, draw_data, objs, type) {
  212. m_print.group(m_subs.subs_label(subs), type);
  213. for (var j = 0; j < objs.length; j++) {
  214. var obj = objs[j];
  215. var render = obj.render;
  216. if (render.type != type)
  217. continue;
  218. var is_visible = false;
  219. for (var k = 0; k < draw_data.length; k++) {
  220. var bundles = draw_data[k].bundles;
  221. for (var m = 0; m < bundles.length; m++) {
  222. var bundle = bundles[m];
  223. if (bundle.do_render && bundle.obj_render == render) {
  224. if (type == "STATIC")
  225. m_print.log_raw(obj.origin_name, obj);
  226. else
  227. m_print.log_raw(obj.name, obj);
  228. is_visible = true;
  229. break;
  230. }
  231. }
  232. if (is_visible)
  233. break;
  234. }
  235. }
  236. m_print.groupEnd();
  237. }
  238. /**
  239. * Print debug info for the object with the given name
  240. * @method module:debug.object_info
  241. * @param {string} name Object name
  242. */
  243. exports.object_info = function(name) {
  244. var scene = m_scenes.get_active();
  245. var objs = m_obj.get_scene_objs(scene, "MESH", m_obj.DATA_ID_ALL);
  246. for (var i = 0; i < objs.length; i++) {
  247. var obj = objs[i];
  248. if (obj.name != name)
  249. continue;
  250. m_print.log("Object", obj);
  251. var subscenes = m_scenes.get_all_subscenes(scene);
  252. for (var j = 0; j < subscenes.length; j++) {
  253. var subs = subscenes[j];
  254. var print_bundles = [];
  255. var draw_data = subs.draw_data;
  256. for (var k = 0; k < draw_data.length; k++) {
  257. var bundles = draw_data[k].bundles;
  258. for (var m = 0; m < bundles.length; m++) {
  259. if (bundles[m].obj_render == obj.render)
  260. print_bundles.push(bundles[m]);
  261. }
  262. }
  263. m_print.log("Subscene " + subs.type, print_bundles);
  264. }
  265. }
  266. }
  267. /**
  268. * Print debug info for the object with the given name
  269. * @method module:debug.objects_stat
  270. */
  271. exports.objects_stat = function() {
  272. var scene = m_scenes.get_active();
  273. m_print.log("Armatures: " + m_obj.get_scene_objs(scene, "ARMATURE",
  274. m_obj.DATA_ID_ALL).length);
  275. m_print.log("Cameras: " + m_obj.get_scene_objs(scene, "CAMERA",
  276. m_obj.DATA_ID_ALL).length);
  277. m_print.log("Curves: " + m_obj.get_scene_objs(scene, "CURVE",
  278. m_obj.DATA_ID_ALL).length);
  279. m_print.log("Empties: " + m_obj.get_scene_objs(scene, "EMPTY",
  280. m_obj.DATA_ID_ALL).length);
  281. m_print.log("Lamps: " + m_obj.get_scene_objs(scene, "LAMP",
  282. m_obj.DATA_ID_ALL).length);
  283. m_print.log("Meshes: " + m_obj.get_scene_objs(scene, "MESH",
  284. m_obj.DATA_ID_ALL).length);
  285. m_print.log("Speakers: " + m_obj.get_scene_objs(scene, "SPEAKER",
  286. m_obj.DATA_ID_ALL).length);
  287. }
  288. /**
  289. * Return the number of vertices in the active scene.
  290. * @method module:debug.num_vertices
  291. * @returns {number} The number of vertices.
  292. */
  293. exports.num_vertices = function() {
  294. var num = 0;
  295. var scene = m_scenes.get_active();
  296. var main_subscenes = m_scenes.subs_array(scene, [m_subs.MAIN_OPAQUE,
  297. m_subs.MAIN_BLEND,
  298. m_subs.MAIN_GLOW]);
  299. for (var i = 0; i < main_subscenes.length; i++) {
  300. var subs = main_subscenes[i];
  301. var draw_data = subs.draw_data;
  302. for (var j = 0; j < draw_data.length; j++) {
  303. var bundles = draw_data[j].bundles;
  304. for (var k = 0; k < bundles.length; k++) {
  305. var batch = bundles[k].batch;
  306. // NOTE: some objects (particles) do not have any submesh
  307. if (batch)
  308. num += batch.num_vertices;
  309. }
  310. }
  311. }
  312. return num;
  313. }
  314. /**
  315. * Return the number of all triangles in the active scene.
  316. * @method module:debug.num_triangles
  317. * @returns {number} The number of all triangles.
  318. */
  319. exports.num_triangles = function() {
  320. var num = 0;
  321. var scene = m_scenes.get_active();
  322. var main_subscenes = m_scenes.subs_array(scene, [m_subs.MAIN_OPAQUE,
  323. m_subs.MAIN_BLEND,
  324. m_subs.MAIN_GLOW]);
  325. for (var i = 0; i < main_subscenes.length; i++) {
  326. var subs = main_subscenes[i];
  327. var draw_data = subs.draw_data;
  328. for (var j = 0; j < draw_data.length; j++) {
  329. var bundles = draw_data[j].bundles;
  330. for (var k = 0; k < bundles.length; k++) {
  331. var batch = bundles[k].batch;
  332. // NOTE: some objects (particles) do not have any submesh
  333. if (batch)
  334. num += batch.num_triangles;
  335. }
  336. }
  337. }
  338. return num;
  339. }
  340. /**
  341. * Return the number of batches in the main scenes.
  342. * @method module:debug.num_draw_calls
  343. * @returns {number} The number of batches.
  344. */
  345. exports.num_draw_calls = function() {
  346. var scene = m_scenes.get_active();
  347. var main_subscenes = m_scenes.subs_array(scene, [m_subs.MAIN_OPAQUE,
  348. m_subs.MAIN_BLEND,
  349. m_subs.MAIN_GLOW,
  350. m_subs.MAIN_PLANE_REFLECT,
  351. m_subs.MAIN_PLANE_REFLECT_BLEND]);
  352. var number = 0;
  353. for (var i = 0; i < main_subscenes.length; i++) {
  354. var subs = main_subscenes[i];
  355. var draw_data = subs.draw_data;
  356. for (var j = 0; j < draw_data.length; j++)
  357. number += draw_data[j].bundles.length;
  358. }
  359. var cube_reflect_subs = m_scenes.subs_array(scene,
  360. [m_subs.MAIN_CUBE_REFLECT, m_subs.MAIN_CUBE_REFLECT_BLEND]);
  361. for (var i = 0; i < cube_reflect_subs.length; i++) {
  362. var subs = cube_reflect_subs[i];
  363. var draw_data = subs.draw_data;
  364. for (var j = 0; j < draw_data.length; j++)
  365. number += 6 * draw_data[j].bundles.length;
  366. }
  367. return number;
  368. }
  369. /**
  370. * Return the number of compiled shaders.
  371. * @method module:debug.num_shaders
  372. * @returns {number} The number of compiled shaders.
  373. */
  374. exports.num_shaders = function() {
  375. var compiled_shaders = m_shaders.get_compiled_shaders();
  376. return m_util.get_dict_length(compiled_shaders);
  377. }
  378. /**
  379. * Return geometry info in the main scenes.
  380. * @method module:debug.geometry_stats
  381. * @returns {Object} Geometry info.
  382. */
  383. exports.geometry_stats = function() {
  384. var scene = m_scenes.get_active();
  385. var subscenes = m_scenes.get_all_subscenes(scene);
  386. var unique_batches = {};
  387. for (var i = 0; i < subscenes.length; i++) {
  388. var subs = subscenes[i];
  389. if (subs.type == m_subs.SINK || subs.type == m_subs.DEBUG_VIEW)
  390. continue;
  391. var draw_data = subs.draw_data;
  392. for (var j = 0; j < draw_data.length; j++) {
  393. var bundles = draw_data[j].bundles;
  394. for (var k = 0; k < bundles.length; k++) {
  395. var batch = bundles[k].batch;
  396. var render = bundles[k].obj_render;
  397. // NOTE: some objects (particles) do not have any submesh
  398. if (batch)
  399. if (subs.type != m_subs.COLOR_PICKING && subs.type != m_subs.OUTLINE_MASK
  400. || render.origin_selectable || render.origin_outlining)
  401. unique_batches[batch.id] = batch;
  402. }
  403. }
  404. }
  405. var vbo_memory = 0;
  406. var ibo_memory = 0;
  407. for (var id in unique_batches) {
  408. var bufs_data = unique_batches[id].bufs_data;
  409. if (bufs_data.debug_ibo_bytes)
  410. ibo_memory += bufs_data.debug_ibo_bytes / (1024 * 1024);
  411. vbo_memory += bufs_data.debug_vbo_bytes / (1024 * 1024);
  412. }
  413. return {"vbo_memory": vbo_memory, "ibo_memory": ibo_memory};
  414. }
  415. /**
  416. * Return the number of unique textures in the main scenes.
  417. * @method module:debug.num_textures
  418. * @returns {Object} Textures info.
  419. */
  420. exports.num_textures = function() {
  421. var tex_list = [];
  422. var memory = 0;
  423. var scene = m_scenes.get_active();
  424. var main_subscenes = m_scenes.subs_array(scene, [m_subs.MAIN_OPAQUE,
  425. m_subs.MAIN_BLEND,
  426. m_subs.MAIN_GLOW]);
  427. for (var i = 0; i < main_subscenes.length; i++) {
  428. var subs = main_subscenes[i];
  429. var draw_data = subs.draw_data;
  430. for (var j = 0; j < draw_data.length; j++) {
  431. var bundles = draw_data[j].bundles;
  432. for (var k = 0; k < bundles.length; k++) {
  433. var batch = bundles[k].batch;
  434. // NOTE: some objects (particles) do not have any submesh
  435. if (batch) {
  436. var batch_texs = batch.textures;
  437. for (var m = 0; m < batch_texs.length; m++) {
  438. var batch_tex = batch_texs[m];
  439. if (batch_tex.source === "IMAGE" ||
  440. batch_tex.source === "ENVIRONMENT_MAP") {
  441. var tex = batch_tex.w_texture;
  442. if (tex_list.indexOf(tex) === -1) {
  443. tex_list.push(tex);
  444. var mem = batch_tex.width * batch_tex.height *
  445. 4 / (1024 * 1024) / batch_tex.compress_ratio;
  446. // mipmaps
  447. mem *= 1.3333;
  448. memory += mem;
  449. }
  450. }
  451. }
  452. }
  453. }
  454. }
  455. }
  456. return {"number": tex_list.length, "memory": memory};
  457. }
  458. /**
  459. * Return the number and the total size of unique output framebuffers.
  460. * @method module:debug.num_render_targets
  461. * @returns {Object} Render targets info.
  462. */
  463. exports.num_render_targets = function() {
  464. var list = [];
  465. var memory = 0;
  466. var scene = m_scenes.get_active();
  467. var subscenes = m_scenes.get_all_subscenes(scene);
  468. for (var i = 0; i < subscenes.length; i++) {
  469. var subs = subscenes[i];
  470. if (subs.type == m_subs.SINK)
  471. continue;
  472. var cam = subs.camera;
  473. var subs_textures = [cam.color_attachment, cam.depth_attachment];
  474. subs_textures.push.apply(subs_textures, subs.textures_internal);
  475. for (var j = 0; j < subs_textures.length; j++) {
  476. var tex = subs_textures[j];
  477. if (m_textures.is_texture(tex) && list.indexOf(tex) == -1) {
  478. list.push(tex);
  479. memory += cam.width * cam.height * m_textures.get_texture_texel_size(tex);
  480. }
  481. }
  482. }
  483. return {"number": list.length, "memory": (memory / 1024 / 1024)};
  484. }
  485. /**
  486. * Draw a frustum for the active camera.
  487. * @method module:debug.make_camera_frustum_shot
  488. */
  489. exports.make_camera_frustum_shot = function() {
  490. var active_scene = m_scenes.get_active();
  491. var subs_main = m_scenes.get_subs(active_scene, m_subs.MAIN_OPAQUE);
  492. if (!subs_main)
  493. return;
  494. m_scenes.make_frustum_shot(subs_main.camera, subs_main, [1,1,0]);
  495. }
  496. /**
  497. * Draw a light frustum, used for rendering the shadow maps.
  498. * @method module:debug.make_light_frustum_shot
  499. */
  500. exports.make_light_frustum_shot = function() {
  501. var active_scene = m_scenes.get_active();
  502. var subs_main = m_scenes.get_subs(active_scene, m_subs.MAIN_OPAQUE);
  503. var subscenes_shadow = m_scenes.subs_array(active_scene, [m_subs.SHADOW_CAST]);
  504. if (!subs_main)
  505. return;
  506. for (var i = 0; i < subscenes_shadow.length; i++) {
  507. var subs_shadow = subscenes_shadow[i];
  508. var color;
  509. switch (i) {
  510. case 0:
  511. color = [1, 0, 0];
  512. break;
  513. case 1:
  514. color = [0, 1, 0];
  515. break;
  516. case 2:
  517. color = [0, 0, 1];
  518. break;
  519. default:
  520. color = [1, 0, 1];
  521. }
  522. m_scenes.make_frustum_shot(subs_shadow.camera, subs_main, color);
  523. }
  524. }
  525. /**
  526. * Print info about the active scene graph in DOT format.
  527. * @method module:debug.scenegraph_to_dot
  528. */
  529. exports.scenegraph_to_dot = function() {
  530. var scenes = m_scenes.get_all_scenes();
  531. for (var i = 0; i < scenes.length; i++) {
  532. var scene = scenes[i];
  533. var graph = m_scenes.get_graph(scene);
  534. m_print.log("\n" + m_scgraph.debug_convert_to_dot(graph));
  535. }
  536. }
  537. exports.loading_graph_to_dot = function(data_id) {
  538. data_id = data_id | 0;
  539. m_print.log("\n" + m_load.graph_to_dot(data_id));
  540. }
  541. /**
  542. * Print info about the controls module.
  543. * @method module:debug.controls_info
  544. */
  545. exports.controls_info = m_ctl.debug;
  546. /**
  547. * Get the distance between two objects.
  548. * @method module:debug.object_distance
  549. * @param {Object3D} obj The first object.
  550. * @param {Object3D} obj2 The second object.
  551. * @returns {number} Distance.
  552. * @deprecated use {@link module:transform.distance|transform.distance} instead.
  553. */
  554. exports.object_distance = function(obj, obj2) {
  555. var trans = m_tsr.get_trans_view(obj.render.world_tsr);
  556. var trans2 = m_tsr.get_trans_view(obj2.render.world_tsr);
  557. var dist = m_vec3.dist(trans, trans2);
  558. return dist;
  559. }
  560. /**
  561. * Store a simple telemetry message.
  562. * @method module:debug.msg
  563. */
  564. exports.msg = function() {
  565. m_debug_telemetry.msg.apply(m_debug_telemetry, arguments);
  566. }
  567. /**
  568. * Store a flashback telemetry message.
  569. * @method module:debug.fbmsg
  570. */
  571. exports.fbmsg = function() {
  572. m_debug_telemetry.fbmsg.apply(m_debug_telemetry, arguments);
  573. }
  574. /**
  575. * Print the list of flashback messages.
  576. * @method module:debug.print_telemetry
  577. */
  578. exports.print_telemetry = function() {
  579. m_debug_telemetry.print_telemetry.apply(m_debug_telemetry, arguments);
  580. }
  581. /**
  582. * Plot the list of flashback messages as a gnuplot datafile.
  583. * @method module:debug.plot_telemetry
  584. */
  585. exports.plot_telemetry = function() {
  586. m_debug_telemetry.plot_telemetry.apply(m_debug_telemetry, arguments);
  587. }
  588. /**
  589. * Store the callback function result as a flashback message.
  590. * @method module:debug.fbres
  591. * @param {Function} fun fun
  592. * @param {number} timeout timeout
  593. */
  594. exports.fbres = function(fun, timeout) {
  595. if (!timeout)
  596. timeout = 16;
  597. var cb = function() {
  598. m_debug_telemetry.fbmsg("FBRES", fun());
  599. setTimeout(cb, timeout);
  600. }
  601. cb();
  602. }
  603. /**
  604. * Check the engine constants, abort if not constant.
  605. * @method module:debug.assert_constants
  606. */
  607. exports.assert_constants = function() {
  608. var VEC3_IDENT = new Float32Array(3);
  609. var QUAT4_IDENT = new Float32Array([0,0,0,1]);
  610. var AXIS_X = new Float32Array([1, 0, 0]);
  611. var AXIS_Y = new Float32Array([0, 1, 0]);
  612. var AXIS_Z = new Float32Array([0, 0, 1]);
  613. var AXIS_MX = new Float32Array([-1, 0, 0]);
  614. var AXIS_MY = new Float32Array([ 0,-1, 0]);
  615. var AXIS_MZ = new Float32Array([ 0, 0,-1]);
  616. if (!m_util.cmp_arr(VEC3_IDENT, m_util.VEC3_IDENT))
  617. throw "Wrong VEC3_IDENT";
  618. if (!m_util.cmp_arr(QUAT4_IDENT, m_util.QUAT4_IDENT))
  619. throw "Wrong QUAT4_IDENT";
  620. if (!m_util.cmp_arr(AXIS_X, m_util.AXIS_X))
  621. throw "Wrong AXIS_X";
  622. if (!m_util.cmp_arr(AXIS_Y, m_util.AXIS_Y))
  623. throw "Wrong AXIS_Y";
  624. if (!m_util.cmp_arr(AXIS_Z, m_util.AXIS_Z))
  625. throw "Wrong AXIS_Z";
  626. if (!m_util.cmp_arr(AXIS_MX, m_util.AXIS_MX))
  627. throw "Wrong AXIS_MX";
  628. if (!m_util.cmp_arr(AXIS_MY, m_util.AXIS_MY))
  629. throw "Wrong AXIS_MY";
  630. if (!m_util.cmp_arr(AXIS_MZ, m_util.AXIS_MZ))
  631. throw "Wrong AXIS_MZ";
  632. }
  633. /**
  634. * Mute the BACKGROUND_MUSIC speakers.
  635. * @method module:debug.mute_music
  636. */
  637. exports.mute_music = function() {
  638. var spks = m_sfx.get_speaker_objects();
  639. for (var i = 0; i < spks.length; i++) {
  640. var spk = spks[i];
  641. if (m_sfx.get_spk_behavior(spk) == "BACKGROUND_MUSIC")
  642. m_sfx.mute(spk, true);
  643. }
  644. }
  645. /**
  646. * Check the object for a finite value.
  647. * @method module:debug.check_finite
  648. * @param {*} o Value
  649. */
  650. exports.check_finite = function(o) {
  651. return m_debug_check.finite(o);
  652. }
  653. /**
  654. * Set debugging parameters.
  655. * @method module:debug.set_debug_params
  656. * @param {DebugParams} params Debug parameters
  657. * @cc_externs debug_view_mode wireframe_edge_color debug_colors_seed
  658. */
  659. exports.set_debug_params = function(params) {
  660. var active_scene = m_scenes.get_active();
  661. var subs_debug_views = m_scenes.subs_array(active_scene, [m_subs.DEBUG_VIEW]);
  662. if (!subs_debug_views.length) {
  663. m_print.error("Debugging is not available on the scene.");
  664. return;
  665. }
  666. for (var i = 0; i < subs_debug_views.length; i++) {
  667. var subs_debug_view = subs_debug_views[i];
  668. if (typeof params.debug_view_mode == "number") {
  669. switch (params.debug_view_mode) {
  670. case m_debug_subscene.DV_NONE:
  671. case m_debug_subscene.DV_OPAQUE_WIREFRAME:
  672. case m_debug_subscene.DV_TRANSPARENT_WIREFRAME:
  673. case m_debug_subscene.DV_FRONT_BACK_VIEW:
  674. case m_debug_subscene.DV_BOUNDINGS:
  675. case m_debug_subscene.DV_CLUSTERS_VIEW:
  676. case m_debug_subscene.DV_BATCHES_VIEW:
  677. case m_debug_subscene.DV_RENDER_TIME:
  678. m_scenes.set_debug_view_mode(subs_debug_view, params.debug_view_mode);
  679. break;
  680. default:
  681. m_print.error("set_debug_params(): Wrong debug view mode");
  682. break;
  683. }
  684. }
  685. if (typeof params.debug_colors_seed == "number")
  686. m_scenes.set_debug_colors_seed(subs_debug_view, params.debug_colors_seed);
  687. if (typeof params.render_time_threshold == "number")
  688. m_scenes.set_render_time_threshold(subs_debug_view, params.render_time_threshold);
  689. if (typeof params.wireframe_edge_color == "object")
  690. m_scenes.set_wireframe_edge_color(subs_debug_view,
  691. m_util.f32(params.wireframe_edge_color));
  692. }
  693. }
  694. exports.get_error_quantity = function() {
  695. return m_print.get_error_count();
  696. }
  697. exports.get_warning_quantity = function() {
  698. return m_print.get_warning_count();
  699. }
  700. exports.clear_errors_warnings = function() {
  701. return m_print.clear_errors_warnings();
  702. }
  703. /**
  704. * Print shaders' statistics.
  705. * @method module:debug.analyze_shaders
  706. * @param {string} [opt_shader_id_part=""] Shader ID (filename) part.
  707. */
  708. exports.analyze_shaders = function(opt_shader_id_part) {
  709. var compiled_shaders = m_shaders.get_compiled_shaders();
  710. var count = 0;
  711. for (var shader_id in compiled_shaders) {
  712. if (opt_shader_id_part && shader_id.indexOf(opt_shader_id_part) === -1)
  713. continue;
  714. count++;
  715. }
  716. var msg = "of " + count + " analyzing...";
  717. var rslts = {};
  718. for (var shader_id in compiled_shaders) {
  719. if (opt_shader_id_part && shader_id.indexOf(opt_shader_id_part) === -1)
  720. continue;
  721. var cshader = compiled_shaders[shader_id];
  722. var stat = get_shaders_stat(cshader.vshader, cshader.fshader);
  723. if (!stat)
  724. continue;
  725. var shaders_info = cshader.shaders_info;
  726. var title = shaders_info.vert + " + " + shaders_info.frag;
  727. // NOTE: cshader.shaders_info
  728. stat.cshader = cshader;
  729. stat.shaders_info = shaders_info;
  730. var stats = rslts[title] = rslts[title] || [];
  731. stats.push(stat);
  732. m_print.log_raw(msg);
  733. }
  734. for (var title in rslts) {
  735. m_print.group("%c" + title, "color: #800");
  736. var stats = rslts[title];
  737. print_shader_stats(stats);
  738. m_print.groupEnd();
  739. }
  740. }
  741. /**
  742. * Return stage callback without loading data.
  743. * @method module:debug.fake_load
  744. * @param {StageloadCallback} stageload_cb Callback to report about the loading progress
  745. * @param {number} [interval=5000] Loading interval
  746. * @param {number} [start=0] Start percentage
  747. * @param {number} [end=5000] End percentage
  748. * @param {LoadedCallback} [loaded_cb=null] Callback to be executed right after load
  749. */
  750. exports.fake_load = function(stageload_cb, interval, start, end, loaded_cb) {
  751. stageload_cb = stageload_cb || null;
  752. if (!stageload_cb)
  753. m_assert.panic("Stage load callback is undefined");
  754. interval = interval || FAKE_LOAD_INTERVAL;
  755. start = start || FAKE_LOAD_START_PERCENTAGE;
  756. end = end || FAKE_LOAD_END_PERCENTAGE;
  757. if (end > 100)
  758. m_assert.panic("Max percentage must be less than 100");
  759. if (start < 0)
  760. m_assert.panic("Min percentage must be greater than 0");
  761. if (start > end)
  762. m_assert.panic("Max percentage must be greater than min percentage");
  763. var animator = m_time.animate(start, end, interval, function(e) {
  764. var rounded_percentage = e.toFixed();
  765. stageload_cb(rounded_percentage);
  766. if (rounded_percentage == 100) {
  767. m_time.clear_animation(animator);
  768. if (loaded_cb)
  769. loaded_cb();
  770. return;
  771. }
  772. })
  773. }
  774. function get_shaders_stat(vshader, fshader) {
  775. var ext_ds = m_ext.get_debug_shaders();
  776. if (!ext_ds) {
  777. m_print.warn("WEBGL_debug_shaders extension not found");
  778. return;
  779. }
  780. var vsrc_trans = ext_ds.getTranslatedShaderSource(vshader);
  781. var fsrc_trans = ext_ds.getTranslatedShaderSource(fshader);
  782. if (m_compat.detect_mobile()) {
  783. vsrc_trans = vsrc_trans.replace("#version", "#version 300 //")
  784. fsrc_trans = fsrc_trans.replace("#version", "#version 300 //")
  785. var vout = post_sync("/analyze_shader/vert_gles", vsrc_trans);
  786. var fout = post_sync("/analyze_shader/frag_gles", fsrc_trans);
  787. } else {
  788. // HACK: lower GLSL version for cgc tool
  789. vsrc_trans = vsrc_trans.replace("#version", "#version 400 //")
  790. fsrc_trans = fsrc_trans.replace("#version", "#version 400 //")
  791. var vout = post_sync("/analyze_shader/vert", vsrc_trans);
  792. var fout = post_sync("/analyze_shader/frag", fsrc_trans);
  793. }
  794. var vstats = parse_shader_assembly(vout);
  795. var fstats = parse_shader_assembly(fout);
  796. return {
  797. vsrc: m_context.get_gl().getShaderSource(vshader),
  798. vsrc_trans: vsrc_trans,
  799. vout: vout,
  800. vstats: vstats,
  801. fsrc: m_context.get_gl().getShaderSource(fshader),
  802. fsrc_trans: fsrc_trans,
  803. fout: fout,
  804. fstats: fstats
  805. };
  806. }
  807. function parse_shader_assembly(data) {
  808. var stats = {};
  809. if (!data)
  810. return stats;
  811. var lines = data.split("\n");
  812. for (var i = 0; i < lines.length; i++) {
  813. var line = lines[i];
  814. if (line.search(new RegExp(/^[A-Z.]+ ?/)) == -1) {
  815. continue;
  816. }
  817. var op = line.split(" ")[0];
  818. if (!(op in stats))
  819. stats[op] = 0;
  820. stats[op]++;
  821. }
  822. var all_ops = 0;
  823. var tex_ops = 0;
  824. var attribs = 0;
  825. for (var op in stats) {
  826. switch (op) {
  827. case "KIL":
  828. case "TEX":
  829. case "TXB":
  830. case "TXP":
  831. case "KIL.F":
  832. case "TEX.F":
  833. case "TXB.F":
  834. case "TXD.F":
  835. case "TXL.F":
  836. case "TXQ.F":
  837. case "TXP.F":
  838. tex_ops += stats[op];
  839. all_ops += stats[op];
  840. break;
  841. // data type qualifiers
  842. case "ATTRIB":
  843. attribs += stats[op];
  844. break;
  845. case "ADDRESS":
  846. case "PARAM":
  847. case "TEMP":
  848. case "ALIAS":
  849. case "OUTPUT":
  850. break;
  851. // end program line
  852. case "END":
  853. break;
  854. default:
  855. all_ops += stats[op];
  856. break;
  857. }
  858. }
  859. stats["ALL_OPS"] = all_ops;
  860. stats["TEX_OPS"] = tex_ops;
  861. stats["ATTRIBS"] = attribs;
  862. return stats;
  863. }
  864. function post_sync(path, data) {
  865. var req = new XMLHttpRequest();
  866. req.open("POST", path, false);
  867. req.send(data);
  868. if (req.status == 200)
  869. return req.responseText;
  870. else {
  871. m_print.error(req.responseText);
  872. throw("Error POST XHR: " + req.status);
  873. }
  874. }
  875. function print_shader_stats(stats) {
  876. // sort in descending order by fragment shader operations
  877. stats.sort(function(a, b) {
  878. return b.fstats["ALL_OPS"] - a.fstats["ALL_OPS"];
  879. })
  880. for (var j = 0; j < stats.length; j++) {
  881. var stat = stats[j];
  882. var fstats = stat.fstats;
  883. var vstats = stat.vstats;
  884. var mat_names = find_material_names_by_comp_shader(stat.cshader);
  885. mat_names = mat_names.length ? "\t\t(" + mat_names.join(", ") + ")" : "\t\t(NA)";
  886. // NOTE some not changing params are commented out
  887. m_print.groupCollapsed(
  888. "VERT ->",
  889. "OPS", vstats["ALL_OPS"],
  890. "ATT", vstats["ATTRIBS"],
  891. "TEX", vstats["TEX_OPS"],
  892. "\t\tFRAG ->",
  893. "OPS", fstats["ALL_OPS"],
  894. "TEX", fstats["TEX_OPS"],
  895. mat_names
  896. );
  897. m_print.groupCollapsed("directives");
  898. // NOTE: perhaps they should be stored in sorted order
  899. var dirs = stat.shaders_info.directives.slice().sort();
  900. for (var i = 0; i < dirs.length; i++) {
  901. var dir = dirs[i];
  902. m_print.log_raw(dir[0], dir[1]);
  903. }
  904. m_print.groupEnd();
  905. m_print.groupCollapsed("node elements");
  906. var nelem = stat.shaders_info.node_elements;
  907. for (var i = 0; i < nelem.length; i++)
  908. m_print.log_raw(nelem[i]);
  909. m_print.groupEnd();
  910. m_print.groupCollapsed("vert source");
  911. m_print.log_raw(stat.vsrc);
  912. m_print.groupEnd();
  913. m_print.groupCollapsed("vert translated source");
  914. m_print.log_raw(stat.vsrc_trans);
  915. m_print.groupEnd();
  916. // ignore them as they used for collective stats
  917. var ignored_stats = ["ALL_OPS", "TEX_OPS", "ATTRIBS"];
  918. m_print.groupCollapsed("vert ops stats");
  919. for (var op in vstats)
  920. if (ignored_stats.indexOf(op) == -1)
  921. m_print.log_raw(op, vstats[op]);
  922. m_print.groupEnd();
  923. m_print.groupCollapsed("vert assembly");
  924. m_print.log_raw(stat.vout);
  925. m_print.groupEnd();
  926. m_print.groupCollapsed("frag source");
  927. m_print.log_raw(stat.fsrc);
  928. m_print.groupEnd();
  929. m_print.groupCollapsed("frag translated source");
  930. m_print.log_raw(stat.fsrc_trans);
  931. m_print.groupEnd();
  932. m_print.groupCollapsed("frag ops stats");
  933. for (var op in fstats)
  934. if (ignored_stats.indexOf(op) == -1)
  935. m_print.log_raw(op, fstats[op]);
  936. m_print.groupEnd();
  937. m_print.groupCollapsed("frag assembly");
  938. m_print.log_raw(stat.fout);
  939. m_print.groupEnd();
  940. m_print.groupEnd();
  941. }
  942. }
  943. function find_material_names_by_comp_shader(cshader) {
  944. var names = [];
  945. var scenes = m_scenes.get_all_scenes();
  946. for (var i = 0; i < scenes.length; i++) {
  947. var scene = scenes[i];
  948. var objects = m_obj.get_scene_objs(scene, "MESH", m_obj.DATA_ID_ALL);
  949. for (var j = 0; j < objects.length; j++) {
  950. var obj = objects[j];
  951. var scene_data = m_obj_util.get_scene_data(obj, scene);
  952. if (!scene_data || !scene_data.batches.length)
  953. continue;
  954. var batches = scene_data.batches;
  955. for (var k = 0; k < batches.length; k++) {
  956. var batch = batches[k];
  957. if (batch.shader == cshader)
  958. for (var l = 0; l < batch.material_names.length; l++) {
  959. var name = batch.material_names[l];
  960. if (names.indexOf(name) == -1)
  961. names.push(name);
  962. }
  963. }
  964. }
  965. }
  966. return names;
  967. }
  968. /**
  969. * Perform simple performance test.
  970. * @method module:debug.test_performance
  971. * @param {TestPerformanceCallback} callback Callback
  972. */
  973. exports.test_performance = function(callback) {
  974. // waiting for shaders
  975. if (!m_shaders.check_shaders_loaded()) {
  976. window.setTimeout(function() {
  977. exports.test_performance(callback);
  978. }, 100);
  979. return;
  980. }
  981. var ext = m_ext.get_disjoint_timer_query();
  982. if (!ext) {
  983. callback(0, 0);
  984. return;
  985. }
  986. var gl_debug_save = cfg_def.gl_debug;
  987. // enable it to force timer queries update for
  988. // paused engine / unloaded scenes
  989. cfg_def.gl_debug = true;
  990. var graph = m_scgraph.create_performance_graph();
  991. m_scenes.generate_auxiliary_batches(null, graph);
  992. var subs = m_scgraph.find_subs(graph, m_subs.PERFORMANCE);
  993. var cam = subs.camera;
  994. for (var i = 0; i < PERF_NUM_CALLS; i++)
  995. m_render.draw(subs);
  996. cfg_def.gl_debug = gl_debug_save;
  997. window.setTimeout(function() {
  998. m_debug_render_time.process_timer_queries(subs);
  999. // in ms
  1000. var time = subs.debug_render_time;
  1001. // in GB/s (100 texture lookups)
  1002. var bandwidth = (cam.width * cam.height * 4 * 10) /
  1003. (time / 1000) / Math.pow(10, 9);
  1004. callback(time, bandwidth);
  1005. }, 100);
  1006. }
  1007. exports.calc_vbo_garbage_byte_size = m_debug.calc_vbo_garbage_byte_size;
  1008. exports.show_vbo_garbage_info = m_debug.show_vbo_garbage_info;
  1009. exports.print_batches_stat = function() {
  1010. m_debug_batch.print_batches_stat();
  1011. }
  1012. function call(func, name) {
  1013. var decor_func = function() {
  1014. _called_funcs.push(decor_func);
  1015. return func.apply(func, arguments);
  1016. }
  1017. return decor_func;
  1018. }
  1019. exports.start_debug = function(module) {
  1020. _called_funcs = [];
  1021. _test_result = true;
  1022. if (typeof module === "string")
  1023. module = b4w.require(module);
  1024. for (var name in module)
  1025. if (typeof module[name] === "function")
  1026. module[name] = call(module[name], name);
  1027. }
  1028. exports.check_debug_result = function() {
  1029. return _test_result;
  1030. }
  1031. /**
  1032. * Test code.
  1033. * @method module:debug.test
  1034. * @param {string} test_name Test name
  1035. * @param {CodeTestCallback} callback Callback
  1036. */
  1037. exports.test = function(test_name, callback) {
  1038. var print_err_func = m_print.error;
  1039. var print_err_once_func = m_print.error_once;
  1040. var print_warn_func = m_print.warn;
  1041. m_print.error = m_print.error_once = function error() {
  1042. var args = m_print.compose_args_prefix(arguments, "B4W ERROR");
  1043. _last_err_message = args.join(" ");
  1044. _err_got = true;
  1045. }
  1046. m_print.warn = function warn() {
  1047. var args = m_print.compose_args_prefix(arguments, "B4W WARN");
  1048. _last_warn_message = args.join(" ");
  1049. _warn_got = true;
  1050. }
  1051. _warn_got = false;
  1052. _err_got = false;
  1053. try {
  1054. callback();
  1055. var success = true;
  1056. } catch(e) {
  1057. _test_result = false;
  1058. console.error("Test \"" + test_name + "\" failed with exception: \"" + e + "\"");
  1059. var success = false;
  1060. _warn_got = false;
  1061. _err_got = false;
  1062. }
  1063. m_print.error = print_err_func;
  1064. m_print.error_once = print_err_once_func;
  1065. m_print.warn = print_warn_func;
  1066. return success;
  1067. }
  1068. /**
  1069. * Compare color picked at the center of the screen with reference RGBA vector.
  1070. * @param {RGBA} ref_color Reference RGBA vector to compare with.
  1071. */
  1072. exports.pix = function(ref_color) {
  1073. var canvas_w = m_cont.get_viewport_width();
  1074. var canvas_h = m_cont.get_viewport_height();
  1075. var canvas_x = canvas_w / 2;
  1076. var canvas_y = canvas_h / 2;
  1077. m_cont.resize(canvas_w, canvas_h, false);
  1078. var scene = m_scenes.get_active();
  1079. var graph = scene._render.graph;
  1080. var subs = m_scgraph.find_on_screen(graph);
  1081. if (!subs)
  1082. m_assert.panic("Couldn't find onscreen subscene");
  1083. var cam = subs.camera;
  1084. var viewport_xy = m_cont.canvas_to_viewport_coords(canvas_x, canvas_y,
  1085. _vec2_tmp, subs.camera);
  1086. viewport_xy[1] = cam.height - viewport_xy[1];
  1087. var color = m_render.read_pixels(cam.framebuffer, viewport_xy[0],
  1088. viewport_xy[1], 1, 1, _pixel);
  1089. eqv(ref_color, color, 1);
  1090. }
  1091. exports.eqs = function(result, exp_result, expected_err, expected_warn) {
  1092. if (JSON.stringify(result) != JSON.stringify(exp_result))
  1093. throw "debug.eqs: wrong result";
  1094. check_err_warn_messages(expected_err, expected_warn, "eqs");
  1095. }
  1096. exports.eqv = eqv;
  1097. function eqv(result, exp_result, eps, expected_err, expected_warn) {
  1098. if (typeof exp_result != typeof result)
  1099. throw "debug.eqv: wrong expected data type";
  1100. if (result.length != exp_result.length)
  1101. throw "debug.eqv: wrong expected vector length";
  1102. eps = eps ? eps : EPS;
  1103. for (var i = 0; i < result.length; i++) {
  1104. // NaN values are not allowed
  1105. if (typeof exp_result[i] != "number" || isNaN(exp_result[i]))
  1106. throw "debug.eqv: wrong expected data type";
  1107. if (typeof result[i] != "number" || isNaN(result[i]))
  1108. throw "debug.eqv: wrong result data type";
  1109. if (exp_result[i] > result[i] + eps || exp_result[i] < result[i] - eps)
  1110. throw "debug.eqv: wrong result";
  1111. }
  1112. check_err_warn_messages(expected_err, expected_warn, "eqv");
  1113. }
  1114. exports.eqf = function(result, exp_result, eps, expected_err, expected_warn) {
  1115. // NaN values are not allowed
  1116. if (typeof exp_result != "number" || isNaN(exp_result))
  1117. throw "debug.eqf: wrong expected data type";
  1118. if (typeof result != "number" || isNaN(result))
  1119. throw "debug.eqf: wrong result data type";
  1120. eps = eps ? eps : EPS;
  1121. if (exp_result > result + eps || exp_result < result - eps)
  1122. throw "debug.eqf: wrong result";
  1123. check_err_warn_messages(expected_err, expected_warn, "eqf");
  1124. }
  1125. exports.eq = function(result, exp_result, expected_err, expected_warn) {
  1126. if (result !== exp_result)
  1127. throw "debug.eq: wrong result";
  1128. check_err_warn_messages(expected_err, expected_warn, "eq");
  1129. }
  1130. exports.ok = function(exp, expected_err, expected_warn) {
  1131. if (!Boolean(exp))
  1132. throw "debug.ok: wrong result";
  1133. check_err_warn_messages(expected_err, expected_warn, "ok");
  1134. }
  1135. function check_err_warn_messages(expected_err, expected_warn, func_name) {
  1136. expected_err = expected_err || "";
  1137. expected_warn = expected_warn || "";
  1138. if (_err_got) {
  1139. if (expected_err == "")
  1140. throw "debug." + func_name + ": no error is expected, but got \""
  1141. + _last_err_message + "\"";
  1142. else if (_last_err_message != expected_err)
  1143. throw "debug." + func_name + ": error \"" + expected_err
  1144. + "\" is expected, but got \"" + _last_err_message + "\"";
  1145. } else {
  1146. if (expected_err != "")
  1147. throw "debug." + func_name + ": error \"" + expected_err
  1148. + "\" is expected, but got nothing";
  1149. }
  1150. if (_warn_got) {
  1151. if (expected_warn == "")
  1152. throw "debug." + func_name + ": no warning is expected, but got \""
  1153. + _last_warn_message + "\"";
  1154. else if (_last_warn_message != expected_warn)
  1155. throw "debug." + func_name + ": warning \"" + expected_warn
  1156. + "\" is expected, but got \"" + _last_warn_message + "\"";
  1157. } else {
  1158. if (expected_warn != "")
  1159. throw "debug." + func_name + ": warning \"" + expected_warn
  1160. + "\" is expected, but got nothing";
  1161. }
  1162. _warn_got = false;
  1163. _err_got = false;
  1164. }
  1165. exports.stat = function(module) {
  1166. var missing_functions = [];
  1167. if (typeof module === "string")
  1168. module = b4w.require(module);
  1169. for (var name in module)
  1170. if (_called_funcs.indexOf(module[name]) == -1 &&
  1171. typeof module[name] === "function")
  1172. missing_functions.push(name);
  1173. if (missing_functions.length) {
  1174. m_print.groupCollapsed(missing_functions.length + " function(s) not tested.");
  1175. for (var i = 0; i < missing_functions.length; i++)
  1176. m_print.log_raw(missing_functions[i]);
  1177. m_print.groupEnd();
  1178. } else
  1179. m_print.group("All functions were tested.");
  1180. }
  1181. /**
  1182. * Show normals of the dynamic object.
  1183. * @method module:debug.show_normals
  1184. * @param {Object3D} obj Object 3D
  1185. * @param {string} mat_name Material name
  1186. * @param {number} length Length of normals
  1187. * @param {number} width Width of normals
  1188. */
  1189. exports.show_normals = function(obj, mat_name, length, width) {
  1190. hide_normals();
  1191. var batch = m_batch.find_batch_material(obj, mat_name, "MAIN");
  1192. if (!m_geom.has_dyn_geom(obj) || !batch) {
  1193. m_print.error("Normals are not avaliable for the dynamic object:", obj.name);
  1194. return false;
  1195. }
  1196. var bufs_data = batch.bufs_data;
  1197. if (!(bufs_data && bufs_data.pointers &&
  1198. bufs_data.pointers["a_position"] &&
  1199. bufs_data.pointers["a_tbn"])) {
  1200. m_print.error("Normals are not avaliable for the object:", obj.name);
  1201. return false;
  1202. }
  1203. var positions = m_geom.extract_array_float(bufs_data, "a_position");
  1204. var norms = m_geom.extract_array_float(bufs_data, "a_normal");
  1205. var obj_tsr = m_trans.get_tsr(obj, _tsr_tmp);
  1206. _normal_line = m_obj.create_line("normal_line");
  1207. var normals = new Float32Array(2 * positions.length);
  1208. for (var i = 0; i < positions.length; i += 3) {
  1209. var ver_pos = _vec3_tmp;
  1210. ver_pos[0] = positions[i + 0];
  1211. ver_pos[1] = positions[i + 1];
  1212. ver_pos[2] = positions[i + 2];
  1213. var begin_norm = m_tsr.transform_vec3(ver_pos, obj_tsr, _vec3_tmp2);
  1214. normals[2 * i + 0] = begin_norm[0];
  1215. normals[2 * i + 1] = begin_norm[1];
  1216. normals[2 * i + 2] = begin_norm[2];
  1217. var dir = m_vec3.scale(norms.subarray(i, i + 3), length, _vec3_tmp2);
  1218. var end_norm_l = m_vec3.add(ver_pos, dir, _vec3_tmp2);
  1219. var end_norm = m_tsr.transform_vec3(end_norm_l, obj_tsr, _vec3_tmp2);
  1220. normals[2 * i + 3] = end_norm[0];
  1221. normals[2 * i + 4] = end_norm[1];
  1222. normals[2 * i + 5] = end_norm[2];
  1223. }
  1224. var normal_line_batch = m_batch.get_first_batch(_normal_line);
  1225. m_geom.draw_line(normal_line_batch, normals, true);
  1226. m_render.assign_attribute_setters(normal_line_batch);
  1227. normal_line_batch.diffuse_color.set([1.0, 1.0, 1.0, 1.0]);
  1228. normal_line_batch.line_width = width;
  1229. }
  1230. /**
  1231. * Hide normals of a dynamic object.
  1232. * @method module:debug.hide_normals
  1233. */
  1234. exports.hide_normals = hide_normals;
  1235. function hide_normals() {
  1236. if (!_normal_line)
  1237. return;
  1238. // NOTE: it is a copy/paste m_scenes.remove_object
  1239. m_obj.obj_switch_cleanup_flags(_normal_line, false, false, false);
  1240. m_data.prepare_object_unloading(_normal_line);
  1241. m_obj.obj_switch_cleanup_flags(_normal_line, true, true, true);
  1242. m_obj.remove_object(_normal_line);
  1243. _normal_line = null;
  1244. }
  1245. }
  1246. var debug_factory = register("debug", Debug);
  1247. export default debug_factory;