Source: addons/app.js

  1. import register from "../util/register.js";
  2. import m_cam_fact from "../extern/camera.js";
  3. import m_cfg_fact from "../extern/config.js";
  4. import m_cons_fact from "../extern/constraints.js";
  5. import m_cont_fact from "../extern/container.js";
  6. import m_ctl_fact from "../extern/controls.js";
  7. import m_data_fact from "../extern/data.js";
  8. import m_dbg_fact from "../extern/debug.js";
  9. import m_input_fact from "../extern/input.js";
  10. import m_main_fact from "../extern/main.js";
  11. import m_phy_fact from "../extern/physics.js";
  12. import m_print_fact from "../intern/print.js";
  13. import m_screen_fact from "../extern/screen.js";
  14. import m_scs_fact from "../extern/scenes.js";
  15. import m_trans_fact from "../extern/transform.js";
  16. import m_util_fact from "../extern/util.js";
  17. import * as m_vec3 from "../libs/gl_matrix/vec3.js";
  18. /**
  19. * Application add-on.
  20. * Provides generic routines for the engine's initialization, UI and I/O.
  21. * @module app
  22. * @local AppInitCallback
  23. * @local AnimFinishCallback
  24. * @local QueueObject
  25. */
  26. function App(ns, exports) {
  27. var m_cam = m_cam_fact(ns);
  28. var m_cfg = m_cfg_fact(ns);
  29. var m_cons = m_cons_fact(ns);
  30. var m_cont = m_cont_fact(ns);
  31. var m_ctl = m_ctl_fact(ns);
  32. var m_data = m_data_fact(ns);
  33. var m_dbg = m_dbg_fact(ns);
  34. var m_input = m_input_fact(ns);
  35. var m_main = m_main_fact(ns);
  36. var m_phy = m_phy_fact(ns);
  37. var m_print = m_print_fact(ns);
  38. var m_screen = m_screen_fact(ns);
  39. var m_scs = m_scs_fact(ns);
  40. var m_trans = m_trans_fact(ns);
  41. var m_util = m_util_fact(ns);
  42. // Constants used for the target camera
  43. var TARGET_KEY_ZOOM_POW1 = 1.0;
  44. var TARGET_KEY_ZOOM_POW2 = 0.15;
  45. var TARGET_TOUCH_ZOOM_FACTOR = 0.03;
  46. // Constants used for the eye camera
  47. var EYE_KEY_TRANS_FACTOR = 5.0;
  48. var EYE_ROTATION_DECREMENT = 0.5;
  49. // Constants used for both target and eye camera
  50. var TARGET_EYE_MOUSE_ROT_MULT_PX = 0.003;
  51. var TARGET_EYE_MOUSE_PAN_MULT_PX = 0.00075;
  52. var TARGET_EYE_TOUCH_ROT_MULT_PX = 0.003;
  53. var TARGET_EYE_TOUCH_PAN_MULT_PX = 0.00075;
  54. var TARGET_EYE_KEY_ROT_FACTOR = 0.75;
  55. // Constants used for the hover camera
  56. var HOVER_MOUSE_PAN_MULT_PX = 0.003;
  57. var HOVER_MOUSE_ROT_MULT_PX = 0.00075;
  58. var HOVER_TOUCH_PAN_MULT_PX = 0.003;
  59. var HOVER_TOUCH_ROT_MULT_PX = 0.003;
  60. var HOVER_KEY_TRANS_FACTOR = 0.5;
  61. var HOVER_KEY_ZOOM_FACTOR = 30.0;
  62. var HOVER_MOUSE_TOUCH_TRANS_FACTOR = 0.2;
  63. var HOVER_MOUSE_ZOOM_FACTOR = 2.0;
  64. var HOVER_TOUCH_ZOOM_FACTOR = 2.0;
  65. var HOVER_ZOOM_FACTOR_MIN = 2.0;
  66. var HOVER_SPEED_MIN = 1.0;
  67. var _smooth_factor = 1;
  68. // Constants used for camera smoothing
  69. var CAM_SMOOTH_ZOOM_MOUSE = 0.1;
  70. var CAM_SMOOTH_ZOOM_TOUCH = 0.15;
  71. var CAM_SMOOTH_ROT_TRANS_MOUSE = 0.08;
  72. var CAM_SMOOTH_ROT_TRANS_TOUCH = 0.12;
  73. // Constants used for camera physics
  74. var CHAR_HEAD_POSITION = 0.5;
  75. var CAM_COLL_DELTA = 0.25
  76. // NOTE: EPSILON_DELTA << EPSILON_DISTANCE to prevent camera freezing near pivot
  77. var EPSILON_DISTANCE = 0.001;
  78. var EPSILON_DELTA = 0.00001;
  79. var FRAME_WAITING_PER = 10;
  80. var AXIS_THRESHOLD = 0.1;
  81. var TRANS_GMPD_KOEF = 0.002;
  82. var ZOOM_GMPD_KOEF = 0.05;
  83. var LOW_BANDWIDTH_LIMIT = 1.5;
  84. // Cached HTML elements
  85. var _fps_logger_elem = null;
  86. // Global flags used for re-initializing of application camera controls
  87. // (which should be done after switching camera type)
  88. var _disable_default_pivot = false;
  89. var _disable_letter_controls = false;
  90. var _disable_zoom = false;
  91. var _element = null;
  92. var _allow_element_exit = false;
  93. var _disable_gamepad_controls = false;
  94. // Cached arrays
  95. var _vec2_tmp = new Float32Array(2);
  96. var _vec2_tmp2 = new Float32Array(2);
  97. var _vec3_tmp = new Float32Array(3);
  98. var _vec3_tmp2 = new Float32Array(3);
  99. var _quat4_tmp = new Float32Array(4);
  100. var _velocity_tmp = {};
  101. var _limits_tmp = {};
  102. /**
  103. * Application initialization callback.
  104. * @callback AppInitCallback
  105. * @param {HTMLElement} canvas Initialized canvas element.
  106. * @param {boolean} success Success flag.
  107. */
  108. /**
  109. * Initialize the engine.
  110. * The "options" object may be extended by adding properties from the engine's
  111. * configuration (see {@link module:config|config} module).
  112. * In that case they will be applied before engine initialization.
  113. * @param {Object} [options={}] Initialization options.
  114. * @param {string} [options.canvas_container_id=null] Canvas container ID.
  115. * @param {AppInitCallback} [options.callback=function(){}] Initialization callback.
  116. * @param {string[]} [options.error_purge_elements=null] Array of IDs of HTML
  117. * elements to be removed in case of initialization error.
  118. * @param {boolean} [options.show_hud_debug_info=false] Show HUD with
  119. * developer info.
  120. * @param {boolean} [options.show_fps=false] Show FPS counter.
  121. * @param {string} [options.fps_elem_id=null] Custom fps counter id.
  122. * @param {string} [options.fps_wrapper_id=null] Show FPS wrapper with
  123. * current id.
  124. * @param {boolean} [options.report_init_failure=true] Show elements with info
  125. * about init failure
  126. * @param {boolean} [options.pause_invisible=true] Pause engine simulation if
  127. * page is not visible (in other tab or minimized).
  128. * @param {boolean} [options.key_pause_enabled=true] Enable key pause
  129. * @param {boolean} [options.autoresize=false] Automatically resize canvas to
  130. * match the size of container element.
  131. * @param {number} [options.force_container_ratio=0] Automatically resize
  132. * canvas container height, based on its width and passed ratio value.
  133. * @param {boolean} [options.min_capabilities=false] Set min capability mode
  134. * @param {boolean} [options.debug_loading=false] Print loading info into the console
  135. * @cc_externs canvas_container_id callback show_hud_debug_info
  136. * @cc_externs sfx_mix_mode show_fps fps_elem_id error_purge_elements
  137. * @cc_externs report_init_failure pause_invisible key_pause_enabled
  138. * @cc_externs alpha alpha_sort_threshold assets_dds_available assets_pvr_available
  139. * @cc_externs assets_min50_available quality fps_wrapper_id
  140. * @cc_externs console_verbose physics_enabled autoresize track_container_position
  141. * @cc_externs force_container_ratio from to elem prop cb duration opt_prefix
  142. * @cc_externs opt_suffix min_capabilities srgb_type debug_loading
  143. */
  144. exports.init = function(options) {
  145. options = options || {};
  146. var autoresize = false;
  147. var canvas_container_id = null;
  148. var callback = function() {};
  149. var error_purge_elements = null;
  150. var fps_elem_id = null;
  151. var force_container_ratio = 0;
  152. var fps_wrapper_id = null;
  153. var key_pause_enabled = true;
  154. var min_capabilities = false;
  155. var pause_invisible = true;
  156. var report_init_failure = true;
  157. var sfx_mix_mode = false;
  158. var show_fps = false;
  159. var show_hud_debug_info = false;
  160. var track_container_position = false;
  161. var quality = m_cfg.get("quality");
  162. for (var opt in options) {
  163. switch (opt) {
  164. case "canvas_container_id":
  165. canvas_container_id = options.canvas_container_id;
  166. break;
  167. case "callback":
  168. callback = options.callback;
  169. break;
  170. case "autoresize":
  171. autoresize = options.autoresize;
  172. break;
  173. case "show_hud_debug_info":
  174. show_hud_debug_info = options.show_hud_debug_info;
  175. break;
  176. case "sfx_mix_mode":
  177. sfx_mix_mode = options.sfx_mix_mode;
  178. break;
  179. case "show_fps":
  180. show_fps = options.show_fps;
  181. break;
  182. case "track_container_position":
  183. track_container_position = options.track_container_position;
  184. m_print.error_once("track_container_position deprecated. " +
  185. "Not needed anymore. Use the container.get_coords_target_space method.");
  186. break;
  187. case "fps_wrapper_id":
  188. fps_wrapper_id = options.fps_wrapper_id;
  189. break;
  190. case "fps_elem_id":
  191. fps_elem_id = options.fps_elem_id;
  192. break;
  193. case "error_purge_elements":
  194. error_purge_elements = options.error_purge_elements;
  195. break;
  196. case "report_init_failure":
  197. report_init_failure = options.report_init_failure;
  198. break;
  199. case "pause_invisible":
  200. pause_invisible = options.pause_invisible;
  201. break;
  202. case "key_pause_enabled":
  203. key_pause_enabled = options.key_pause_enabled;
  204. break;
  205. case "force_container_ratio":
  206. force_container_ratio = options.force_container_ratio;
  207. break;
  208. case "min_capabilities":
  209. min_capabilities = options.min_capabilities;
  210. break;
  211. case "quality":
  212. quality = options.quality;
  213. break;
  214. default:
  215. m_cfg.set(opt, options[opt]);
  216. break;
  217. }
  218. }
  219. if (quality != m_cfg.P_AUTO)
  220. m_cfg.set("quality", quality);
  221. var on_key_pause = function(e) {
  222. if (e.keyCode == m_ctl.KEY_P) {
  223. if (m_main.is_paused())
  224. m_main.resume();
  225. else
  226. m_main.pause();
  227. }
  228. }
  229. if (key_pause_enabled)
  230. document.addEventListener("keydown", on_key_pause, false);
  231. m_cfg.set("show_hud_debug_info", show_hud_debug_info);
  232. m_cfg.set("sfx_mix_mode", sfx_mix_mode);
  233. var init_hud_canvas = show_hud_debug_info || sfx_mix_mode || null;
  234. var onload_cb = function() {
  235. var init_ok = setup_canvas(canvas_container_id, init_hud_canvas,
  236. report_init_failure, error_purge_elements);
  237. var canvas_elem = m_cont.get_canvas();
  238. if (!init_ok) {
  239. callback(canvas_elem, false);
  240. return;
  241. }
  242. var canvas_container_elem = m_cont.get_container();
  243. m_cont.resize_to_container();
  244. if (show_fps) {
  245. create_fps_logger_elem(fps_elem_id, fps_wrapper_id);
  246. m_main.set_fps_callback(fps_callback);
  247. }
  248. if (pause_invisible) {
  249. handle_page_visibility();
  250. handle_position_visibility();
  251. }
  252. if (force_container_ratio) {
  253. m_main.append_loop_cb(function() {
  254. canvas_container_elem.style.height =
  255. canvas_container_elem.clientWidth / force_container_ratio + "px";
  256. });
  257. }
  258. if (autoresize)
  259. m_main.append_loop_cb(function() {
  260. m_cont.resize_to_container();
  261. });
  262. if (track_container_position) {
  263. m_main.append_loop_cb(function() {
  264. m_cont.force_offsets_updating();
  265. });
  266. }
  267. if (min_capabilities)
  268. m_cfg.reset_limits();
  269. if (quality == m_cfg.P_AUTO) {
  270. m_dbg.test_performance(function(time, bw) {
  271. if (time == 0) {
  272. m_print.log("QUALITY TEST RESULT: UNSUPPORTED, using HIGH");
  273. m_cfg.apply_quality(m_cfg.P_HIGH);
  274. } else if (bw < LOW_BANDWIDTH_LIMIT) {
  275. m_print.log("QUALITY TEST RESULT: LOW (" + bw.toFixed(1) + " GB/s)");
  276. m_cfg.apply_quality(m_cfg.P_LOW);
  277. } else {
  278. m_print.log("QUALITY TEST RESULT: HIGH (" + bw.toFixed(1) + " GB/s)");
  279. m_cfg.apply_quality(m_cfg.P_HIGH);
  280. }
  281. callback(canvas_elem, true);
  282. });
  283. } else
  284. callback(canvas_elem, true);
  285. };
  286. var onunload_cb = function() {
  287. m_data.cleanup();
  288. };
  289. if (document.readyState == "complete")
  290. window.setTimeout(onload_cb, 0);
  291. else
  292. window.addEventListener("load", onload_cb, false);
  293. window.addEventListener("unload", onunload_cb, false);
  294. }
  295. function handle_page_visibility() {
  296. var was_paused = m_main.is_paused();
  297. var visibility_change = function() {
  298. if (document.hidden) {
  299. was_paused = m_main.is_paused();
  300. m_main.pause();
  301. } else if (!was_paused)
  302. m_main.resume();
  303. }
  304. document.addEventListener("visibilitychange", visibility_change, false);
  305. }
  306. function handle_position_visibility() {
  307. var frame_counter = 0;
  308. var container = m_cont.get_container();
  309. var was_paused = m_main.is_paused();
  310. var check_paused = true;
  311. var visibility_change = function() {
  312. if (frame_counter % FRAME_WAITING_PER == 0) {
  313. var coords = container.getBoundingClientRect();
  314. if (coords.top > window.innerHeight
  315. || coords.bottom < 0) {
  316. if (check_paused)
  317. was_paused = m_main.is_paused();
  318. m_main.pause();
  319. check_paused = false;
  320. } else if (!was_paused && !check_paused) {
  321. m_main.resume();
  322. check_paused = true;
  323. }
  324. }
  325. frame_counter++;
  326. }
  327. m_main.append_loop_cb(visibility_change);
  328. }
  329. function setup_canvas(canvas_container_id, init_hud_canvas,
  330. report_init_failure, purge_elements) {
  331. var canvas_elem = document.createElement("canvas");
  332. var append_to = document.getElementById(canvas_container_id);
  333. if (!append_to) {
  334. m_print.error("Warning: canvas container \"" + canvas_container_id +
  335. "\" not found, appending to body");
  336. append_to = document.body;
  337. }
  338. canvas_elem.style.position = "absolute";
  339. canvas_elem.style.left = 0;
  340. canvas_elem.style.top = 0;
  341. canvas_elem.style.width = append_to.offsetWidth + "px";
  342. canvas_elem.style.height = append_to.offsetHeight + "px";
  343. canvas_elem.width = append_to.offsetWidth * window.devicePixelRatio;
  344. canvas_elem.height = append_to.offsetHeight * window.devicePixelRatio;
  345. if (init_hud_canvas) {
  346. var canvas_elem_hud = document.createElement("canvas");
  347. // NOTE: pointer-events only for Chrome, Firefox, Safari
  348. canvas_elem_hud.style.position = "absolute";
  349. canvas_elem.style.left = 0;
  350. canvas_elem.style.top = 0;
  351. canvas_elem_hud.style.pointerEvents = "none";
  352. } else
  353. var canvas_elem_hud = null;
  354. append_to.appendChild(canvas_elem);
  355. if (!m_main.init(canvas_elem, canvas_elem_hud)) {
  356. if (report_init_failure)
  357. report_app_error("Browser could not initialize WebGL", "For more info visit",
  358. "https://www.blend4web.com/doc/en/problems_and_solutions.html#problems-upon-startup", purge_elements);
  359. return false;
  360. }
  361. if (canvas_elem_hud)
  362. m_cont.insert_to_container(canvas_elem_hud, "LAST");
  363. return true;
  364. }
  365. function create_fps_logger_elem(fps_elem_id, fps_wrapper_id) {
  366. if (fps_elem_id) {
  367. if (fps_wrapper_id)
  368. document.getElementById(fps_wrapper_id).style.display = "block";
  369. _fps_logger_elem = document.getElementById(fps_elem_id);
  370. } else {
  371. _fps_logger_elem = document.createElement("div");
  372. _fps_logger_elem.innerHTML = 0;
  373. _fps_logger_elem.style.cssText =
  374. "position:absolute;" +
  375. "font-family: Arial, sans-serif;" +
  376. "top: 20px;" +
  377. "right: 20px;" +
  378. "text-shadow: 0px 0px 6px #000;" +
  379. "font-size: 40px;" +
  380. "line-height: 50px;" +
  381. "font-weight: bold;" +
  382. "color: #fff;";
  383. m_cont.insert_to_container(_fps_logger_elem, "JUST_AFTER_CANVAS");
  384. }
  385. }
  386. function fps_callback(fps, phy_fps) {
  387. var fps_str = String(fps);
  388. if (phy_fps)
  389. fps_str += "/" + String(phy_fps);
  390. _fps_logger_elem.innerHTML = fps_str;
  391. }
  392. function elem_cloned(elem_id) {
  393. var target = document.getElementById(elem_id);
  394. // clone to prevent adding event listeners more than once
  395. var new_element = target.cloneNode(true);
  396. target.parentNode.replaceChild(new_element, target);
  397. return new_element;
  398. }
  399. exports.set_onclick = function(elem_id, callback) {
  400. var elem = elem_cloned(elem_id);
  401. elem.addEventListener("mouseup", function(e) {
  402. callback(elem.value);
  403. }, false);
  404. }
  405. exports.set_onchange = function(elem_id, callback) {
  406. var elem = elem_cloned(elem_id);
  407. elem.addEventListener("change", function(e) {
  408. var checked = elem.checked;
  409. var rslt = checked != undefined ? checked : elem.value;
  410. callback(rslt);
  411. }, false);
  412. }
  413. exports.set_onkeypress = function(elem_id, callback) {
  414. var elem = elem_cloned(elem_id);
  415. elem.addEventListener("keypress", function(e) {
  416. callback(e.keyCode, elem.value);
  417. }, false);
  418. }
  419. function trans_hover_cam_horiz_local(camobj, dir, fact) {
  420. var dist = Math.max(m_cam.hover_get_distance(camobj), HOVER_SPEED_MIN);
  421. var obj_quat = m_trans.get_rotation(camobj, _quat4_tmp);
  422. var abs_dir = m_util.quat_to_dir(obj_quat, dir, _vec3_tmp);
  423. abs_dir[2] = 0;
  424. m_vec3.normalize(abs_dir, abs_dir);
  425. m_vec3.scale(abs_dir, dist * fact, abs_dir);
  426. var obj_trans = m_trans.get_translation(camobj, _vec3_tmp2);
  427. m_vec3.add(obj_trans, abs_dir, obj_trans)
  428. m_cam.set_translation(camobj, obj_trans);
  429. }
  430. function zoom_hover_cam(camobj, fact) {
  431. var limits = m_cam.hover_get_vertical_limits(camobj, _limits_tmp);
  432. if (limits.up != limits.down) {
  433. var y_angle = m_cam.get_camera_angles(camobj, _vec2_tmp2)[1];
  434. var angle_factor = (limits.down - y_angle) / (limits.down - limits.up);
  435. var dist_limits = m_cam.hover_get_distance_limits(camobj, _limits_tmp);
  436. angle_factor = Math.max(angle_factor, HOVER_ZOOM_FACTOR_MIN / dist_limits.max);
  437. m_cam.rotate_camera(camobj, 0, angle_factor * fact);
  438. }
  439. }
  440. function trans_eye_cam_local(camobj, fact_x, fact_y, fact_z) {
  441. fact_x *= EYE_KEY_TRANS_FACTOR;
  442. fact_y *= EYE_KEY_TRANS_FACTOR;
  443. fact_z *= EYE_KEY_TRANS_FACTOR;
  444. m_trans.move_local(camobj, fact_x, fact_y, fact_z);
  445. }
  446. function calc_fact_from(fact_to) {
  447. return fact_to / (1 - fact_to);
  448. }
  449. function trans_targ_cam_local(camobj, fact_view, elapsed) {
  450. var dist = m_cam.target_get_distance(camobj);
  451. var abs_fact_view = Math.abs(fact_view);
  452. var fact = Math.pow(abs_fact_view * elapsed, TARGET_KEY_ZOOM_POW1
  453. - Math.pow(abs_fact_view * elapsed, TARGET_KEY_ZOOM_POW2));
  454. if (fact_view < 0)
  455. if (dist > EPSILON_DISTANCE)
  456. fact_view = - dist * fact;
  457. else
  458. fact_view = 0;
  459. else
  460. fact_view = dist * calc_fact_from(fact);
  461. m_trans.move_local(camobj, 0, 0, fact_view);
  462. }
  463. function get_dest_mouse_touch(obj, value, fact, dist, dest_value) {
  464. var t_mult = dist * fact;
  465. for (var i = value; i > 0; --i) {
  466. dist += t_mult;
  467. dest_value += t_mult;
  468. t_mult = dist * fact;
  469. }
  470. return dest_value;
  471. }
  472. function get_dest_zoom(obj, value, velocity_zoom, dest_value, dev_fact,
  473. use_pivot) {
  474. if (use_pivot) {
  475. // camera zooming
  476. var cam_pivot = m_cam.target_get_pivot(obj, _vec3_tmp);
  477. var cam_eye = m_cam.get_translation(obj);
  478. var dist = m_vec3.dist(cam_pivot, cam_eye) + dest_value;
  479. if (value > 0)
  480. dest_value = get_dest_mouse_touch(obj, value,
  481. -velocity_zoom, dist, dest_value);
  482. else
  483. dest_value = get_dest_mouse_touch(obj, -value,
  484. calc_fact_from(velocity_zoom), dist, dest_value);
  485. } else
  486. // use_hover == True
  487. dest_value -= value * dev_fact * velocity_zoom;
  488. return dest_value;
  489. }
  490. /**
  491. * Assign keyboard and mouse controls to the active camera.
  492. * (arrow keys, ADSW, wheel and others)
  493. * @method module:app.enable_camera_controls
  494. * @param {boolean} [disable_default_pivot=false] Do not use the possible
  495. * camera-defined pivot point
  496. * @param {boolean} [disable_letter_controls=false] Disable keyboard letter controls
  497. * (only arrow keys will be used to control the camera).
  498. * @param {boolean} [disable_zoom=false] Disable zoom
  499. * @param {HTMLElement} [element] HTML element to add event listeners to.
  500. * The canvas container will be use by default.
  501. * @param {boolean} [allow_element_exit=false] Continue receiving mouse events
  502. * even when the mouse is leaving the HTML element
  503. * @param {boolean} [disable_gamepad_controls=false] Disable gamepad controls
  504. * @example
  505. * var m_app = require("app");
  506. * m_app.enable_camera_controls();
  507. */
  508. exports.enable_camera_controls = enable_camera_controls;
  509. function enable_camera_controls(disable_default_pivot, disable_letter_controls,
  510. disable_zoom, element, allow_element_exit,
  511. disable_gamepad_controls) {
  512. _disable_default_pivot = disable_default_pivot;
  513. _disable_letter_controls = disable_letter_controls;
  514. _disable_zoom = disable_zoom;
  515. _element = element;
  516. _allow_element_exit = allow_element_exit;
  517. _disable_gamepad_controls = disable_gamepad_controls;
  518. var obj = m_scs.get_active_camera();
  519. enable_cam_controls_resetting(obj);
  520. var use_pivot = false;
  521. var character = null;
  522. var use_hover = false;
  523. switch (m_cam.get_move_style(obj)) {
  524. case m_cam.MS_TARGET_CONTROLS:
  525. use_pivot = !disable_default_pivot;
  526. break;
  527. case m_cam.MS_EYE_CONTROLS:
  528. character = m_scs.get_first_character();
  529. break;
  530. case m_cam.MS_STATIC:
  531. return;
  532. case m_cam.MS_HOVER_CONTROLS:
  533. use_hover = true;
  534. break;
  535. }
  536. var velocity = m_cam.get_velocities(obj, _velocity_tmp);
  537. var elapsed = m_ctl.create_elapsed_sensor();
  538. if (m_phy.has_simulated_physics(obj)) {
  539. var collision = m_ctl.create_collision_sensor(obj, null, true);
  540. var collision_cb = function(obj, id, pulse) {
  541. var coll_dist = m_ctl.get_sensor_payload(obj, id, 0).coll_dist;
  542. if (coll_dist < 0) {
  543. var coll_norm = m_ctl.get_sensor_payload(obj, id, 0).coll_norm;
  544. var recover_offset = _vec3_tmp;
  545. m_vec3.scale(coll_norm, -CAM_COLL_DELTA * coll_dist, recover_offset);
  546. var trans = m_trans.get_translation(obj, _vec3_tmp2);
  547. m_vec3.add(trans, recover_offset, trans);
  548. m_trans.set_translation_v(obj, trans);
  549. }
  550. }
  551. m_ctl.create_sensor_manifold(obj, "CAMERA_COLLISION", m_ctl.CT_POSITIVE,
  552. [collision], null, collision_cb);
  553. }
  554. if (character) {
  555. // apply camera transform to character
  556. var trans = m_trans.get_translation(obj);
  557. var quat = m_trans.get_rotation(obj);
  558. var char_quat = m_util.cam_quat_to_mesh_quat(quat);
  559. trans[2] -= CHAR_HEAD_POSITION;
  560. m_phy.set_transform(character, trans, char_quat);
  561. m_cons.append_stiff_trans(obj, character, [0, 0, 0.5]);
  562. var char_dir = new Float32Array(2);
  563. var is_fly = true;
  564. m_phy.set_character_move_type(character, m_phy.CM_FLY);
  565. var move_type_cb = function() {
  566. is_fly = !is_fly;
  567. m_phy.set_character_move_type(character,
  568. is_fly ? m_phy.CM_FLY : m_phy.CM_WALK);
  569. }
  570. m_ctl.create_kb_sensor_manifold(obj, "TOGGLE_CHAR_MOVE_TYPE",
  571. m_ctl.CT_SHOT, m_ctl.KEY_C, move_type_cb);
  572. }
  573. var key_cb = function(obj, id, pulse) {
  574. if (pulse == 1) {
  575. var elapsed = m_ctl.get_sensor_value(obj, id, 0);
  576. m_cam.get_velocities(obj, velocity);
  577. switch (id) {
  578. case "FORWARD":
  579. if (character)
  580. char_dir[0] = 1;
  581. else if (use_hover) {
  582. var hover_angle = m_cam.get_camera_angles(obj, _vec2_tmp2)[1];
  583. var axis = (Math.abs(hover_angle) >= Math.PI / 4) ? m_util.AXIS_Y : m_util.AXIS_MZ;
  584. trans_hover_cam_horiz_local(obj, axis,
  585. velocity.trans * HOVER_KEY_TRANS_FACTOR * elapsed);
  586. } else if (use_pivot)
  587. trans_targ_cam_local(obj, -velocity.zoom, elapsed);
  588. else
  589. trans_eye_cam_local(obj, 0, 0, -velocity.trans * elapsed);
  590. break;
  591. case "BACKWARD":
  592. if (character)
  593. char_dir[0] = -1;
  594. else if (use_hover) {
  595. var hover_angle = m_cam.get_camera_angles(obj, _vec2_tmp2)[1];
  596. var axis = (Math.abs(hover_angle) >= Math.PI / 4) ? m_util.AXIS_MY : m_util.AXIS_Z;
  597. trans_hover_cam_horiz_local(obj, axis,
  598. velocity.trans * HOVER_KEY_TRANS_FACTOR * elapsed);
  599. } else if (use_pivot)
  600. trans_targ_cam_local(obj, velocity.zoom, elapsed);
  601. else
  602. trans_eye_cam_local(obj, 0, 0, velocity.trans * elapsed);
  603. break;
  604. case "UP":
  605. if (use_hover)
  606. zoom_hover_cam(obj, - velocity.zoom * HOVER_KEY_ZOOM_FACTOR
  607. * elapsed);
  608. else if (!character)
  609. trans_eye_cam_local(obj, 0, velocity.trans * elapsed, 0);
  610. break;
  611. case "DOWN":
  612. if (use_hover)
  613. zoom_hover_cam(obj, velocity.zoom * HOVER_KEY_ZOOM_FACTOR
  614. * elapsed);
  615. else if (!character)
  616. trans_eye_cam_local(obj, 0, -velocity.trans * elapsed, 0);
  617. break;
  618. case "LEFT":
  619. if (character)
  620. char_dir[1] = 1;
  621. else if (use_hover)
  622. trans_hover_cam_horiz_local(obj, m_util.AXIS_MX,
  623. velocity.trans * HOVER_KEY_TRANS_FACTOR * elapsed);
  624. else
  625. trans_eye_cam_local(obj, -velocity.trans * elapsed, 0, 0);
  626. break;
  627. case "RIGHT":
  628. if (character)
  629. char_dir[1] = -1;
  630. else if (use_hover)
  631. trans_hover_cam_horiz_local(obj, m_util.AXIS_X,
  632. velocity.trans * HOVER_KEY_TRANS_FACTOR * elapsed);
  633. else
  634. trans_eye_cam_local(obj, velocity.trans * elapsed, 0, 0);
  635. break;
  636. case "ROT_LEFT":
  637. if (use_pivot)
  638. m_cam.rotate_camera(obj, -velocity.rot
  639. * TARGET_EYE_KEY_ROT_FACTOR * elapsed, 0);
  640. else
  641. m_cam.rotate_camera(obj, velocity.rot * TARGET_EYE_KEY_ROT_FACTOR
  642. * elapsed, 0);
  643. break;
  644. case "ROT_RIGHT":
  645. if (use_pivot)
  646. m_cam.rotate_camera(obj, velocity.rot
  647. * TARGET_EYE_KEY_ROT_FACTOR * elapsed, 0);
  648. else
  649. m_cam.rotate_camera(obj, -velocity.rot * TARGET_EYE_KEY_ROT_FACTOR
  650. * elapsed, 0);
  651. break;
  652. case "ROT_UP":
  653. if (use_pivot)
  654. m_cam.rotate_camera(obj, 0, -velocity.rot
  655. * TARGET_EYE_KEY_ROT_FACTOR * elapsed);
  656. else
  657. m_cam.rotate_camera(obj, 0, velocity.rot
  658. * TARGET_EYE_KEY_ROT_FACTOR * elapsed);
  659. break;
  660. case "ROT_DOWN":
  661. if (use_pivot)
  662. m_cam.rotate_camera(obj, 0, velocity.rot
  663. * TARGET_EYE_KEY_ROT_FACTOR * elapsed);
  664. else
  665. m_cam.rotate_camera(obj, 0, -velocity.rot
  666. * TARGET_EYE_KEY_ROT_FACTOR * elapsed);
  667. break;
  668. default:
  669. break;
  670. }
  671. } else {
  672. switch (id) {
  673. case "FORWARD":
  674. case "BACKWARD":
  675. if (character)
  676. char_dir[0] = 0;
  677. break;
  678. case "LEFT":
  679. case "RIGHT":
  680. if (character)
  681. char_dir[1] = 0;
  682. break;
  683. }
  684. }
  685. if (character) {
  686. m_phy.set_character_move_dir(character, char_dir[0], char_dir[1]);
  687. var angles = m_cam.get_camera_angles_char(obj, _vec2_tmp);
  688. m_phy.set_character_rotation_h(character, angles[0]);
  689. m_phy.set_character_vert_move_dir_angle(character, angles[1]);
  690. }
  691. }
  692. var key_w, key_s, key_a, key_d, key_r, key_f, gmpd_btn_6, gmpd_btn_7;
  693. var lh_axis, lv_axis, rh_axis, rv_axis;
  694. if (!disable_gamepad_controls) {
  695. var gmpd_indices = m_input.check_enable_gamepad_indices();
  696. if (gmpd_indices.length)
  697. var gamepad_id = gmpd_indices[gmpd_indices.length - 1];
  698. else
  699. var gamepad_id = 0;
  700. gmpd_btn_6 = m_ctl.create_gamepad_btn_sensor(m_input.GMPD_BUTTON_6,
  701. gamepad_id);
  702. gmpd_btn_7 = m_ctl.create_gamepad_btn_sensor(m_input.GMPD_BUTTON_7,
  703. gamepad_id);
  704. lh_axis = m_ctl.create_gamepad_axis_sensor(m_input.GMPD_AXIS_0, gmpd_indices);
  705. lv_axis = m_ctl.create_gamepad_axis_sensor(m_input.GMPD_AXIS_1, gmpd_indices);
  706. rh_axis = m_ctl.create_gamepad_axis_sensor(m_input.GMPD_AXIS_2, gmpd_indices);
  707. rv_axis = m_ctl.create_gamepad_axis_sensor(m_input.GMPD_AXIS_3, gmpd_indices);
  708. } else
  709. gmpd_btn_6 = gmpd_btn_7 = lh_axis = lv_axis = rh_axis = rv_axis =
  710. m_ctl.create_custom_sensor(0);
  711. if (!disable_letter_controls) {
  712. key_w = m_ctl.create_keyboard_sensor(m_ctl.KEY_W);
  713. key_s = m_ctl.create_keyboard_sensor(m_ctl.KEY_S);
  714. key_a = m_ctl.create_keyboard_sensor(m_ctl.KEY_A);
  715. key_d = m_ctl.create_keyboard_sensor(m_ctl.KEY_D);
  716. key_r = m_ctl.create_keyboard_sensor(m_ctl.KEY_R);
  717. key_f = m_ctl.create_keyboard_sensor(m_ctl.KEY_F);
  718. } else
  719. key_w = key_s = key_a = key_d = key_r = key_f = m_ctl.create_custom_sensor(0);
  720. var key_up = m_ctl.create_keyboard_sensor(m_ctl.KEY_UP);
  721. var key_down = m_ctl.create_keyboard_sensor(m_ctl.KEY_DOWN);
  722. var key_left = m_ctl.create_keyboard_sensor(m_ctl.KEY_LEFT);
  723. var key_right = m_ctl.create_keyboard_sensor(m_ctl.KEY_RIGHT);
  724. // var key_single_logic = null;
  725. // var key_double_logic = function(s) {
  726. // return s[0] && (s[1] || s[2]);
  727. // }
  728. var key_triple_logic = function(s) {
  729. return s[0] && (s[1] || s[2] || s[3]);
  730. }
  731. var key_double_pos_logic = function(s) {
  732. return s[0] && (s[1] || s[2] > AXIS_THRESHOLD);
  733. }
  734. var key_double_neg_logic = function(s) {
  735. return s[0] && (s[1] || s[2] < -AXIS_THRESHOLD);
  736. }
  737. var key_triple_pos_logic = function(s) {
  738. return s[0] && (s[1] || s[2] || s[3] > AXIS_THRESHOLD);
  739. }
  740. var key_triple_neg_logic = function(s) {
  741. return s[0] && (s[1] || s[2] || s[3] < -AXIS_THRESHOLD);
  742. }
  743. if (!use_hover) {
  744. m_ctl.create_sensor_manifold(obj, "FORWARD", m_ctl.CT_CONTINUOUS,
  745. [elapsed, key_w, lv_axis], key_double_neg_logic, key_cb);
  746. m_ctl.create_sensor_manifold(obj, "BACKWARD", m_ctl.CT_CONTINUOUS,
  747. [elapsed, key_s, lv_axis], key_double_pos_logic, key_cb);
  748. }
  749. if (use_pivot) {
  750. m_ctl.create_sensor_manifold(obj, "ROT_UP", m_ctl.CT_CONTINUOUS,
  751. [elapsed, key_up, key_r, rv_axis], key_triple_neg_logic, key_cb);
  752. m_ctl.create_sensor_manifold(obj, "ROT_DOWN", m_ctl.CT_CONTINUOUS,
  753. [elapsed, key_down, key_f, rv_axis], key_triple_pos_logic, key_cb);
  754. m_ctl.create_sensor_manifold(obj, "ROT_LEFT", m_ctl.CT_CONTINUOUS,
  755. [elapsed, key_left, key_a, rh_axis], key_triple_neg_logic, key_cb);
  756. m_ctl.create_sensor_manifold(obj, "ROT_RIGHT", m_ctl.CT_CONTINUOUS,
  757. [elapsed, key_right, key_d, rh_axis], key_triple_pos_logic, key_cb);
  758. } else if (use_hover) {
  759. m_ctl.create_sensor_manifold(obj, "LEFT", m_ctl.CT_CONTINUOUS,
  760. [elapsed, key_left, key_a, lh_axis], key_triple_neg_logic, key_cb);
  761. m_ctl.create_sensor_manifold(obj, "RIGHT", m_ctl.CT_CONTINUOUS,
  762. [elapsed, key_right, key_d, lh_axis], key_triple_pos_logic, key_cb);
  763. m_ctl.create_sensor_manifold(obj, "FORWARD", m_ctl.CT_CONTINUOUS,
  764. [elapsed, key_up, key_w, lv_axis], key_triple_neg_logic, key_cb);
  765. m_ctl.create_sensor_manifold(obj, "BACKWARD", m_ctl.CT_CONTINUOUS,
  766. [elapsed, key_down, key_s, lv_axis], key_triple_pos_logic, key_cb);
  767. m_ctl.create_sensor_manifold(obj, "UP", m_ctl.CT_CONTINUOUS,
  768. [elapsed, key_f, rv_axis], key_double_pos_logic, key_cb);
  769. m_ctl.create_sensor_manifold(obj, "DOWN", m_ctl.CT_CONTINUOUS,
  770. [elapsed, key_r, rv_axis], key_double_neg_logic, key_cb);
  771. } else {
  772. m_ctl.create_sensor_manifold(obj, "UP", m_ctl.CT_CONTINUOUS,
  773. [elapsed, key_r, gmpd_btn_6], key_triple_logic, key_cb);
  774. m_ctl.create_sensor_manifold(obj, "DOWN", m_ctl.CT_CONTINUOUS,
  775. [elapsed, key_f, gmpd_btn_7], key_triple_logic, key_cb);
  776. m_ctl.create_sensor_manifold(obj, "LEFT", m_ctl.CT_CONTINUOUS,
  777. [elapsed, key_a, lh_axis], key_double_neg_logic, key_cb);
  778. m_ctl.create_sensor_manifold(obj, "RIGHT", m_ctl.CT_CONTINUOUS,
  779. [elapsed, key_d, lh_axis], key_double_pos_logic, key_cb);
  780. m_ctl.create_sensor_manifold(obj, "ROT_UP", m_ctl.CT_CONTINUOUS,
  781. [elapsed, key_up, rv_axis], key_double_neg_logic, key_cb);
  782. m_ctl.create_sensor_manifold(obj, "ROT_DOWN", m_ctl.CT_CONTINUOUS,
  783. [elapsed, key_down, rv_axis], key_double_pos_logic, key_cb);
  784. m_ctl.create_sensor_manifold(obj, "ROT_LEFT", m_ctl.CT_CONTINUOUS,
  785. [elapsed, key_left, rh_axis], key_double_neg_logic, key_cb);
  786. m_ctl.create_sensor_manifold(obj, "ROT_RIGHT", m_ctl.CT_CONTINUOUS,
  787. [elapsed, key_right, rh_axis], key_double_pos_logic, key_cb);
  788. }
  789. if (!disable_zoom) {
  790. // mouse wheel: camera zooming and translation speed adjusting
  791. var dest_zoom_mouse = 0;
  792. var mouse_wheel = m_ctl.create_mouse_wheel_sensor(element);
  793. // camera zooming with touch
  794. var dest_zoom_touch = 0;
  795. var touch_zoom = m_ctl.create_touch_zoom_sensor(element);
  796. var mouse_wheel_cb = function(obj, id, pulse) {
  797. if (pulse == 1) {
  798. var value = m_ctl.get_sensor_value(obj, id, 0);
  799. m_cam.get_velocities(obj, velocity);
  800. if (use_pivot || use_hover) {
  801. dest_zoom_mouse = get_dest_zoom(obj, value, velocity.zoom,
  802. dest_zoom_mouse, HOVER_MOUSE_ZOOM_FACTOR, use_pivot);
  803. } else {
  804. // translation speed adjusting
  805. var factor = value * velocity.zoom;
  806. velocity.trans *= (1 + factor);
  807. m_cam.set_velocities(obj, velocity);
  808. }
  809. }
  810. }
  811. m_ctl.create_sensor_manifold(obj, "MOUSE_WHEEL", m_ctl.CT_LEVEL,
  812. [mouse_wheel], null, mouse_wheel_cb);
  813. var touch_zoom_cb = function(obj, id, pulse, param) {
  814. if (pulse == 1) {
  815. var value = m_ctl.get_sensor_value(obj, id, 0);
  816. m_cam.get_velocities(obj, velocity);
  817. if (m_ctl.get_sensor_payload(obj, id, 0)
  818. === m_ctl.PL_MULTITOUCH_MOVE_ZOOM) {
  819. dest_zoom_touch = get_dest_zoom(obj, value, velocity.zoom
  820. * TARGET_TOUCH_ZOOM_FACTOR, dest_zoom_touch,
  821. HOVER_TOUCH_ZOOM_FACTOR, use_pivot);
  822. }
  823. }
  824. }
  825. m_ctl.create_sensor_manifold(obj, "TOUCH_ZOOM", m_ctl.CT_LEVEL,
  826. [touch_zoom], null, touch_zoom_cb);
  827. // camera zoom smoothing
  828. var zoom_interp_cb = function(obj, id, pulse) {
  829. if (use_pivot || use_hover) {
  830. if (Math.abs(dest_zoom_mouse) > EPSILON_DELTA
  831. || Math.abs(dest_zoom_touch) > EPSILON_DELTA) {
  832. var value = m_ctl.get_sensor_value(obj, id, 0);
  833. var zoom_mouse = m_util.smooth(dest_zoom_mouse, 0, value,
  834. smooth_coeff_zoom_mouse());
  835. dest_zoom_mouse -= zoom_mouse;
  836. var zoom_touch = m_util.smooth(dest_zoom_touch, 0, value,
  837. smooth_coeff_zoom_touch());
  838. dest_zoom_touch -= zoom_touch;
  839. if (use_hover) {
  840. zoom_hover_cam(obj, - (zoom_mouse + zoom_touch));
  841. } else {
  842. var res_dist = m_cam.target_get_distance(obj)
  843. + zoom_mouse + zoom_touch;
  844. // NOTE: prevent zoom overshooting.
  845. res_dist = Math.max(res_dist, EPSILON_DISTANCE);
  846. m_cam.target_set_distance(obj, res_dist);
  847. }
  848. } else {
  849. dest_zoom_mouse = 0;
  850. dest_zoom_touch = 0;
  851. }
  852. }
  853. }
  854. m_ctl.create_sensor_manifold(obj, "ZOOM_INTERPOL", m_ctl.CT_POSITIVE,
  855. [elapsed], null, zoom_interp_cb);
  856. }
  857. // camera rotation and translation with mouse
  858. var dest_x_mouse = 0;
  859. var dest_y_mouse = 0;
  860. // camera panning with mouse
  861. var dest_pan_x_mouse = 0;
  862. var dest_pan_y_mouse = 0;
  863. var mouse_cb = function(obj, id, pulse, param) {
  864. if (pulse == 1) {
  865. var value = m_ctl.get_sensor_value(obj, id, 1);
  866. m_cam.get_velocities(obj, velocity);
  867. if (!use_hover) {
  868. var left_mult = TARGET_EYE_MOUSE_ROT_MULT_PX * velocity.rot;
  869. var right_mult = TARGET_EYE_MOUSE_PAN_MULT_PX * velocity.trans;
  870. } else {
  871. var left_mult = HOVER_MOUSE_PAN_MULT_PX * velocity.trans;
  872. var right_mult = HOVER_MOUSE_ROT_MULT_PX * velocity.rot;
  873. }
  874. if (m_ctl.get_sensor_payload(obj, id, 0).which === 1) {
  875. dest_x_mouse += (param == "X") ? -value * left_mult : 0;
  876. dest_y_mouse += (param == "Y") ? -value * left_mult : 0;
  877. } else if (m_ctl.get_sensor_payload(obj, id, 0).which === 2
  878. || m_ctl.get_sensor_payload(obj, id, 0).which === 3) {
  879. dest_pan_x_mouse += (param == "X") ? -value * right_mult : 0;
  880. dest_pan_y_mouse += (param == "Y") ? -value * right_mult : 0;
  881. }
  882. }
  883. }
  884. // camera panning with gamepad
  885. var dest_pan_x_gmpd = 0;
  886. var dest_pan_y_gmpd = 0;
  887. if (!disable_gamepad_controls) {
  888. var gmpd_panning_x_pos_cb = function(obj, id, pulse) {
  889. m_cam.get_velocities(obj, velocity);
  890. dest_pan_x_gmpd += velocity.trans * TRANS_GMPD_KOEF;
  891. }
  892. var gmpd_panning_y_pos_cb = function(obj, id, pulse) {
  893. m_cam.get_velocities(obj, velocity);
  894. dest_pan_y_gmpd += velocity.zoom * ZOOM_GMPD_KOEF;
  895. }
  896. var gmpd_panning_x_neg_cb = function(obj, id, pulse) {
  897. m_cam.get_velocities(obj, velocity);
  898. dest_pan_x_gmpd -= velocity.trans * TRANS_GMPD_KOEF;
  899. }
  900. var gmpd_panning_y_neg_cb = function(obj, id, pulse) {
  901. m_cam.get_velocities(obj, velocity);
  902. dest_pan_y_gmpd -= velocity.zoom * ZOOM_GMPD_KOEF;
  903. }
  904. if (use_pivot) {
  905. m_ctl.create_sensor_manifold(obj, "GMPD_PAN_Y_POS", m_ctl.CT_CONTINUOUS,
  906. [gmpd_btn_6], null, gmpd_panning_y_pos_cb);
  907. m_ctl.create_sensor_manifold(obj, "GMPD_PAN_Y_NEG", m_ctl.CT_CONTINUOUS,
  908. [gmpd_btn_7], null, gmpd_panning_y_neg_cb);
  909. m_ctl.create_sensor_manifold(obj, "GMPD_PAN_X_POS", m_ctl.CT_CONTINUOUS,
  910. [lh_axis], function(s) {return s[0] < -AXIS_THRESHOLD}, gmpd_panning_x_neg_cb);
  911. m_ctl.create_sensor_manifold(obj, "GMPD_PAN_X_NEG", m_ctl.CT_CONTINUOUS,
  912. [lh_axis], function(s) {return s[0] > AXIS_THRESHOLD}, gmpd_panning_x_pos_cb);
  913. } else if (use_hover) {
  914. m_ctl.create_sensor_manifold(obj, "GMPD_PAN_X_POS", m_ctl.CT_CONTINUOUS,
  915. [rh_axis], function(s) {return s[0] < -AXIS_THRESHOLD}, gmpd_panning_x_neg_cb);
  916. m_ctl.create_sensor_manifold(obj, "GMPD_PAN_X_NEG", m_ctl.CT_CONTINUOUS,
  917. [rh_axis], function(s) {return s[0] > AXIS_THRESHOLD}, gmpd_panning_x_pos_cb);
  918. }
  919. }
  920. if (allow_element_exit) {
  921. var mouse_move_x = m_ctl.create_mouse_move_sensor("X", window);
  922. var mouse_move_y = m_ctl.create_mouse_move_sensor("Y", window);
  923. var mouse_down_c = m_ctl.create_mouse_click_sensor(element);
  924. var mouse_down_w = m_ctl.create_mouse_click_sensor(window);
  925. var element_exit_state = false;
  926. var element_exit_logic = function(s) {
  927. if (s[2])
  928. element_exit_state = true;
  929. else if (!s[0])
  930. element_exit_state = false;
  931. return element_exit_state && s[0];
  932. }
  933. m_ctl.create_sensor_manifold(obj, "MOUSE_X", m_ctl.CT_POSITIVE,
  934. [mouse_down_w, mouse_move_x, mouse_down_c], element_exit_logic, mouse_cb, "X");
  935. m_ctl.create_sensor_manifold(obj, "MOUSE_Y", m_ctl.CT_POSITIVE,
  936. [mouse_down_w, mouse_move_y, mouse_down_c], element_exit_logic, mouse_cb, "Y");
  937. var device = m_input.get_device_by_type_element(m_input.DEVICE_MOUSE, window);
  938. m_input.switch_prevent_default(device, false);
  939. } else {
  940. var mouse_move_x = m_ctl.create_mouse_move_sensor("X", element);
  941. var mouse_move_y = m_ctl.create_mouse_move_sensor("Y", element);
  942. var mouse_down = m_ctl.create_mouse_click_sensor(element);
  943. m_ctl.create_sensor_manifold(obj, "MOUSE_X", m_ctl.CT_POSITIVE,
  944. [mouse_down, mouse_move_x], null, mouse_cb, "X");
  945. m_ctl.create_sensor_manifold(obj, "MOUSE_Y", m_ctl.CT_POSITIVE,
  946. [mouse_down, mouse_move_y], null, mouse_cb, "Y");
  947. }
  948. // camera rotation and translation with touch
  949. var dest_x_touch = 0;
  950. var dest_y_touch = 0;
  951. // camera panning with touch
  952. var dest_pan_x_touch = 0;
  953. var dest_pan_y_touch = 0;
  954. var touch_move_x = m_ctl.create_touch_move_sensor("X", element);
  955. var touch_move_y = m_ctl.create_touch_move_sensor("Y", element);
  956. var touch_cb = function(obj, id, pulse, param) {
  957. if (pulse == 1) {
  958. m_cam.get_velocities(obj, velocity);
  959. if (use_hover)
  960. var r_mult = HOVER_TOUCH_PAN_MULT_PX * velocity.trans;
  961. else
  962. var r_mult = TARGET_EYE_TOUCH_ROT_MULT_PX * velocity.rot;
  963. var value = m_ctl.get_sensor_value(obj, id, 0);
  964. if (m_ctl.get_sensor_payload(obj, id, 0).gesture
  965. === m_ctl.PL_SINGLE_TOUCH_MOVE) {
  966. dest_x_touch += (param == "X") ? -value * r_mult : 0;
  967. dest_y_touch += (param == "Y") ? -value * r_mult : 0;
  968. } else if (m_ctl.get_sensor_payload(obj, id, 0).gesture
  969. === m_ctl.PL_MULTITOUCH_MOVE_PAN) {
  970. if (!use_hover) {
  971. var pan_mult = TARGET_EYE_TOUCH_PAN_MULT_PX * velocity.trans;
  972. } else {
  973. var pan_mult = HOVER_TOUCH_ROT_MULT_PX * velocity.rot;
  974. }
  975. dest_pan_x_touch += (param == "X") ? -value * pan_mult : 0;
  976. dest_pan_y_touch += (param == "Y") ? -value * pan_mult : 0;
  977. }
  978. }
  979. }
  980. m_ctl.create_sensor_manifold(obj, "TOUCH_X", m_ctl.CT_POSITIVE,
  981. [touch_move_x], null, touch_cb, "X");
  982. m_ctl.create_sensor_manifold(obj, "TOUCH_Y", m_ctl.CT_POSITIVE,
  983. [touch_move_y], null, touch_cb, "Y");
  984. // camera rotation and translation smoothing
  985. var rot_trans_interp_cb = function(obj, id, pulse) {
  986. if ( Math.abs(dest_x_mouse) > EPSILON_DELTA ||
  987. Math.abs(dest_y_mouse) > EPSILON_DELTA ||
  988. Math.abs(dest_x_touch) > EPSILON_DELTA ||
  989. Math.abs(dest_y_touch) > EPSILON_DELTA ||
  990. Math.abs(dest_pan_x_mouse) > EPSILON_DELTA ||
  991. Math.abs(dest_pan_y_mouse) > EPSILON_DELTA ||
  992. Math.abs(dest_pan_x_touch) > EPSILON_DELTA ||
  993. Math.abs(dest_pan_y_touch) > EPSILON_DELTA ||
  994. Math.abs(dest_pan_x_gmpd) > EPSILON_DELTA ||
  995. Math.abs(dest_pan_y_gmpd) > EPSILON_DELTA) {
  996. var value = m_ctl.get_sensor_value(obj, id, 0);
  997. var x_mouse = m_util.smooth(dest_x_mouse, 0, value,
  998. smooth_coeff_rot_trans_mouse());
  999. var y_mouse = m_util.smooth(dest_y_mouse, 0, value,
  1000. smooth_coeff_rot_trans_mouse());
  1001. dest_x_mouse -= x_mouse;
  1002. dest_y_mouse -= y_mouse;
  1003. var x_touch = m_util.smooth(dest_x_touch, 0, value,
  1004. smooth_coeff_rot_trans_touch());
  1005. var y_touch = m_util.smooth(dest_y_touch, 0, value,
  1006. smooth_coeff_rot_trans_touch());
  1007. dest_x_touch -= x_touch;
  1008. dest_y_touch -= y_touch;
  1009. var trans_x_mouse = m_util.smooth(dest_pan_x_mouse, 0,
  1010. value, smooth_coeff_rot_trans_mouse());
  1011. var trans_y_mouse = m_util.smooth(dest_pan_y_mouse, 0,
  1012. value, smooth_coeff_rot_trans_mouse());
  1013. dest_pan_x_mouse -= trans_x_mouse;
  1014. dest_pan_y_mouse -= trans_y_mouse;
  1015. var trans_x_touch = m_util.smooth(dest_pan_x_touch, 0,
  1016. value, smooth_coeff_rot_trans_touch());
  1017. var trans_y_touch = m_util.smooth(dest_pan_y_touch, 0,
  1018. value, smooth_coeff_rot_trans_touch());
  1019. dest_pan_x_touch -= trans_x_touch;
  1020. dest_pan_y_touch -= trans_y_touch;
  1021. var trans_x_gmpd = m_util.smooth(dest_pan_x_gmpd, 0,
  1022. value, smooth_coeff_rot_trans_mouse());
  1023. var trans_y_gmpd = m_util.smooth(dest_pan_y_gmpd, 0,
  1024. value, smooth_coeff_rot_trans_mouse());
  1025. dest_pan_x_gmpd -= trans_x_gmpd;
  1026. dest_pan_y_gmpd -= trans_y_gmpd;
  1027. if (use_pivot) {
  1028. m_cam.rotate_camera(obj, x_mouse + x_touch,
  1029. y_mouse + y_touch);
  1030. var dist = m_cam.target_get_distance(obj);
  1031. m_cam.target_pan_pivot(obj,
  1032. dist * (trans_x_mouse + trans_x_touch + trans_x_gmpd),
  1033. dist * (trans_y_mouse + trans_y_touch + trans_y_gmpd));
  1034. } else if (use_hover) {
  1035. if (x_mouse + x_touch) {
  1036. trans_hover_cam_horiz_local(obj, m_util.AXIS_X,
  1037. (x_mouse + x_touch)
  1038. * HOVER_MOUSE_TOUCH_TRANS_FACTOR);
  1039. }
  1040. if (y_mouse + y_touch) {
  1041. var hover_angle = m_cam.get_camera_angles(obj, _vec2_tmp2)[1];
  1042. var axis = (Math.abs(hover_angle) > Math.PI / 4) ? m_util.AXIS_MY : m_util.AXIS_Z;
  1043. trans_hover_cam_horiz_local(obj, axis, (y_mouse + y_touch)
  1044. * HOVER_MOUSE_TOUCH_TRANS_FACTOR);
  1045. }
  1046. m_cam.rotate_camera(obj, trans_x_mouse + trans_x_touch + trans_x_gmpd, 0);
  1047. } else {
  1048. m_cam.rotate_camera(obj, (x_mouse + x_touch) * EYE_ROTATION_DECREMENT,
  1049. (y_mouse + y_touch) * EYE_ROTATION_DECREMENT);
  1050. if (character) {
  1051. var angles = m_cam.get_camera_angles_char(obj, _vec2_tmp);
  1052. m_phy.set_character_rotation_h(character, angles[0]);
  1053. m_phy.set_character_vert_move_dir_angle(character, angles[1]);
  1054. }
  1055. }
  1056. }
  1057. }
  1058. m_ctl.create_sensor_manifold(obj, "ROT_TRANS_INTERPOL", m_ctl.CT_POSITIVE,
  1059. [elapsed], null, rot_trans_interp_cb);
  1060. m_ctl.create_kb_sensor_manifold(obj, "DEC_STEREO_DIST", m_ctl.CT_SHOT,
  1061. m_ctl.KEY_LEFT_SQ_BRACKET, function(obj, id, pulse) {
  1062. var dist = m_cam.get_stereo_distance(obj);
  1063. m_cam.set_stereo_distance(obj, 0.9 * dist);
  1064. });
  1065. m_ctl.create_kb_sensor_manifold(obj, "INC_STEREO_DIST", m_ctl.CT_SHOT,
  1066. m_ctl.KEY_RIGHT_SQ_BRACKET, function(obj, id, pulse) {
  1067. var dist = m_cam.get_stereo_distance(obj);
  1068. m_cam.set_stereo_distance(obj, 1.1 * dist);
  1069. });
  1070. }
  1071. /**
  1072. * Register sensors for resetting the camera controls if the camera move style
  1073. * was changed.
  1074. */
  1075. function enable_cam_controls_resetting(cam) {
  1076. var prev_ms = m_cam.get_move_style(cam);
  1077. var move_style_cb = function() {
  1078. var curr_ms = m_cam.get_move_style(cam);
  1079. var is_changed = curr_ms != prev_ms;
  1080. prev_ms = curr_ms;
  1081. return is_changed;
  1082. }
  1083. var cb_sensor = m_ctl.create_callback_sensor(move_style_cb);
  1084. function reset_controls_cb(cam, id, pulse) {
  1085. disable_camera_controls();
  1086. enable_camera_controls(_disable_default_pivot, _disable_letter_controls,
  1087. _disable_zoom, _element, _allow_element_exit,
  1088. _disable_gamepad_controls);
  1089. }
  1090. m_ctl.create_sensor_manifold(cam, "CHANGE_MOVE_STYLE", m_ctl.CT_POSITIVE,
  1091. [cb_sensor], null, reset_controls_cb);
  1092. }
  1093. /**
  1094. * Disable controls for the active camera.
  1095. * @method module:app.disable_camera_controls
  1096. * @example
  1097. * var m_app = require("app");
  1098. * m_app.disable_camera_controls();
  1099. */
  1100. exports.disable_camera_controls = disable_camera_controls;
  1101. function disable_camera_controls() {
  1102. var cam = m_scs.get_active_camera();
  1103. if (m_ctl.check_sensor_manifold(cam, "TOGGLE_CHAR_MOVE_TYPE")
  1104. && m_scs.get_first_character())
  1105. m_cons.remove(cam);
  1106. var cam_std_manifolds = ["FORWARD", "BACKWARD", "ROT_UP", "ROT_DOWN",
  1107. "ROT_LEFT", "ROT_RIGHT", "UP", "DOWN", "LEFT", "RIGHT",
  1108. "MOUSE_WHEEL", "TOUCH_ZOOM", "ZOOM_INTERPOL", "MOUSE_X", "MOUSE_Y",
  1109. "TOUCH_X", "TOUCH_Y", "ROT_TRANS_INTERPOL", "CHANGE_MOVE_STYLE",
  1110. "TOGGLE_CHAR_MOVE_TYPE"];
  1111. for (var i = 0; i < cam_std_manifolds.length; i++)
  1112. m_ctl.remove_sensor_manifold(cam, cam_std_manifolds[i]);
  1113. }
  1114. /**
  1115. * Assign some controls to the non-camera object.
  1116. * @param {Object3D} obj Object 3D
  1117. * @param {HTMLElement} [element=Canvas container element] HTML element
  1118. */
  1119. exports.enable_object_controls = function(obj, element) {
  1120. var trans_speed = 1;
  1121. var is_vehicle = m_phy.is_vehicle_chassis(obj) ||
  1122. m_phy.is_vehicle_hull(obj);
  1123. var key_cb = function(obj, id, pulse) {
  1124. if (pulse == 1) {
  1125. var elapsed = m_ctl.get_sensor_value(obj, id, 0);
  1126. switch (id) {
  1127. case "FORWARD":
  1128. if (is_vehicle)
  1129. m_phy.vehicle_throttle(obj, 1);
  1130. else
  1131. m_trans.move_local(obj, 0, -trans_speed * elapsed, 0);
  1132. break;
  1133. case "BACKWARD":
  1134. if (is_vehicle)
  1135. m_phy.vehicle_throttle(obj, -1);
  1136. else
  1137. m_trans.move_local(obj, 0, trans_speed * elapsed, 0);
  1138. break;
  1139. case "LEFT":
  1140. if (is_vehicle)
  1141. m_phy.vehicle_steer(obj, -1);
  1142. else
  1143. m_trans.move_local(obj, trans_speed * elapsed, 0, 0);
  1144. break;
  1145. case "RIGHT":
  1146. if (is_vehicle)
  1147. m_phy.vehicle_steer(obj, 1);
  1148. else
  1149. m_trans.move_local(obj, -trans_speed * elapsed, 0, 0);
  1150. break;
  1151. default:
  1152. break;
  1153. }
  1154. } else {
  1155. switch (id) {
  1156. case "FORWARD":
  1157. case "BACKWARD":
  1158. if (is_vehicle)
  1159. m_phy.vehicle_throttle(obj, 0);
  1160. break;
  1161. case "LEFT":
  1162. case "RIGHT":
  1163. if (is_vehicle)
  1164. m_phy.vehicle_steer(obj, 0);
  1165. break;
  1166. default:
  1167. break;
  1168. }
  1169. }
  1170. }
  1171. var elapsed = m_ctl.create_elapsed_sensor();
  1172. var key_w = m_ctl.create_keyboard_sensor(m_ctl.KEY_W);
  1173. var key_s = m_ctl.create_keyboard_sensor(m_ctl.KEY_S);
  1174. var key_a = m_ctl.create_keyboard_sensor(m_ctl.KEY_A);
  1175. var key_d = m_ctl.create_keyboard_sensor(m_ctl.KEY_D);
  1176. var key_up = m_ctl.create_keyboard_sensor(m_ctl.KEY_UP);
  1177. var key_down = m_ctl.create_keyboard_sensor(m_ctl.KEY_DOWN);
  1178. var key_left = m_ctl.create_keyboard_sensor(m_ctl.KEY_LEFT);
  1179. var key_right = m_ctl.create_keyboard_sensor(m_ctl.KEY_RIGHT);
  1180. var key_logic = function(s) {
  1181. return s[0] && (s[1] || s[2]);
  1182. }
  1183. m_ctl.create_sensor_manifold(obj, "FORWARD", m_ctl.CT_CONTINUOUS,
  1184. [elapsed, key_w, key_up], key_logic, key_cb);
  1185. m_ctl.create_sensor_manifold(obj, "BACKWARD", m_ctl.CT_CONTINUOUS,
  1186. [elapsed, key_s, key_down], key_logic, key_cb);
  1187. m_ctl.create_sensor_manifold(obj, "LEFT", m_ctl.CT_CONTINUOUS,
  1188. [elapsed, key_a, key_left], key_logic, key_cb);
  1189. m_ctl.create_sensor_manifold(obj, "RIGHT", m_ctl.CT_CONTINUOUS,
  1190. [elapsed, key_d, key_right], key_logic, key_cb);
  1191. }
  1192. /**
  1193. * Remove controls from the non-camera object.
  1194. * @param {Object3D} obj Object.
  1195. */
  1196. exports.disable_object_controls = function(obj) {
  1197. var obj_std_manifolds = ["FORWARD", "BACKWARD", "LEFT", "RIGHT"];
  1198. for (var i = 0; i < obj_std_manifolds.length; i++)
  1199. m_ctl.remove_sensor_manifold(obj, obj_std_manifolds[i]);
  1200. }
  1201. /**
  1202. * Enable debug controls:
  1203. * <ul>
  1204. * <li>K - make camera debug shot
  1205. * <li>L - make light debug shot
  1206. * <li>M - flashback messages
  1207. * </ul>
  1208. */
  1209. exports.enable_debug_controls = function() {
  1210. m_ctl.create_kb_sensor_manifold(null, "CAMERA_SHOT", m_ctl.CT_SHOT,
  1211. m_ctl.KEY_K, function() {m_dbg.make_camera_frustum_shot();});
  1212. m_ctl.create_kb_sensor_manifold(null, "LIGHT_SHOT", m_ctl.CT_SHOT,
  1213. m_ctl.KEY_L, function() {m_dbg.make_light_frustum_shot();});
  1214. m_ctl.create_kb_sensor_manifold(null, "TELEMETRY", m_ctl.CT_SHOT,
  1215. m_ctl.KEY_T, function() {m_dbg.plot_telemetry();});
  1216. }
  1217. /**
  1218. * Request fullscreen mode.
  1219. * Security issues: execute by user event.
  1220. * @method module:app.request_fullscreen
  1221. * @param {HTMLElement} elem Element
  1222. * @param {FullscreenEnabledCallback} enabled_cb Enabled callback
  1223. * @param {FullscreenDisabledCallback} disabled_cb Disabled callback
  1224. * @deprecated Use {@link module:screen.request_fullscreen} instead
  1225. */
  1226. exports.request_fullscreen = function(elem, enabled_cb, disabled_cb) {
  1227. m_print.error_deprecated("request_fullscreen", "screen.request_fullscreen");
  1228. m_screen.request_fullscreen(elem, enabled_cb, disabled_cb);
  1229. };
  1230. /**
  1231. * Exit fullscreen mode.
  1232. * @method module:app.exit_fullscreen
  1233. * @deprecated Use {@link module:screen.exit_fullscreen} instead
  1234. */
  1235. exports.exit_fullscreen = function() {
  1236. m_print.error_deprecated("exit_fullscreen", "screen.exit_fullscreen");
  1237. m_screen.exit_fullscreen();
  1238. };
  1239. /**
  1240. * Check whether fullscreen mode is available.
  1241. * @method module:app.check_fullscreen
  1242. * @returns {boolean} Result of the check.
  1243. * @deprecated Use {@link module:screen.check_fullscreen} instead
  1244. */
  1245. exports.check_fullscreen = function() {
  1246. m_print.error_deprecated("check_fullscreen", "screen.check_fullscreen");
  1247. return m_screen.check_fullscreen();
  1248. };
  1249. exports.report_app_error = report_app_error;
  1250. /**
  1251. * Report an application error.
  1252. * Creates standard HTML elements with error info and inserts them into the page body.
  1253. * @method module:app.report_app_error
  1254. * @param {string} text_message Message to place on upper element.
  1255. * @param {string} link_message Message to place on bottom element.
  1256. * @param {string} link Link to place on bottom element.
  1257. * @param {string[]} purge_elements Array of element IDs to destroy just before the error
  1258. * elements are inserted.
  1259. */
  1260. function report_app_error(text_message, link_message, link, purge_elements) {
  1261. var elem = document.createElement("div");
  1262. var top_elem = document.createElement("div");
  1263. var bottom_elem = document.createElement("div");
  1264. if (purge_elements) {
  1265. for (var i = 0; i < purge_elements.length; i++) {
  1266. var purge_elem = document.getElementById(purge_elements[i]);
  1267. if (purge_elem)
  1268. purge_elem.parentNode.removeChild(purge_elem);
  1269. }
  1270. }
  1271. elem.style.cssText = "z-index:10;width:100%;height:auto;position:absolute;top:50%;margin-top:150px;text-align:center;";
  1272. top_elem.style.cssText = "color:#fff;font-size:24px;";
  1273. bottom_elem.style.cssText = "color:#fff;font-size:20px;";
  1274. top_elem.innerHTML = text_message;
  1275. bottom_elem.innerHTML = link_message + ' ' + '<a style="color:#fff;font-size:20px;width:100%;" href="' + link + '">'+link.replace("https://www.", "")+'</a>';
  1276. elem.appendChild(top_elem);
  1277. elem.appendChild(bottom_elem);
  1278. document.body.appendChild(elem);
  1279. }
  1280. /**
  1281. * Retrieve parameters of the page's URL.
  1282. * @param {boolean} [allow_param_array=false] Create arrays of parameters if they share the same name.
  1283. * @returns {Object|Null} URL parameters in a key-value format.
  1284. */
  1285. exports.get_url_params = function(allow_param_array) {
  1286. allow_param_array = !!allow_param_array;
  1287. var url = decodeURIComponent(location.href.toString());
  1288. if (url.indexOf("?") == -1)
  1289. return null;
  1290. var params = url.split("?")[1].split("&");
  1291. var out = {};
  1292. for (var i = 0; i < params.length; i++) {
  1293. var param = params[i].split("=");
  1294. var prop_name = param[0];
  1295. if (param.length > 1) {
  1296. var prop_val = param[1];
  1297. if (allow_param_array) {
  1298. if (prop_name in out)
  1299. out[prop_name].push(prop_val);
  1300. else
  1301. out[prop_name] = [prop_val];
  1302. } else
  1303. out[prop_name] = prop_val;
  1304. } else {
  1305. if (allow_param_array) {
  1306. if (!(prop_name in out))
  1307. out[prop_name] = [];
  1308. } else
  1309. out[prop_name] = '';
  1310. }
  1311. }
  1312. return out;
  1313. }
  1314. /**
  1315. * Animate css-property value.
  1316. * @method module:app.css_animate
  1317. * @param {HTMLElement} elem HTML-element.
  1318. * @param {string} prop Animated css-property.
  1319. * @param {number} from Value from.
  1320. * @param {number} to Value to.
  1321. * @param {number} timeout Time for animation.
  1322. * @param {string} [opt_prefix] Prefix of css-property (" scale(", "%" and etc).
  1323. * @param {string} [opt_suffix] Suffix of css-property (" px", "%" and etc).
  1324. * @param {GenericCallback} [opt_callback] Finish callback function.
  1325. */
  1326. exports.css_animate = function(elem, prop, from, to, timeout, opt_prefix, opt_suffix, opt_callback) {
  1327. if (!elem || !prop || !isFinite(from) || !isFinite(to) || !isFinite(timeout))
  1328. return;
  1329. opt_prefix = opt_prefix || "";
  1330. opt_suffix = opt_suffix || "";
  1331. opt_callback = opt_callback || function() {};
  1332. var elem_style = elem.style;
  1333. var vendor_prop = prop.charAt(0).toUpperCase() + prop.slice(1);
  1334. if (elem_style[prop] != undefined) {
  1335. } else if (elem_style["webkit" + vendor_prop] != undefined) {
  1336. prop = "webkit" + vendor_prop;
  1337. } else if (elem_style["ms" + vendor_prop] != undefined) {
  1338. prop = "ms" + vendor_prop;
  1339. } else if (elem_style["moz" + vendor_prop] != undefined) {
  1340. prop = "moz" + vendor_prop;
  1341. } else
  1342. return;
  1343. function css_anim_cb(val) {
  1344. if (!elem)
  1345. return;
  1346. elem_style[prop] = opt_prefix + val + opt_suffix;
  1347. if (from > to && val <= to || from < to && val >= to) {
  1348. elem_style[prop] = opt_prefix + to + opt_suffix;
  1349. opt_callback();
  1350. }
  1351. }
  1352. animate(elem, from, to, timeout, css_anim_cb);
  1353. }
  1354. /**
  1355. * Animate html tag attribute.
  1356. * @method module:app.attr_animate
  1357. * @param {HTMLElement} elem HTML-element.
  1358. * @param {string} attr_name Animated attribute name.
  1359. * @param {number} from Value from.
  1360. * @param {number} to Value to.
  1361. * @param {number} timeout Time for animation.
  1362. * @param {GenericCallback} [opt_callback] Finish callback function.
  1363. */
  1364. exports.attr_animate = function(elem, attr_name, from, to, timeout, opt_callback) {
  1365. if (!elem || !attr_name || !isFinite(from) || !isFinite(to) || !isFinite(timeout))
  1366. return;
  1367. opt_callback = opt_callback || function() {};
  1368. function attr_anim_cb(val) {
  1369. if (!elem)
  1370. return;
  1371. if (val >= 0)
  1372. elem.setAttribute(attr_name, val);
  1373. if (from > to && val <= to || from < to && val >= to) {
  1374. elem.setAttribute(attr_name, to);
  1375. opt_callback();
  1376. }
  1377. }
  1378. animate(elem, from, to, timeout, attr_anim_cb);
  1379. }
  1380. // Animate value from "from" to "to" for "timeout" mseconds.
  1381. function animate(elem, from, to, timeout, anim_cb) {
  1382. var start = performance.now();
  1383. function cb() {
  1384. var elapsed_total = performance.now() - start;
  1385. var value = from + elapsed_total * (to - from) / timeout;
  1386. anim_cb(value);
  1387. if (elapsed_total >= timeout || !elem.parentNode)
  1388. m_main.remove_loop_cb(cb);
  1389. }
  1390. m_main.append_loop_cb(cb);
  1391. }
  1392. /**
  1393. * Animation finish callback.
  1394. * @callback AnimFinishCallback
  1395. */
  1396. /**
  1397. * Queue object params.
  1398. * @typedef {Object} QueueObject
  1399. * @property {string} type Animation type.
  1400. * @property {HTMLElement} elem Animated html element.
  1401. * @property {string} prop Animated property.
  1402. * @property {number} from Initial property value.
  1403. * @property {number} to Target property value.
  1404. * @property {number} duration Animation duration in ms.
  1405. * @property {string} [opt_prefix=''] Prefix for the css property.
  1406. * @property {string} [opt_suffix=''] Prefix for the css property.
  1407. * @property {AnimFinishCallback} [cb=function(){}] Animation finish callback.
  1408. */
  1409. /**
  1410. * Animate queue of the html elements.
  1411. * @method module:app.queue_animate
  1412. * @param {QueueObject[]} queue Array of the queue objects.
  1413. */
  1414. exports.queue_animate = function(queue) {
  1415. if (!queue.length)
  1416. return;
  1417. var queue_obj = queue.shift();
  1418. var elem = queue_obj.elem;
  1419. var prop = queue_obj.prop;
  1420. var from = queue_obj.from;
  1421. var to = queue_obj.to;
  1422. var duration = queue_obj.duration;
  1423. var prefix = queue_obj.opt_prefix;
  1424. var suffix = queue_obj.opt_suffix;
  1425. var cb = function() {
  1426. if (queue_obj.cb)
  1427. queue_obj.cb();
  1428. exports.queue_animate(queue);
  1429. }
  1430. if (queue_obj.type == "css")
  1431. exports.css_animate(elem, prop, from, to, duration, prefix, suffix, cb);
  1432. else if (queue_obj.type == "attr")
  1433. exports.attr_animate(elem, prop, from, to, duration, cb);
  1434. }
  1435. function smooth_coeff_zoom_mouse() {
  1436. return CAM_SMOOTH_ZOOM_MOUSE * _smooth_factor;
  1437. }
  1438. function smooth_coeff_zoom_touch() {
  1439. return CAM_SMOOTH_ZOOM_TOUCH * _smooth_factor;
  1440. }
  1441. function smooth_coeff_rot_trans_mouse() {
  1442. return CAM_SMOOTH_ROT_TRANS_MOUSE * _smooth_factor;
  1443. }
  1444. function smooth_coeff_rot_trans_touch() {
  1445. return CAM_SMOOTH_ROT_TRANS_TOUCH * _smooth_factor;
  1446. }
  1447. /**
  1448. * Set smooth factor for camera rotation and zoom.
  1449. * @method module:app.set_camera_smooth_factor
  1450. * @param {number} value New smooth factor
  1451. */
  1452. exports.set_camera_smooth_factor = function(value) {
  1453. _smooth_factor = value;
  1454. }
  1455. /**
  1456. * Get smooth factor for camera rotation and zoom.
  1457. * @method module:app.get_camera_smooth_factor
  1458. * @returns {number} Smooth factor
  1459. */
  1460. exports.get_camera_smooth_factor = function() {
  1461. return _smooth_factor;
  1462. }
  1463. }
  1464. var app_factory = register("app", App);
  1465. export default app_factory;