visual_server_light_culler.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. /**************************************************************************/
  2. /* visual_server_light_culler.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "visual_server_light_culler.h"
  31. #include "core/math/camera_matrix.h"
  32. #include "core/math/plane.h"
  33. #include "scene/3d/camera.h"
  34. #include "visual_server_globals.h"
  35. #include "visual_server_scene.h"
  36. #ifdef VISUAL_SERVER_LIGHT_CULLER_DEBUG_STRINGS
  37. const char *VisualServerLightCuller::Data::string_planes[] = {
  38. "NEAR",
  39. "FAR",
  40. "LEFT",
  41. "TOP",
  42. "RIGHT",
  43. "BOTTOM",
  44. };
  45. const char *VisualServerLightCuller::Data::string_points[] = {
  46. "FAR_LEFT_TOP",
  47. "FAR_LEFT_BOTTOM",
  48. "FAR_RIGHT_TOP",
  49. "FAR_RIGHT_BOTTOM",
  50. "NEAR_LEFT_TOP",
  51. "NEAR_LEFT_BOTTOM",
  52. "NEAR_RIGHT_TOP",
  53. "NEAR_RIGHT_BOTTOM",
  54. };
  55. String VisualServerLightCuller::Data::plane_bitfield_to_string(unsigned int BF) {
  56. String sz;
  57. for (int n = 0; n < 6; n++) {
  58. unsigned int bit = 1 << n;
  59. if (BF & bit) {
  60. sz += String(string_planes[n]) + ", ";
  61. }
  62. }
  63. return sz;
  64. }
  65. #endif
  66. bool VisualServerLightCuller::prepare_light(const VisualServerScene::Instance &p_instance) {
  67. if (!data.is_active()) {
  68. return true;
  69. }
  70. LightSource lsource;
  71. switch (VSG::storage->light_get_type(p_instance.base)) {
  72. case VS::LIGHT_SPOT:
  73. lsource.type = LightSource::ST_SPOTLIGHT;
  74. lsource.angle = VSG::storage->light_get_param(p_instance.base, VS::LIGHT_PARAM_SPOT_ANGLE);
  75. lsource.range = VSG::storage->light_get_param(p_instance.base, VS::LIGHT_PARAM_RANGE);
  76. break;
  77. case VS::LIGHT_OMNI:
  78. lsource.type = LightSource::ST_OMNI;
  79. lsource.range = VSG::storage->light_get_param(p_instance.base, VS::LIGHT_PARAM_RANGE);
  80. break;
  81. case VS::LIGHT_DIRECTIONAL:
  82. lsource.type = LightSource::ST_DIRECTIONAL;
  83. // Could deal with a max directional shadow range here? NYI
  84. // LIGHT_PARAM_SHADOW_MAX_DISTANCE
  85. break;
  86. }
  87. lsource.pos = p_instance.transform.origin;
  88. lsource.dir = -p_instance.transform.basis.get_axis(2);
  89. lsource.dir.normalize();
  90. bool visible = _add_light_camera_planes(lsource);
  91. if (data.light_culling_active) {
  92. return visible;
  93. }
  94. return true;
  95. }
  96. int VisualServerLightCuller::cull(int p_count, VisualServerScene::Instance **p_result_array) {
  97. if (!data.is_active() || !is_caster_culling_active()) {
  98. return p_count;
  99. }
  100. // If the light is out of range, no need to check anything, just return 0 casters.
  101. // Ideally an out of range light should not even be drawn AT ALL (no shadow map, no PCF etc).
  102. if (data.out_of_range) {
  103. return 0;
  104. }
  105. int new_count = p_count;
  106. // Go through all the casters in the list (the list will hopefully shrink as we go).
  107. for (int n = 0; n < new_count; n++) {
  108. // World space aabb.
  109. const AABB &bb = p_result_array[n]->transformed_aabb;
  110. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  111. if (is_logging()) {
  112. print_line("bb : " + String(bb));
  113. }
  114. #endif
  115. float r_min, r_max;
  116. bool show = true;
  117. for (int p = 0; p < data.num_cull_planes; p++) {
  118. // As we only need r_min, could this be optimized?
  119. bb.project_range_in_plane(data.cull_planes[p], r_min, r_max);
  120. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  121. if (is_logging()) {
  122. print_line("\tplane " + itos(p) + " : " + String(data.cull_planes[p]) + " r_min " + String(Variant(r_min)) + " r_max " + String(Variant(r_max)));
  123. }
  124. #endif
  125. if (r_min > 0.0f) {
  126. show = false;
  127. break;
  128. }
  129. }
  130. // Remove.
  131. if (!show) {
  132. // Quick unsorted remove - swap last element and reduce count.
  133. p_result_array[n] = p_result_array[new_count - 1];
  134. new_count--;
  135. // Repeat this element next iteration of the loop as it has been removed and replaced by the last.
  136. n--;
  137. }
  138. }
  139. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  140. int removed = p_count - new_count;
  141. if (removed) {
  142. if (((data.debug_count) % 60) == 0) {
  143. print_line("[" + itos(data.debug_count) + "] linear cull before " + itos(p_count) + " after " + itos(new_count));
  144. }
  145. }
  146. #endif
  147. return new_count;
  148. }
  149. void VisualServerLightCuller::add_cull_plane(const Plane &p) {
  150. ERR_FAIL_COND(data.num_cull_planes >= MAX_CULL_PLANES);
  151. data.cull_planes[data.num_cull_planes++] = p;
  152. }
  153. // Directional lights are different to points, as the origin is infinitely in the distance, so the plane third
  154. // points are derived differently.
  155. bool VisualServerLightCuller::add_light_camera_planes_directional(const LightSource &p_light_source) {
  156. uint32_t lookup = 0;
  157. // Directional light, we will use dot against the light direction to determine back facing planes.
  158. for (int n = 0; n < 6; n++) {
  159. float dot = data.frustum_planes[n].normal.dot(p_light_source.dir);
  160. if (dot > 0.0f) {
  161. lookup |= 1 << n;
  162. // Add backfacing camera frustum planes.
  163. add_cull_plane(data.frustum_planes[n]);
  164. }
  165. }
  166. ERR_FAIL_COND_V(lookup >= LUT_SIZE, true);
  167. // Deal with special case... if the light is INSIDE the view frustum (i.e. all planes face away)
  168. // then we will add the camera frustum planes to clip the light volume .. there is no need to
  169. // render shadow casters outside the frustum as shadows can never re-enter the frustum.
  170. // Should never happen with directional light?? This may be able to be removed.
  171. if (lookup == 63) {
  172. data.num_cull_planes = 0;
  173. for (int n = 0; n < data.frustum_planes.size(); n++) {
  174. add_cull_plane(data.frustum_planes[n]);
  175. }
  176. return true;
  177. }
  178. // Each edge forms a plane.
  179. #ifdef VISUAL_SERVER_LIGHT_CULLER_CALCULATE_LUT
  180. const LocalVector<uint8_t> &entry = _calculated_LUT[lookup];
  181. // each edge forms a plane
  182. int n_edges = entry.size() - 1;
  183. #else
  184. uint8_t *entry = &data.LUT_entries[lookup][0];
  185. int n_edges = data.LUT_entry_sizes[lookup] - 1;
  186. #endif
  187. for (int e = 0; e < n_edges; e++) {
  188. int i0 = entry[e];
  189. int i1 = entry[e + 1];
  190. const Vector3 &pt0 = data.frustum_points[i0];
  191. const Vector3 &pt1 = data.frustum_points[i1];
  192. // Create a third point from the light direction.
  193. Vector3 pt2 = pt0 - p_light_source.dir;
  194. if (!_is_colinear_tri(pt0, pt1, pt2)) {
  195. // Create plane from 3 points.
  196. Plane p(pt0, pt1, pt2);
  197. add_cull_plane(p);
  198. }
  199. }
  200. // Last to 0 edge.
  201. if (n_edges) {
  202. int i0 = entry[n_edges]; // Last.
  203. int i1 = entry[0]; // First.
  204. const Vector3 &pt0 = data.frustum_points[i0];
  205. const Vector3 &pt1 = data.frustum_points[i1];
  206. // Create a third point from the light direction.
  207. Vector3 pt2 = pt0 - p_light_source.dir;
  208. if (!_is_colinear_tri(pt0, pt1, pt2)) {
  209. // Create plane from 3 points.
  210. Plane p(pt0, pt1, pt2);
  211. add_cull_plane(p);
  212. }
  213. }
  214. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  215. if (is_logging()) {
  216. print_line("lcam.pos is " + String(p_light_source.pos));
  217. }
  218. #endif
  219. return true;
  220. }
  221. bool VisualServerLightCuller::_add_light_camera_planes(const LightSource &p_light_source) {
  222. if (!data.is_active()) {
  223. return true;
  224. }
  225. // We should have called prepare_camera before this.
  226. ERR_FAIL_COND_V(data.frustum_planes.size() != 6, true);
  227. // Start with 0 cull planes.
  228. data.num_cull_planes = 0;
  229. data.out_of_range = false;
  230. switch (p_light_source.type) {
  231. case LightSource::ST_SPOTLIGHT:
  232. case LightSource::ST_OMNI:
  233. break;
  234. case LightSource::ST_DIRECTIONAL:
  235. return add_light_camera_planes_directional(p_light_source);
  236. break;
  237. default:
  238. return false; // not yet supported
  239. break;
  240. }
  241. uint32_t lookup = 0;
  242. // Find which of the camera planes are facing away from the light.
  243. // We can also test for the situation where the light max range means it cannot
  244. // affect the camera frustum. This is absolutely worth doing because it is relatively
  245. // cheap, and if the entire light can be culled this can vastly improve performance
  246. // (much more than just culling casters).
  247. // POINT LIGHT (spotlight, omni)
  248. // Instead of using dot product to compare light direction to plane, we can simply
  249. // find out which side of the plane the camera is on. By definition this marks the point at which the plane
  250. // becomes invisible.
  251. // OMNIS
  252. if (p_light_source.type == LightSource::ST_OMNI) {
  253. for (int n = 0; n < 6; n++) {
  254. float dist = data.frustum_planes[n].distance_to(p_light_source.pos);
  255. if (dist < 0.0f) {
  256. lookup |= 1 << n;
  257. // Add backfacing camera frustum planes.
  258. add_cull_plane(data.frustum_planes[n]);
  259. } else {
  260. // Is the light out of range?
  261. // This is one of the tests. If the point source is more than range distance from a frustum plane, it can't
  262. // be seen.
  263. if (dist >= p_light_source.range) {
  264. // If the light is out of range, no need to do anything else, everything will be culled.
  265. data.out_of_range = true;
  266. return false;
  267. }
  268. }
  269. }
  270. } else {
  271. // SPOTLIGHTs, more complex to cull.
  272. Vector3 pos_end = p_light_source.pos + (p_light_source.dir * p_light_source.range);
  273. // This is the radius of the cone at distance 1.
  274. float radius_at_dist_one = Math::tan(Math::deg2rad(p_light_source.angle));
  275. // The worst case radius of the cone at the end point can be calculated
  276. // (the radius will scale linearly with length along the cone).
  277. float end_cone_radius = radius_at_dist_one * p_light_source.range;
  278. for (int n = 0; n < 6; n++) {
  279. float dist = data.frustum_planes[n].distance_to(p_light_source.pos);
  280. if (dist < 0.0f) {
  281. // Either the plane is backfacing or we are inside the frustum.
  282. lookup |= 1 << n;
  283. // Add backfacing camera frustum planes.
  284. add_cull_plane(data.frustum_planes[n]);
  285. } else {
  286. // The light is in front of the plane.
  287. // Is the light out of range?
  288. if (dist >= p_light_source.range) {
  289. data.out_of_range = true;
  290. return false;
  291. }
  292. // For a spotlight, we can use an extra test
  293. // at this point the cone start is in front of the plane...
  294. // If the cone end point is further than the maximum possible distance to the plane
  295. // we can guarantee that the cone does not cross the plane, and hence the cone
  296. // is outside the frustum.
  297. float dist_end = data.frustum_planes[n].distance_to(pos_end);
  298. if (dist_end >= end_cone_radius) {
  299. data.out_of_range = true;
  300. return false;
  301. }
  302. }
  303. }
  304. }
  305. // The lookup should be within the LUT, logic should prevent this.
  306. ERR_FAIL_COND_V(lookup >= LUT_SIZE, true);
  307. // Deal with special case... if the light is INSIDE the view frustum (i.e. all planes face away)
  308. // then we will add the camera frustum planes to clip the light volume .. there is no need to
  309. // render shadow casters outside the frustum as shadows can never re-enter the frustum.
  310. if (lookup == 63) {
  311. data.num_cull_planes = 0;
  312. for (int n = 0; n < data.frustum_planes.size(); n++) {
  313. add_cull_plane(data.frustum_planes[n]);
  314. }
  315. return true;
  316. }
  317. // Each edge forms a plane.
  318. uint8_t *entry = &data.LUT_entries[lookup][0];
  319. int n_edges = data.LUT_entry_sizes[lookup] - 1;
  320. const Vector3 &pt2 = p_light_source.pos;
  321. for (int e = 0; e < n_edges; e++) {
  322. int i0 = entry[e];
  323. int i1 = entry[e + 1];
  324. const Vector3 &pt0 = data.frustum_points[i0];
  325. const Vector3 &pt1 = data.frustum_points[i1];
  326. if (!_is_colinear_tri(pt0, pt1, pt2)) {
  327. // Create plane from 3 points.
  328. Plane p(pt0, pt1, pt2);
  329. add_cull_plane(p);
  330. }
  331. }
  332. // Last to 0 edge.
  333. if (n_edges) {
  334. int i0 = entry[n_edges]; // Last.
  335. int i1 = entry[0]; // First.
  336. const Vector3 &pt0 = data.frustum_points[i0];
  337. const Vector3 &pt1 = data.frustum_points[i1];
  338. if (!_is_colinear_tri(pt0, pt1, pt2)) {
  339. // Create plane from 3 points.
  340. Plane p(pt0, pt1, pt2);
  341. add_cull_plane(p);
  342. }
  343. }
  344. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  345. if (is_logging()) {
  346. print_line("lsource.pos is " + String(p_light_source.pos));
  347. }
  348. #endif
  349. return true;
  350. }
  351. bool VisualServerLightCuller::prepare_camera(const Transform &p_cam_transform, const CameraMatrix &p_cam_matrix) {
  352. data.debug_count++;
  353. // For debug flash off and on.
  354. #ifdef LIGHT_CULLER_DEBUG_FLASH
  355. if (!Engine::get_singleton()->is_editor_hint()) {
  356. int dc = data.debug_count / LIGHT_CULLER_DEBUG_FLASH_FREQUENCY;
  357. bool bnew_active;
  358. bnew_active = (dc % 2) == 0;
  359. if (bnew_active != data.active) {
  360. data.active = bnew_active;
  361. print_line("switching light culler " + String(Variant(data.active)));
  362. }
  363. }
  364. #endif
  365. if (!data.is_active()) {
  366. return false;
  367. }
  368. // Get the camera frustum planes in world space.
  369. data.frustum_planes = p_cam_matrix.get_projection_planes(p_cam_transform);
  370. data.num_cull_planes = 0;
  371. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  372. if (is_logging()) {
  373. for (int p = 0; p < 6; p++) {
  374. print_line("plane " + itos(p) + " : " + String(data.frustum_planes[p]));
  375. }
  376. }
  377. #endif
  378. // We want to calculate the frustum corners in a specific order.
  379. const CameraMatrix::Planes intersections[8][3] = {
  380. { CameraMatrix::PLANE_FAR, CameraMatrix::PLANE_LEFT, CameraMatrix::PLANE_TOP },
  381. { CameraMatrix::PLANE_FAR, CameraMatrix::PLANE_LEFT, CameraMatrix::PLANE_BOTTOM },
  382. { CameraMatrix::PLANE_FAR, CameraMatrix::PLANE_RIGHT, CameraMatrix::PLANE_TOP },
  383. { CameraMatrix::PLANE_FAR, CameraMatrix::PLANE_RIGHT, CameraMatrix::PLANE_BOTTOM },
  384. { CameraMatrix::PLANE_NEAR, CameraMatrix::PLANE_LEFT, CameraMatrix::PLANE_TOP },
  385. { CameraMatrix::PLANE_NEAR, CameraMatrix::PLANE_LEFT, CameraMatrix::PLANE_BOTTOM },
  386. { CameraMatrix::PLANE_NEAR, CameraMatrix::PLANE_RIGHT, CameraMatrix::PLANE_TOP },
  387. { CameraMatrix::PLANE_NEAR, CameraMatrix::PLANE_RIGHT, CameraMatrix::PLANE_BOTTOM },
  388. };
  389. for (int i = 0; i < 8; i++) {
  390. // 3 plane intersection, gives us a point.
  391. bool res = data.frustum_planes[intersections[i][0]].intersect_3(data.frustum_planes[intersections[i][1]], data.frustum_planes[intersections[i][2]], &data.frustum_points[i]);
  392. // What happens with a zero frustum? NYI - deal with this.
  393. ERR_FAIL_COND_V(!res, false);
  394. #ifdef LIGHT_CULLER_DEBUG_LOGGING
  395. if (is_logging()) {
  396. print_line("point " + itos(i) + " -> " + String(data.frustum_points[i]));
  397. }
  398. #endif
  399. }
  400. return true;
  401. }
  402. VisualServerLightCuller::VisualServerLightCuller() {
  403. // Used to determine which frame to give debug output.
  404. data.debug_count = -1;
  405. // bactive is switching on and off the light culler
  406. data.caster_culling_active = Engine::get_singleton()->is_editor_hint() == false;
  407. #ifdef VISUAL_SERVER_LIGHT_CULLER_CALCULATE_LUT
  408. create_LUT();
  409. #endif
  410. }
  411. /* clang-format off */
  412. uint8_t VisualServerLightCuller::Data::LUT_entry_sizes[LUT_SIZE] = {0, 4, 4, 0, 4, 6, 6, 8, 4, 6, 6, 8, 6, 6, 6, 6, 4, 6, 6, 8, 0, 8, 8, 0, 6, 6, 6, 6, 8, 6, 6, 4, 4, 6, 6, 8, 6, 6, 6, 6, 0, 8, 8, 0, 8, 6, 6, 4, 6, 6, 6, 6, 8, 6, 6, 4, 8, 6, 6, 4, 0, 4, 4, 0, };
  413. // The lookup table used to determine which edges form the silhouette of the camera frustum,
  414. // depending on the viewing angle (defined by which camera planes are backward facing).
  415. uint8_t VisualServerLightCuller::Data::LUT_entries[LUT_SIZE][8] = {
  416. {0, 0, 0, 0, 0, 0, 0, 0, },
  417. {7, 6, 4, 5, 0, 0, 0, 0, },
  418. {1, 0, 2, 3, 0, 0, 0, 0, },
  419. {0, 0, 0, 0, 0, 0, 0, 0, },
  420. {1, 5, 4, 0, 0, 0, 0, 0, },
  421. {1, 5, 7, 6, 4, 0, 0, 0, },
  422. {4, 0, 2, 3, 1, 5, 0, 0, },
  423. {5, 7, 6, 4, 0, 2, 3, 1, },
  424. {0, 4, 6, 2, 0, 0, 0, 0, },
  425. {0, 4, 5, 7, 6, 2, 0, 0, },
  426. {6, 2, 3, 1, 0, 4, 0, 0, },
  427. {2, 3, 1, 0, 4, 5, 7, 6, },
  428. {0, 1, 5, 4, 6, 2, 0, 0, },
  429. {0, 1, 5, 7, 6, 2, 0, 0, },
  430. {6, 2, 3, 1, 5, 4, 0, 0, },
  431. {2, 3, 1, 5, 7, 6, 0, 0, },
  432. {2, 6, 7, 3, 0, 0, 0, 0, },
  433. {2, 6, 4, 5, 7, 3, 0, 0, },
  434. {7, 3, 1, 0, 2, 6, 0, 0, },
  435. {3, 1, 0, 2, 6, 4, 5, 7, },
  436. {0, 0, 0, 0, 0, 0, 0, 0, },
  437. {2, 6, 4, 0, 1, 5, 7, 3, },
  438. {7, 3, 1, 5, 4, 0, 2, 6, },
  439. {0, 0, 0, 0, 0, 0, 0, 0, },
  440. {2, 0, 4, 6, 7, 3, 0, 0, },
  441. {2, 0, 4, 5, 7, 3, 0, 0, },
  442. {7, 3, 1, 0, 4, 6, 0, 0, },
  443. {3, 1, 0, 4, 5, 7, 0, 0, },
  444. {2, 0, 1, 5, 4, 6, 7, 3, },
  445. {2, 0, 1, 5, 7, 3, 0, 0, },
  446. {7, 3, 1, 5, 4, 6, 0, 0, },
  447. {3, 1, 5, 7, 0, 0, 0, 0, },
  448. {3, 7, 5, 1, 0, 0, 0, 0, },
  449. {3, 7, 6, 4, 5, 1, 0, 0, },
  450. {5, 1, 0, 2, 3, 7, 0, 0, },
  451. {7, 6, 4, 5, 1, 0, 2, 3, },
  452. {3, 7, 5, 4, 0, 1, 0, 0, },
  453. {3, 7, 6, 4, 0, 1, 0, 0, },
  454. {5, 4, 0, 2, 3, 7, 0, 0, },
  455. {7, 6, 4, 0, 2, 3, 0, 0, },
  456. {0, 0, 0, 0, 0, 0, 0, 0, },
  457. {3, 7, 6, 2, 0, 4, 5, 1, },
  458. {5, 1, 0, 4, 6, 2, 3, 7, },
  459. {0, 0, 0, 0, 0, 0, 0, 0, },
  460. {3, 7, 5, 4, 6, 2, 0, 1, },
  461. {3, 7, 6, 2, 0, 1, 0, 0, },
  462. {5, 4, 6, 2, 3, 7, 0, 0, },
  463. {7, 6, 2, 3, 0, 0, 0, 0, },
  464. {3, 2, 6, 7, 5, 1, 0, 0, },
  465. {3, 2, 6, 4, 5, 1, 0, 0, },
  466. {5, 1, 0, 2, 6, 7, 0, 0, },
  467. {1, 0, 2, 6, 4, 5, 0, 0, },
  468. {3, 2, 6, 7, 5, 4, 0, 1, },
  469. {3, 2, 6, 4, 0, 1, 0, 0, },
  470. {5, 4, 0, 2, 6, 7, 0, 0, },
  471. {6, 4, 0, 2, 0, 0, 0, 0, },
  472. {3, 2, 0, 4, 6, 7, 5, 1, },
  473. {3, 2, 0, 4, 5, 1, 0, 0, },
  474. {5, 1, 0, 4, 6, 7, 0, 0, },
  475. {1, 0, 4, 5, 0, 0, 0, 0, },
  476. {0, 0, 0, 0, 0, 0, 0, 0, },
  477. {3, 2, 0, 1, 0, 0, 0, 0, },
  478. {5, 4, 6, 7, 0, 0, 0, 0, },
  479. {0, 0, 0, 0, 0, 0, 0, 0, },
  480. };
  481. /* clang-format on */
  482. #ifdef VISUAL_SERVER_LIGHT_CULLER_CALCULATE_LUT
  483. // See e.g. http://lspiroengine.com/?p=153 for reference.
  484. // Principles are the same, but differences to the article:
  485. // * Order of planes / points is different in Godot.
  486. // * We use a lookup table at runtime.
  487. void VisualServerLightCuller::create_LUT() {
  488. // Each pair of planes that are opposite can have an edge.
  489. for (int plane_0 = 0; plane_0 < PLANE_TOTAL; plane_0++) {
  490. // For each neighbour of the plane.
  491. PlaneOrder neighs[4];
  492. get_neighbouring_planes((PlaneOrder)plane_0, neighs);
  493. for (int n = 0; n < 4; n++) {
  494. int plane_1 = neighs[n];
  495. // If these are opposite we need to add the 2 points they share.
  496. PointOrder pts[2];
  497. get_corners_of_planes((PlaneOrder)plane_0, (PlaneOrder)plane_1, pts);
  498. add_LUT(plane_0, plane_1, pts);
  499. }
  500. }
  501. for (uint32_t n = 0; n < LUT_SIZE; n++) {
  502. compact_LUT_entry(n);
  503. }
  504. debug_print_LUT();
  505. debug_print_LUT_as_table();
  506. }
  507. // we can pre-create the entire LUT and store it hard coded as a static inside the executable!
  508. // it is only small in size, 64 entries with max 8 bytes per entry
  509. void VisualServerLightCuller::debug_print_LUT_as_table() {
  510. print_line("\nLIGHT VOLUME TABLE BEGIN\n");
  511. print_line("Copy this to LUT_entry_sizes:\n");
  512. String sz = "{";
  513. for (int n = 0; n < LUT_SIZE; n++) {
  514. const LocalVector<uint8_t> &entry = _calculated_LUT[n];
  515. sz += itos(entry.size()) + ", ";
  516. }
  517. sz += "}";
  518. print_line(sz);
  519. print_line("\nCopy this to LUT_entries:\n");
  520. for (int n = 0; n < LUT_SIZE; n++) {
  521. const LocalVector<uint8_t> &entry = _calculated_LUT[n];
  522. String sz = "{";
  523. // First is the number of points in the entry.
  524. int s = entry.size();
  525. for (int p = 0; p < 8; p++) {
  526. if (p < s)
  527. sz += itos(entry[p]);
  528. else
  529. sz += "0"; // just a spacer
  530. sz += ", ";
  531. }
  532. sz += "},";
  533. print_line(sz);
  534. }
  535. print_line("\nLIGHT VOLUME TABLE END\n");
  536. }
  537. void VisualServerLightCuller::debug_print_LUT() {
  538. for (uint32_t n = 0; n < LUT_SIZE; n++) {
  539. String sz;
  540. sz = "LUT" + itos(n) + ":\t";
  541. sz += Data::plane_bitfield_to_string(n);
  542. print_line(sz);
  543. const LocalVector<uint8_t> &entry = _calculated_LUT[n];
  544. sz = "\t" + string_LUT_entry(entry);
  545. print_line(sz);
  546. }
  547. }
  548. String VisualServerLightCuller::string_LUT_entry(const LocalVector<uint8_t> &p_entry) {
  549. String string;
  550. for (uint32_t n = 0; n < p_entry.size(); n++) {
  551. uint8_t val = p_entry[n];
  552. DEV_ASSERT(val < 8);
  553. const char *sz_point = Data::string_points[val];
  554. string += sz_point;
  555. string += ", ";
  556. }
  557. return string;
  558. }
  559. String VisualServerLightCuller::debug_string_LUT_entry(const LocalVector<uint8_t> &p_entry, bool p_pair) {
  560. String string;
  561. for (int i = 0; i < p_entry.size(); i++) {
  562. int pt_order = p_entry[i];
  563. if (p_pair && ((i % 2) == 0)) {
  564. string += itos(pt_order) + "-";
  565. } else {
  566. string += itos(pt_order) + ", ";
  567. }
  568. }
  569. return string;
  570. }
  571. void VisualServerLightCuller::add_LUT(int p_plane_0, int p_plane_1, PointOrder p_pts[2]) {
  572. uint32_t bit0 = 1 << p_plane_0;
  573. uint32_t bit1 = 1 << p_plane_1;
  574. // All entries of the LUT that have plane 0 set and plane 1 not set.
  575. for (uint32_t n = 0; n < 64; n++) {
  576. // If bit0 not set...
  577. if (!(n & bit0))
  578. continue;
  579. // If bit1 set...
  580. if (n & bit1)
  581. continue;
  582. // Meets criteria.
  583. add_LUT_entry(n, p_pts);
  584. }
  585. }
  586. void VisualServerLightCuller::add_LUT_entry(uint32_t p_entry_id, PointOrder p_pts[2]) {
  587. DEV_ASSERT(p_entry_id < LUT_SIZE);
  588. LocalVector<uint8_t> &entry = _calculated_LUT[p_entry_id];
  589. entry.push_back(p_pts[0]);
  590. entry.push_back(p_pts[1]);
  591. }
  592. void VisualServerLightCuller::compact_LUT_entry(uint32_t p_entry_id) {
  593. DEV_ASSERT(p_entry_id < LUT_SIZE);
  594. LocalVector<uint8_t> &entry = _calculated_LUT[p_entry_id];
  595. int num_pairs = entry.size() / 2;
  596. if (num_pairs == 0)
  597. return;
  598. LocalVector<uint8_t> temp;
  599. String string;
  600. string = "Compact LUT" + itos(p_entry_id) + ":\t";
  601. string += debug_string_LUT_entry(entry, true);
  602. print_line(string);
  603. // Add first pair.
  604. temp.push_back(entry[0]);
  605. temp.push_back(entry[1]);
  606. unsigned int BFpairs = 1;
  607. string = debug_string_LUT_entry(temp) + " -> ";
  608. print_line(string);
  609. // Attempt to add a pair each time.
  610. for (int done = 1; done < num_pairs; done++) {
  611. string = "done " + itos(done) + ": ";
  612. // Find a free pair.
  613. for (int p = 1; p < num_pairs; p++) {
  614. unsigned int bit = 1 << p;
  615. // Is it done already?
  616. if (BFpairs & bit)
  617. continue;
  618. // There must be at least 1 free pair.
  619. // Attempt to add.
  620. int a = entry[p * 2];
  621. int b = entry[(p * 2) + 1];
  622. string += "[" + itos(a) + "-" + itos(b) + "], ";
  623. int found_a = temp.find(a);
  624. int found_b = temp.find(b);
  625. // Special case, if they are both already in the list, no need to add
  626. // as this is a link from the tail to the head of the list.
  627. if ((found_a != -1) && (found_b != -1)) {
  628. string += "foundAB link " + itos(found_a) + ", " + itos(found_b) + " ";
  629. BFpairs |= bit;
  630. goto found;
  631. }
  632. // Find a.
  633. if (found_a != -1) {
  634. string += "foundA " + itos(found_a) + " ";
  635. temp.insert(found_a + 1, b);
  636. BFpairs |= bit;
  637. goto found;
  638. }
  639. // Find b.
  640. if (found_b != -1) {
  641. string += "foundB " + itos(found_b) + " ";
  642. temp.insert(found_b, a);
  643. BFpairs |= bit;
  644. goto found;
  645. }
  646. } // Check each pair for adding.
  647. // If we got here before finding a link, the whole set of planes is INVALID
  648. // e.g. far and near plane only, does not create continuous sillouhette of edges.
  649. print_line("\tINVALID");
  650. entry.clear();
  651. return;
  652. found:;
  653. print_line(string);
  654. string = "\ttemp now : " + debug_string_LUT_entry(temp);
  655. print_line(string);
  656. }
  657. // temp should now be the sorted entry .. delete the old one and replace by temp.
  658. entry.clear();
  659. entry = temp;
  660. }
  661. void VisualServerLightCuller::get_neighbouring_planes(PlaneOrder p_plane, PlaneOrder r_neigh_planes[4]) const {
  662. // Table of neighbouring planes to each.
  663. static const PlaneOrder neigh_table[PLANE_TOTAL][4] = {
  664. { // LSM_FP_NEAR
  665. PLANE_LEFT,
  666. PLANE_RIGHT,
  667. PLANE_TOP,
  668. PLANE_BOTTOM },
  669. { // LSM_FP_FAR
  670. PLANE_LEFT,
  671. PLANE_RIGHT,
  672. PLANE_TOP,
  673. PLANE_BOTTOM },
  674. { // LSM_FP_LEFT
  675. PLANE_TOP,
  676. PLANE_BOTTOM,
  677. PLANE_NEAR,
  678. PLANE_FAR },
  679. { // LSM_FP_TOP
  680. PLANE_LEFT,
  681. PLANE_RIGHT,
  682. PLANE_NEAR,
  683. PLANE_FAR },
  684. { // LSM_FP_RIGHT
  685. PLANE_TOP,
  686. PLANE_BOTTOM,
  687. PLANE_NEAR,
  688. PLANE_FAR },
  689. { // LSM_FP_BOTTOM
  690. PLANE_LEFT,
  691. PLANE_RIGHT,
  692. PLANE_NEAR,
  693. PLANE_FAR },
  694. };
  695. for (int n = 0; n < 4; n++) {
  696. r_neigh_planes[n] = neigh_table[p_plane][n];
  697. }
  698. }
  699. // Given two planes, returns the two points shared by those planes. The points are always
  700. // returned in counter-clockwise order, assuming the first input plane is facing towards
  701. // the viewer.
  702. // param p_plane_a The plane facing towards the viewer.
  703. // param p_plane_b A plane neighboring p_plane_a.
  704. // param r_points An array of exactly two elements to be filled with the indices of the points
  705. // on return.
  706. void VisualServerLightCuller::get_corners_of_planes(PlaneOrder p_plane_a, PlaneOrder p_plane_b, PointOrder r_points[2]) const {
  707. static const PointOrder fp_table[PLANE_TOTAL][PLANE_TOTAL][2] = {
  708. {
  709. // LSM_FP_NEAR
  710. {
  711. // LSM_FP_NEAR
  712. PT_NEAR_LEFT_TOP, PT_NEAR_RIGHT_TOP, // Invalid combination.
  713. },
  714. {
  715. // LSM_FP_FAR
  716. PT_FAR_RIGHT_TOP, PT_FAR_LEFT_TOP, // Invalid combination.
  717. },
  718. {
  719. // LSM_FP_LEFT
  720. PT_NEAR_LEFT_TOP,
  721. PT_NEAR_LEFT_BOTTOM,
  722. },
  723. {
  724. // LSM_FP_TOP
  725. PT_NEAR_RIGHT_TOP,
  726. PT_NEAR_LEFT_TOP,
  727. },
  728. {
  729. // LSM_FP_RIGHT
  730. PT_NEAR_RIGHT_BOTTOM,
  731. PT_NEAR_RIGHT_TOP,
  732. },
  733. {
  734. // LSM_FP_BOTTOM
  735. PT_NEAR_LEFT_BOTTOM,
  736. PT_NEAR_RIGHT_BOTTOM,
  737. },
  738. },
  739. {
  740. // LSM_FP_FAR
  741. {
  742. // LSM_FP_NEAR
  743. PT_FAR_LEFT_TOP, PT_FAR_RIGHT_TOP, // Invalid combination.
  744. },
  745. {
  746. // LSM_FP_FAR
  747. PT_FAR_RIGHT_TOP, PT_FAR_LEFT_TOP, // Invalid combination.
  748. },
  749. {
  750. // LSM_FP_LEFT
  751. PT_FAR_LEFT_BOTTOM,
  752. PT_FAR_LEFT_TOP,
  753. },
  754. {
  755. // LSM_FP_TOP
  756. PT_FAR_LEFT_TOP,
  757. PT_FAR_RIGHT_TOP,
  758. },
  759. {
  760. // LSM_FP_RIGHT
  761. PT_FAR_RIGHT_TOP,
  762. PT_FAR_RIGHT_BOTTOM,
  763. },
  764. {
  765. // LSM_FP_BOTTOM
  766. PT_FAR_RIGHT_BOTTOM,
  767. PT_FAR_LEFT_BOTTOM,
  768. },
  769. },
  770. {
  771. // LSM_FP_LEFT
  772. {
  773. // LSM_FP_NEAR
  774. PT_NEAR_LEFT_BOTTOM,
  775. PT_NEAR_LEFT_TOP,
  776. },
  777. {
  778. // LSM_FP_FAR
  779. PT_FAR_LEFT_TOP,
  780. PT_FAR_LEFT_BOTTOM,
  781. },
  782. {
  783. // LSM_FP_LEFT
  784. PT_FAR_LEFT_BOTTOM, PT_FAR_LEFT_BOTTOM, // Invalid combination.
  785. },
  786. {
  787. // LSM_FP_TOP
  788. PT_NEAR_LEFT_TOP,
  789. PT_FAR_LEFT_TOP,
  790. },
  791. {
  792. // LSM_FP_RIGHT
  793. PT_FAR_LEFT_BOTTOM, PT_FAR_LEFT_BOTTOM, // Invalid combination.
  794. },
  795. {
  796. // LSM_FP_BOTTOM
  797. PT_FAR_LEFT_BOTTOM,
  798. PT_NEAR_LEFT_BOTTOM,
  799. },
  800. },
  801. {
  802. // LSM_FP_TOP
  803. {
  804. // LSM_FP_NEAR
  805. PT_NEAR_LEFT_TOP,
  806. PT_NEAR_RIGHT_TOP,
  807. },
  808. {
  809. // LSM_FP_FAR
  810. PT_FAR_RIGHT_TOP,
  811. PT_FAR_LEFT_TOP,
  812. },
  813. {
  814. // LSM_FP_LEFT
  815. PT_FAR_LEFT_TOP,
  816. PT_NEAR_LEFT_TOP,
  817. },
  818. {
  819. // LSM_FP_TOP
  820. PT_NEAR_LEFT_TOP, PT_FAR_LEFT_TOP, // Invalid combination.
  821. },
  822. {
  823. // LSM_FP_RIGHT
  824. PT_NEAR_RIGHT_TOP,
  825. PT_FAR_RIGHT_TOP,
  826. },
  827. {
  828. // LSM_FP_BOTTOM
  829. PT_FAR_LEFT_BOTTOM, PT_NEAR_LEFT_BOTTOM, // Invalid combination.
  830. },
  831. },
  832. {
  833. // LSM_FP_RIGHT
  834. {
  835. // LSM_FP_NEAR
  836. PT_NEAR_RIGHT_TOP,
  837. PT_NEAR_RIGHT_BOTTOM,
  838. },
  839. {
  840. // LSM_FP_FAR
  841. PT_FAR_RIGHT_BOTTOM,
  842. PT_FAR_RIGHT_TOP,
  843. },
  844. {
  845. // LSM_FP_LEFT
  846. PT_FAR_RIGHT_BOTTOM, PT_FAR_RIGHT_BOTTOM, // Invalid combination.
  847. },
  848. {
  849. // LSM_FP_TOP
  850. PT_FAR_RIGHT_TOP,
  851. PT_NEAR_RIGHT_TOP,
  852. },
  853. {
  854. // LSM_FP_RIGHT
  855. PT_FAR_RIGHT_BOTTOM, PT_FAR_RIGHT_BOTTOM, // Invalid combination.
  856. },
  857. {
  858. // LSM_FP_BOTTOM
  859. PT_NEAR_RIGHT_BOTTOM,
  860. PT_FAR_RIGHT_BOTTOM,
  861. },
  862. },
  863. // ==
  864. // P_NEAR,
  865. // P_FAR,
  866. // P_LEFT,
  867. // P_TOP,
  868. // P_RIGHT,
  869. // P_BOTTOM,
  870. {
  871. // LSM_FP_BOTTOM
  872. {
  873. // LSM_FP_NEAR
  874. PT_NEAR_RIGHT_BOTTOM,
  875. PT_NEAR_LEFT_BOTTOM,
  876. },
  877. {
  878. // LSM_FP_FAR
  879. PT_FAR_LEFT_BOTTOM,
  880. PT_FAR_RIGHT_BOTTOM,
  881. },
  882. {
  883. // LSM_FP_LEFT
  884. PT_NEAR_LEFT_BOTTOM,
  885. PT_FAR_LEFT_BOTTOM,
  886. },
  887. {
  888. // LSM_FP_TOP
  889. PT_NEAR_LEFT_BOTTOM, PT_FAR_LEFT_BOTTOM, // Invalid combination.
  890. },
  891. {
  892. // LSM_FP_RIGHT
  893. PT_FAR_RIGHT_BOTTOM,
  894. PT_NEAR_RIGHT_BOTTOM,
  895. },
  896. {
  897. // LSM_FP_BOTTOM
  898. PT_FAR_LEFT_BOTTOM, PT_NEAR_LEFT_BOTTOM, // Invalid combination.
  899. },
  900. },
  901. // ==
  902. };
  903. r_points[0] = fp_table[p_plane_a][p_plane_b][0];
  904. r_points[1] = fp_table[p_plane_a][p_plane_b][1];
  905. }
  906. #endif