rendering_light_culler.cpp 32 KB

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