Source: extern/lights.js

  1. import register from "../util/register.js";
  2. import * as m_lights from "../intern/lights.js";
  3. import m_obj_fact from "../intern/objects.js";
  4. import m_obj_util_fact from "../intern/obj_util.js";
  5. import m_print_fact from "../intern/print.js";
  6. import m_scenes_fact from "../intern/scenes.js";
  7. import m_trans_fact from "../intern/transform.js";
  8. import * as m_tsr from "../intern/tsr.js";
  9. import * as m_util from "../intern/util.js";
  10. import * as m_vec3 from "../libs/gl_matrix/vec3.js";
  11. /**
  12. * API to control light sources.
  13. * @module lights
  14. * @local LightParams
  15. * @see https://www.blend4web.com/doc/en/lighting.html#lighting-with-light-sources
  16. */
  17. function Lights(ns, exports) {
  18. // TODO: consider use of standard translation/rotation functions from transform module
  19. var m_obj = m_obj_fact(ns);
  20. var m_obj_util = m_obj_util_fact(ns);
  21. var m_print = m_print_fact(ns);
  22. var m_scenes = m_scenes_fact(ns);
  23. var m_trans = m_trans_fact(ns);
  24. var _sun_pos = new Float32Array(3);
  25. var _date = {};
  26. var _max_sun_angle = 60;
  27. /**
  28. * @typedef {Object} LightParams
  29. * @property {string} light_type Light type
  30. * @property {number} light_energy Light energy
  31. * @property {RGB} light_color Light color
  32. * @property {number} light_spot_blend Blend parameter of SPOT light
  33. * @property {number} light_spot_size Size parameter of SPOT light
  34. * @property {number} light_distance Light falloff distance for POINT and SPOT
  35. * lights
  36. * @cc_externs light_type light_energy light_color
  37. * @cc_externs light_spot_blend light_spot_size light_distance
  38. */
  39. /**
  40. * Get lamp objects.
  41. * If lamps_type is defined, creates a new array
  42. * @method module:lights.get_lamps
  43. * @param {string} [lamps_type] Lamps type ("POINT", "SPOT", "SUN", "HEMI")
  44. * @returns {Object3D[]} Array with lamp objects.
  45. */
  46. exports.get_lamps = function(lamps_type) {
  47. var scene = m_scenes.get_active();
  48. var lamps = m_obj.get_scene_objs(scene, "LAMP", m_obj.DATA_ID_ALL);
  49. if (!lamps_type)
  50. return lamps;
  51. var rslt = [];
  52. for (var i = 0; i < lamps.length; i++) {
  53. var lamp = lamps[i];
  54. if (lamp.light.type === lamps_type)
  55. rslt.push(lamp);
  56. }
  57. return rslt;
  58. }
  59. exports.get_sun_params = get_sun_params;
  60. /**
  61. * Get the sun parameters.
  62. * @method module:lights.get_sun_params
  63. * @returns {SunParams} Sun params object
  64. * @cc_externs hor_position vert_position
  65. */
  66. function get_sun_params() {
  67. var scene = m_scenes.get_active();
  68. var lamps = m_obj.get_scene_objs(scene, "LAMP", m_obj.DATA_ID_ALL);
  69. var sun = null;
  70. for (var i = 0; i < lamps.length; i++) {
  71. var lamp = lamps[i];
  72. var light = lamp.light;
  73. if (light.type == "SUN") {
  74. sun = lamp;
  75. break
  76. }
  77. }
  78. if (sun) {
  79. var cur_dir = sun.light.direction;
  80. // sun azimuth
  81. var angle_hor = m_util.rad_to_deg(Math.atan2(-cur_dir[1], cur_dir[0])) + 90;
  82. if (angle_hor > 180)
  83. angle_hor -= 360;
  84. // sun altitude
  85. var angle_vert = m_util.rad_to_deg(Math.atan2(
  86. cur_dir[2],
  87. Math.sqrt(cur_dir[0]*cur_dir[0] + cur_dir[1]*cur_dir[1])
  88. ));
  89. var sun_params = {};
  90. sun_params.hor_position = angle_hor;
  91. sun_params.vert_position = angle_vert;
  92. return sun_params;
  93. } else
  94. return null;
  95. }
  96. exports.set_sun_params = set_sun_params;
  97. /**
  98. * Set the sun parameters.
  99. * @method module:lights.set_sun_params
  100. * @param {SunParams} sun_params sun parameters
  101. */
  102. function set_sun_params(sun_params) {
  103. var scene = m_scenes.get_active();
  104. var lamps = m_obj.get_scene_objs(scene, "LAMP", m_obj.DATA_ID_ALL);
  105. // Index of lamp(sun) on the scene
  106. for (var i = 0; i < lamps.length; i++) {
  107. var lamp = lamps[i];
  108. var light = lamp.light;
  109. if (light.type == "SUN") {
  110. var sun = lamp;
  111. break;
  112. }
  113. }
  114. if (!sun) {
  115. m_print.error("There is no sun on the scene");
  116. return;
  117. }
  118. if (typeof sun_params.hor_position == "number" &&
  119. typeof sun_params.vert_position == "number") {
  120. // convert to radians
  121. var angle_hor = m_util.deg_to_rad(180 - sun_params.hor_position);
  122. var angle_vert = m_util.deg_to_rad(90 - sun_params.vert_position);
  123. var sun_render = sun.render;
  124. // rotate sun
  125. m_trans.set_rotation_euler(sun, [angle_vert, 0, angle_hor]);
  126. var dir = new Float32Array(3);
  127. var sun_quat = m_tsr.get_quat_view(sun_render.world_tsr);
  128. m_util.quat_to_dir(sun_quat, m_util.AXIS_Z, dir);
  129. var trans = m_tsr.get_trans_view(sun_render.world_tsr);
  130. var dist_to_center = m_vec3.length(trans);
  131. m_vec3.copy(dir, _sun_pos);
  132. m_vec3.scale(_sun_pos, dist_to_center, _sun_pos);
  133. // translate sun
  134. m_trans.set_translation(sun, _sun_pos);
  135. m_trans.update_transform(sun);
  136. var sun_light = sun.light;
  137. if (sun_light.dynamic_intensity) {
  138. // set amplitude lighting params
  139. var def_env_color = scene["world"]["light_settings"]["environment_energy"];
  140. var def_horizon_color = scene["world"]["horizon_color"];
  141. var def_zenith_color = scene["world"]["zenith_color"];
  142. // change sun intensity dependent to its position
  143. var energy = Math.cos(Math.abs(angle_vert));
  144. var sun_energy = Math.max( Math.min(3.0 * energy, 1.0), 0.0) * sun_light.default_energy;
  145. var env_energy = Math.max(energy, 0.1) * def_env_color;
  146. m_lights.set_light_energy(sun_light, sun_energy);
  147. m_scenes.set_environment_colors(scene, env_energy, def_horizon_color, def_zenith_color);
  148. }
  149. m_scenes.update_lamp_scene(sun, scene);
  150. }
  151. }
  152. exports.set_day_time = set_day_time;
  153. /**
  154. * Set the time of day.
  155. * @method module:lights.set_day_time
  156. * @param {number} time new time (0.0...24.0)
  157. */
  158. function set_day_time(time) {
  159. var scene = m_scenes.get_active();
  160. var lamps = m_obj.get_scene_objs(scene, "LAMP", m_obj.DATA_ID_ALL);
  161. for (var i = 0; i < lamps.length; i++) {
  162. var lamp = lamps[i];
  163. var light = lamp.light;
  164. if (light.type == "SUN") {
  165. var sun = lamp;
  166. break;
  167. }
  168. }
  169. if (!sun) {
  170. m_print.error("There is no sun on the scene");
  171. return;
  172. }
  173. update_sun_position(time);
  174. }
  175. /**
  176. * Set the date.
  177. * @method module:lights.set_date
  178. * @param {Date} date new date
  179. */
  180. exports.set_date = function(date) {
  181. _date.y = date.getFullYear();
  182. _date.m = date.getMonth();
  183. _date.d = date.getDate();
  184. if(!_date.y) {
  185. m_print.error("There is no year 0 in the Julian system!");
  186. return;
  187. }
  188. if( _date.y == 1582 && _date.m == 9 && _date.d > 4 && _date.d < 15 ) {
  189. m_print.error("The dates 5 through 14 October, 1582, do not exist in the Gregorian system!");
  190. return;
  191. }
  192. }
  193. /**
  194. * Set the maximum sun angle
  195. * @method module:lights.set_max_sun_angle
  196. * @param {number} angle New angle in degrees (0..90)
  197. */
  198. exports.set_max_sun_angle = function(angle) {
  199. _max_sun_angle = Math.min(Math.max(angle, 0), 90);
  200. }
  201. /**
  202. * Get the light params.
  203. * @method module:lights.get_light_params
  204. * @param {Object3D} lamp_obj Lamp object
  205. * @returns {LightParams | null} Light params or null in case of error
  206. */
  207. exports.get_light_params = function(lamp_obj) {
  208. if (m_obj_util.is_lamp(lamp_obj))
  209. var light = lamp_obj.light;
  210. else {
  211. m_print.error("get_light_params(): Wrong object");
  212. return null;
  213. }
  214. var type = get_light_type(lamp_obj);
  215. if (type)
  216. switch (type) {
  217. case "SPOT":
  218. var rslt = {
  219. "light_type": type,
  220. "light_color": new Float32Array(3),
  221. "light_energy": light.energy,
  222. "light_spot_blend": light.spot_blend,
  223. "light_spot_size": light.spot_size,
  224. "light_distance" : light.distance
  225. };
  226. rslt["light_color"].set(light.color);
  227. break;
  228. case "POINT":
  229. var rslt = {
  230. "light_type": type,
  231. "light_color": new Float32Array(3),
  232. "light_energy": light.energy,
  233. "light_distance" : light.distance
  234. };
  235. rslt["light_color"].set(light.color);
  236. break;
  237. default:
  238. var rslt = {
  239. "light_type": type,
  240. "light_color": new Float32Array(3),
  241. "light_energy": light.energy
  242. };
  243. rslt["light_color"].set(light.color);
  244. break;
  245. }
  246. if (rslt)
  247. return rslt;
  248. else
  249. return null;
  250. }
  251. exports.get_light_type = get_light_type
  252. /**
  253. * Get the light type.
  254. * @method module:lights.get_light_type
  255. * @param {Object3D} lamp_obj Lamp object.
  256. * @returns {string} Light type
  257. */
  258. function get_light_type(lamp_obj) {
  259. if (m_obj_util.is_lamp(lamp_obj))
  260. return lamp_obj.light.type;
  261. else
  262. m_print.error("get_light_type(): Wrong object");
  263. return "";
  264. }
  265. /**
  266. * Set the light params.
  267. * @method module:lights.set_light_params
  268. * @param {Object3D} lamp_obj Lamp object
  269. * @param {LightParams} light_params Light params
  270. */
  271. exports.set_light_params = function(lamp_obj, light_params) {
  272. if (m_obj_util.is_lamp(lamp_obj))
  273. var light = lamp_obj.light;
  274. else {
  275. m_print.error("set_light_params(): Wrong object");
  276. return;
  277. }
  278. var scene = m_scenes.get_active();
  279. var need_update_shaders = false;
  280. if (typeof light_params.light_energy == "number")
  281. m_lights.set_light_energy(light, light_params.light_energy);
  282. if (typeof light_params.light_color == "object")
  283. m_lights.set_light_color(light, light_params.light_color);
  284. if (typeof light_params.light_spot_blend == "number") {
  285. m_lights.set_light_spot_blend(light, light_params.light_spot_blend);
  286. need_update_shaders = true;
  287. }
  288. if (typeof light_params.light_spot_size == "number") {
  289. m_lights.set_light_spot_size(light, light_params.light_spot_size);
  290. need_update_shaders = true;
  291. }
  292. if (typeof light_params.light_distance == "number") {
  293. m_lights.set_light_distance(light, light_params.light_distance);
  294. need_update_shaders = true;
  295. }
  296. m_scenes.update_lamp_scene(lamp_obj, scene);
  297. if (need_update_shaders)
  298. m_scenes.update_all_mesh_shaders(scene);
  299. }
  300. /**
  301. * Get the light energy.
  302. * @method module:lights.get_light_energy
  303. * @param {Object3D} lamp_obj Lamp object
  304. * @returns {number} Light energy value
  305. */
  306. exports.get_light_energy = function(lamp_obj) {
  307. if (!m_obj_util.is_lamp(lamp_obj)) {
  308. m_print.error("get_light_energy(): Wrong object");
  309. return 0;
  310. }
  311. return lamp_obj.light.energy;
  312. }
  313. /**
  314. * Set the light energy.
  315. * @method module:lights.set_light_energy
  316. * @param {Object3D} lamp_obj Lamp object
  317. * @param {number} energy Light energy value
  318. */
  319. exports.set_light_energy = function(lamp_obj, energy) {
  320. if (!m_obj_util.is_lamp(lamp_obj)) {
  321. m_print.error("set_light_energy(): Wrong object");
  322. return;
  323. }
  324. var scene = m_scenes.get_active();
  325. m_lights.set_light_energy(lamp_obj.light, energy);
  326. m_scenes.update_lamp_scene(lamp_obj, scene);
  327. }
  328. /**
  329. * Get the light color.
  330. * @method module:lights.get_light_color
  331. * @param {Object3D} lamp_obj Lamp object
  332. * @param {?RGB} [dest=new Float32Array(3)] Destination RGB vector
  333. * @returns {?RGB} Destination RGB vector
  334. */
  335. exports.get_light_color = function(lamp_obj, dest) {
  336. if (!m_obj_util.is_lamp(lamp_obj)) {
  337. m_print.error("get_light_color(): Wrong object");
  338. return null;
  339. }
  340. dest = dest || new Float32Array(3);
  341. dest.set(lamp_obj.light.color);
  342. return dest;
  343. }
  344. /**
  345. * Set the light color.
  346. * @method module:lights.set_light_color
  347. * @param {Object3D} lamp_obj Lamp object
  348. * @param {RGB} color Light color
  349. */
  350. exports.set_light_color = function(lamp_obj, color) {
  351. if (!m_obj_util.is_lamp(lamp_obj)) {
  352. m_print.error("set_light_color(): Wrong object");
  353. return;
  354. }
  355. var scene = m_scenes.get_active();
  356. m_lights.set_light_color(lamp_obj.light, color);
  357. m_scenes.update_lamp_scene(lamp_obj, scene);
  358. }
  359. function update_sun_position(time) {
  360. // var day = _date.d;
  361. // var month = _date.m;
  362. // var year = _date.y;
  363. // TODO: Calculate real sun position depending on date
  364. // Detect if current year is leap
  365. //var leap_year = (year % 4 == 0) ? 0: 1;
  366. // Number of days after January 1st
  367. //var days_passed = day + 31 * (month - 1);
  368. //if (month <= 2)
  369. // {}
  370. //else if (month <= 4)
  371. // days_passed += leap_year - 3;
  372. //else if (month <= 6)
  373. // days_passed += leap_year - 4;
  374. //else if (month <= 9)
  375. // days_passed += leap_year - 5;
  376. //else if (month <= 11)
  377. // days_passed += leap_year - 6;
  378. //else
  379. // days_passed += leap_year - 7;
  380. //var angle = get_sun_coordinates (_julian_date, (days_passed - 1));
  381. var angle_hor = time < 12 ? time * 15 : (time - 24) * 15 ;
  382. var angle_vert = -Math.cos(time / 12 * Math.PI) * _max_sun_angle;
  383. var sun_params = {};
  384. sun_params.hor_position = angle_hor;
  385. sun_params.vert_position = angle_vert;
  386. set_sun_params(sun_params);
  387. }
  388. function get_sun_coordinates (jul_date, days) {
  389. ////////////////////////////////////////////////////////////////////////////
  390. // Ecliptic coordinates //
  391. ////////////////////////////////////////////////////////////////////////////
  392. // Number of days since GreenWich noon
  393. var n = jul_date - 2451545;
  394. // The mean longitude of the Sun, corrected for the aberration of light
  395. var l = 280.460 + 0.9856474 * n;
  396. l = l % 360;
  397. // The mean anomaly of the Sun
  398. var g = 357.528 + 0.9856003 * n;
  399. g = g % 360;
  400. g = m_util.deg_to_rad(g);
  401. // Ecliptic longitude of the Sun
  402. // var e_longitude = l + 1.915 * Math.sin(g) + 0.020 * Math.sin(2 * g);
  403. // Distance of the Sun from the Earth, in astronomical units
  404. // var r = 1.00014 - 0.01671 * Math.cos(g) - 0.00014 * Math.cos(2 * g);
  405. // Oblique of the ecliptic
  406. var oblique = 23.439 - 0.0000004 * n;
  407. return oblique;
  408. }
  409. function calendar_to_julian(date) {
  410. var y = date.y;
  411. var m = date.m;
  412. var d = date.d;
  413. var jy, ja, jm; //scratch
  414. if( m > 2 ) {
  415. jy = y;
  416. jm = m + 1;
  417. } else {
  418. jy = y - 1;
  419. jm = m + 13;
  420. }
  421. var intgr = Math.floor( Math.floor(365.25 * jy) +
  422. Math.floor(30.6001 * jm) + d + 1720995 );
  423. //check for switch to Gregorian calendar
  424. var gregcal = 15 + 31*( 10 + 12*1582 );
  425. if( d + 31 * (m + 12 * y) >= gregcal ) {
  426. ja = Math.floor (0.01 * jy);
  427. intgr += 2 - ja + Math.floor (0.25 * ja);
  428. }
  429. //round to nearest second
  430. var jd0 = (intgr) * 100000;
  431. var jd = Math.floor(jd0);
  432. if( jd0 - jd > 0.5 ) ++jd;
  433. return jd/100000;
  434. }
  435. }
  436. var lights_factory = register("lights", Lights);
  437. export default lights_factory;