portal_occlusion_culler.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. /**************************************************************************/
  2. /* portal_occlusion_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 "portal_occlusion_culler.h"
  31. #include "core/engine.h"
  32. #include "core/math/aabb.h"
  33. #include "core/project_settings.h"
  34. #include "portal_renderer.h"
  35. #include "servers/visual/visual_server_globals.h"
  36. #include "servers/visual/visual_server_scene.h"
  37. #define _log(a, b) ;
  38. //#define _log_prepare(a) log(a, 0)
  39. #define _log_prepare(a) ;
  40. bool PortalOcclusionCuller::_debug_log = true;
  41. bool PortalOcclusionCuller::_redraw_gizmo = false;
  42. void PortalOcclusionCuller::Clipper::debug_print_points(String p_string) {
  43. print_line(p_string);
  44. for (int n = 0; n < _pts_in.size(); n++) {
  45. print_line("\t" + itos(n) + " : " + String(Variant(_pts_in[n])));
  46. }
  47. }
  48. Plane PortalOcclusionCuller::Clipper::interpolate(const Plane &p_a, const Plane &p_b, real_t p_t) const {
  49. Vector3 diff = p_b.normal - p_a.normal;
  50. real_t d = p_b.d - p_a.d;
  51. diff *= p_t;
  52. d *= p_t;
  53. return Plane(p_a.normal + diff, p_a.d + d);
  54. }
  55. real_t PortalOcclusionCuller::Clipper::clip_and_find_poly_area(const Plane *p_verts, int p_num_verts) {
  56. _pts_in.clear();
  57. _pts_out.clear();
  58. // seed
  59. for (int n = 0; n < p_num_verts; n++) {
  60. _pts_in.push_back(p_verts[n]);
  61. }
  62. if (!clip_to_plane(-1, 0, 0, 1)) {
  63. return 0.0;
  64. }
  65. if (!clip_to_plane(1, 0, 0, 1)) {
  66. return 0.0;
  67. }
  68. if (!clip_to_plane(0, -1, 0, 1)) {
  69. return 0.0;
  70. }
  71. if (!clip_to_plane(0, 1, 0, 1)) {
  72. return 0.0;
  73. }
  74. if (!clip_to_plane(0, 0, -1, 1)) {
  75. return 0.0;
  76. }
  77. if (!clip_to_plane(0, 0, 1, 1)) {
  78. return 0.0;
  79. }
  80. // perspective divide
  81. _pts_final.resize(_pts_in.size());
  82. for (int n = 0; n < _pts_in.size(); n++) {
  83. _pts_final[n] = _pts_in[n].normal / _pts_in[n].d;
  84. }
  85. return Geometry::find_polygon_area(&_pts_final[0], _pts_final.size());
  86. }
  87. bool PortalOcclusionCuller::Clipper::is_inside(const Plane &p_pt, Boundary p_boundary) {
  88. real_t w = p_pt.d;
  89. switch (p_boundary) {
  90. case B_LEFT: {
  91. return p_pt.normal.x > -w;
  92. } break;
  93. case B_RIGHT: {
  94. return p_pt.normal.x < w;
  95. } break;
  96. case B_TOP: {
  97. return p_pt.normal.y < w;
  98. } break;
  99. case B_BOTTOM: {
  100. return p_pt.normal.y > -w;
  101. } break;
  102. case B_NEAR: {
  103. return p_pt.normal.z < w;
  104. } break;
  105. case B_FAR: {
  106. return p_pt.normal.z > -w;
  107. } break;
  108. default:
  109. break;
  110. }
  111. return false;
  112. }
  113. // a is out, b is in
  114. Plane PortalOcclusionCuller::Clipper::intersect(const Plane &p_a, const Plane &p_b, Boundary p_boundary) {
  115. Plane diff_plane(p_b.normal - p_a.normal, p_b.d - p_a.d);
  116. const Vector3 &diff = diff_plane.normal;
  117. real_t t = 0.0;
  118. const real_t epsilon = 0.001f;
  119. // prevent divide by zero
  120. switch (p_boundary) {
  121. case B_LEFT: {
  122. if (diff.x > epsilon) {
  123. t = (-1.0f - p_a.normal.x) / diff.x;
  124. }
  125. } break;
  126. case B_RIGHT: {
  127. if (-diff.x > epsilon) {
  128. t = (p_a.normal.x - 1.0f) / -diff.x;
  129. }
  130. } break;
  131. case B_TOP: {
  132. if (-diff.y > epsilon) {
  133. t = (p_a.normal.y - 1.0f) / -diff.y;
  134. }
  135. } break;
  136. case B_BOTTOM: {
  137. if (diff.y > epsilon) {
  138. t = (-1.0f - p_a.normal.y) / diff.y;
  139. }
  140. } break;
  141. case B_NEAR: {
  142. if (-diff.z > epsilon) {
  143. t = (p_a.normal.z - 1.0f) / -diff.z;
  144. }
  145. } break;
  146. case B_FAR: {
  147. if (diff.z > epsilon) {
  148. t = (-1.0f - p_a.normal.z) / diff.z;
  149. }
  150. } break;
  151. default:
  152. break;
  153. }
  154. diff_plane.normal *= t;
  155. diff_plane.d *= t;
  156. return Plane(p_a.normal + diff_plane.normal, p_a.d + diff_plane.d);
  157. }
  158. // Clip the poly to the plane given by the formula a * x + b * y + c * z + d * w.
  159. bool PortalOcclusionCuller::Clipper::clip_to_plane(real_t a, real_t b, real_t c, real_t d) {
  160. _pts_out.clear();
  161. // repeat the first
  162. _pts_in.push_back(_pts_in[0]);
  163. Plane vPrev = _pts_in[0];
  164. real_t dpPrev = a * vPrev.normal.x + b * vPrev.normal.y + c * vPrev.normal.z + d * vPrev.d;
  165. for (int i = 1; i < _pts_in.size(); ++i) {
  166. Plane v = _pts_in[i];
  167. real_t dp = a * v.normal.x + b * v.normal.y + c * v.normal.z + d * v.d;
  168. if (dpPrev >= 0) {
  169. _pts_out.push_back(vPrev);
  170. }
  171. if (sgn(dp) != sgn(dpPrev)) {
  172. real_t t = dp < 0 ? dpPrev / (dpPrev - dp) : -dpPrev / (dp - dpPrev);
  173. Plane vOut = interpolate(vPrev, v, t);
  174. _pts_out.push_back(vOut);
  175. }
  176. vPrev = v;
  177. dpPrev = dp;
  178. }
  179. // start again from the output points next time
  180. _pts_in = _pts_out;
  181. return _pts_in.size() > 2;
  182. }
  183. Geometry::MeshData PortalOcclusionCuller::debug_get_current_polys() const {
  184. Geometry::MeshData md;
  185. for (int n = 0; n < _num_polys; n++) {
  186. const Occlusion::PolyPlane &p = _polys[n].poly;
  187. int first_index = md.vertices.size();
  188. Vector3 normal_push = p.plane.normal * 0.001f;
  189. // copy verts
  190. for (int c = 0; c < p.num_verts; c++) {
  191. md.vertices.push_back(p.verts[c] + normal_push);
  192. }
  193. // indices
  194. Geometry::MeshData::Face face;
  195. // triangle fan
  196. face.indices.resize(p.num_verts);
  197. for (int c = 0; c < p.num_verts; c++) {
  198. face.indices.set(c, first_index + c);
  199. }
  200. md.faces.push_back(face);
  201. }
  202. return md;
  203. }
  204. void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, const LocalVector<uint32_t, uint32_t> &p_occluder_pool_ids, const Vector3 &pt_camera, const LocalVector<Plane> &p_planes) {
  205. _portal_renderer = &p_portal_renderer;
  206. // Bodge to keep settings up to date, until the project settings PR is merged
  207. #ifdef TOOLS_ENABLED
  208. if (Engine::get_singleton()->is_editor_hint() && ((Engine::get_singleton()->get_frames_drawn() % 16) == 0)) {
  209. _max_polys = GLOBAL_GET("rendering/misc/occlusion_culling/max_active_polygons");
  210. }
  211. #endif
  212. _num_spheres = 0;
  213. _pt_camera = pt_camera;
  214. // spheres
  215. _num_spheres = 0;
  216. real_t goodness_of_fit_sphere[MAX_SPHERES];
  217. for (int n = 0; n < _max_spheres; n++) {
  218. goodness_of_fit_sphere[n] = 0.0f;
  219. }
  220. real_t weakest_fit_sphere = FLT_MAX;
  221. int weakest_sphere = 0;
  222. _sphere_closest_dist = FLT_MAX;
  223. // polys
  224. _num_polys = 0;
  225. for (int n = 0; n < _max_polys; n++) {
  226. _polys[n].goodness_of_fit = 0.0f;
  227. }
  228. real_t weakest_fit_poly = FLT_MAX;
  229. int weakest_poly_id = 0;
  230. #ifdef TOOLS_ENABLED
  231. uint32_t polycount = 0;
  232. #endif
  233. const PortalResources &resources = VSG::scene->get_portal_resources();
  234. // find occluders
  235. for (unsigned int o = 0; o < p_occluder_pool_ids.size(); o++) {
  236. int id = p_occluder_pool_ids[o];
  237. VSOccluder_Instance &occ = p_portal_renderer.get_pool_occluder_instance(id);
  238. // is it active?
  239. // in the case of rooms, they will always be active, as inactive
  240. // are removed from rooms. But for whole scene mode, some may be inactive.
  241. if (!occ.active) {
  242. continue;
  243. }
  244. // TODO : occlusion cull spheres AGAINST themselves.
  245. // i.e. a sphere that is occluded by another occluder is no
  246. // use as an occluder...
  247. if (occ.type == VSOccluder_Instance::OT_SPHERE) {
  248. // make sure world space spheres are up to date
  249. p_portal_renderer.occluder_ensure_up_to_date_sphere(resources, occ);
  250. // cull entire AABB
  251. if (is_aabb_culled(occ.aabb, p_planes)) {
  252. continue;
  253. }
  254. // multiple spheres
  255. for (int n = 0; n < occ.list_ids.size(); n++) {
  256. const Occlusion::Sphere &occluder_sphere = p_portal_renderer.get_pool_occluder_world_sphere(occ.list_ids[n]);
  257. // is the occluder sphere culled?
  258. if (is_sphere_culled(occluder_sphere.pos, occluder_sphere.radius, p_planes)) {
  259. continue;
  260. }
  261. real_t dist = (occluder_sphere.pos - pt_camera).length();
  262. // calculate the goodness of fit .. smaller distance better, and larger radius
  263. // calculate adjusted radius at 100.0
  264. real_t fit = 100 / MAX(dist, 0.01f);
  265. fit *= occluder_sphere.radius;
  266. // until we reach the max, just keep recording, and keep track
  267. // of the worst fit
  268. if (_num_spheres < _max_spheres) {
  269. _spheres[_num_spheres] = occluder_sphere;
  270. _sphere_distances[_num_spheres] = dist;
  271. goodness_of_fit_sphere[_num_spheres] = fit;
  272. if (fit < weakest_fit_sphere) {
  273. weakest_fit_sphere = fit;
  274. weakest_sphere = _num_spheres;
  275. }
  276. // keep a record of the closest sphere for quick rejects
  277. if (dist < _sphere_closest_dist) {
  278. _sphere_closest_dist = dist;
  279. }
  280. _num_spheres++;
  281. } else {
  282. // must beat the weakest
  283. if (fit > weakest_fit_sphere) {
  284. _spheres[weakest_sphere] = occluder_sphere;
  285. _sphere_distances[weakest_sphere] = dist;
  286. goodness_of_fit_sphere[weakest_sphere] = fit;
  287. // keep a record of the closest sphere for quick rejects
  288. if (dist < _sphere_closest_dist) {
  289. _sphere_closest_dist = dist;
  290. }
  291. // the weakest may have changed (this could be done more efficiently)
  292. weakest_fit_sphere = FLT_MAX;
  293. for (int s = 0; s < _max_spheres; s++) {
  294. if (goodness_of_fit_sphere[s] < weakest_fit_sphere) {
  295. weakest_fit_sphere = goodness_of_fit_sphere[s];
  296. weakest_sphere = s;
  297. }
  298. }
  299. }
  300. }
  301. }
  302. } // sphere
  303. if (occ.type == VSOccluder_Instance::OT_MESH) {
  304. // make sure world space spheres are up to date
  305. p_portal_renderer.occluder_ensure_up_to_date_polys(resources, occ);
  306. // multiple polys
  307. for (int n = 0; n < occ.list_ids.size(); n++) {
  308. const VSOccluder_Poly &opoly = p_portal_renderer.get_pool_occluder_world_poly(occ.list_ids[n]);
  309. const Occlusion::PolyPlane &poly = opoly.poly;
  310. // backface cull
  311. bool faces_camera = poly.plane.is_point_over(pt_camera);
  312. if (!faces_camera && !opoly.two_way) {
  313. continue;
  314. }
  315. real_t fit;
  316. if (!calculate_poly_goodness_of_fit(opoly, fit)) {
  317. continue;
  318. }
  319. if (_num_polys < _max_polys) {
  320. SortPoly &dest = _polys[_num_polys];
  321. dest.poly = poly;
  322. dest.flags = faces_camera ? SortPoly::SPF_FACES_CAMERA : 0;
  323. if (opoly.num_holes) {
  324. dest.flags |= SortPoly::SPF_HAS_HOLES;
  325. }
  326. #ifdef TOOLS_ENABLED
  327. dest.poly_source_id = polycount++;
  328. #endif
  329. dest.mesh_source_id = occ.list_ids[n];
  330. dest.goodness_of_fit = fit;
  331. if (fit < weakest_fit_poly) {
  332. weakest_fit_poly = fit;
  333. weakest_poly_id = _num_polys;
  334. }
  335. _num_polys++;
  336. } else {
  337. // must beat the weakest
  338. if (fit > weakest_fit_poly) {
  339. SortPoly &dest = _polys[weakest_poly_id];
  340. dest.poly = poly;
  341. //dest.faces_camera = faces_camera;
  342. dest.flags = faces_camera ? SortPoly::SPF_FACES_CAMERA : 0;
  343. if (opoly.num_holes) {
  344. dest.flags |= SortPoly::SPF_HAS_HOLES;
  345. }
  346. #ifdef TOOLS_ENABLED
  347. dest.poly_source_id = polycount++;
  348. #endif
  349. dest.mesh_source_id = occ.list_ids[n];
  350. dest.goodness_of_fit = fit;
  351. // the weakest may have changed (this could be done more efficiently)
  352. weakest_fit_poly = FLT_MAX;
  353. for (int p = 0; p < _max_polys; p++) {
  354. real_t goodness_of_fit = _polys[p].goodness_of_fit;
  355. if (goodness_of_fit < weakest_fit_poly) {
  356. weakest_fit_poly = goodness_of_fit;
  357. weakest_poly_id = p;
  358. }
  359. }
  360. }
  361. } // polys full up, replace
  362. }
  363. }
  364. } // for o
  365. precalc_poly_edge_planes(pt_camera);
  366. // flip polys so always facing camera
  367. for (int n = 0; n < _num_polys; n++) {
  368. if (!(_polys[n].flags & SortPoly::SPF_FACES_CAMERA)) {
  369. _polys[n].poly.flip();
  370. // must flip holes and planes too
  371. _precalced_poly[n].flip();
  372. }
  373. }
  374. // cull polys against each other.
  375. whittle_polys();
  376. // checksum is used only in the editor, to decide
  377. // whether to redraw the gizmo of active polys
  378. #ifdef TOOLS_ENABLED
  379. uint32_t last_checksum = _poly_checksum;
  380. _poly_checksum = 0;
  381. for (int n = 0; n < _num_polys; n++) {
  382. _poly_checksum += _polys[n].poly_source_id;
  383. //_log_prepare("prepfinal : " + itos(_polys[n].poly_source_id) + " fit : " + rtos(_polys[n].goodness_of_fit));
  384. }
  385. if (_poly_checksum != last_checksum) {
  386. _redraw_gizmo = true;
  387. }
  388. #endif
  389. // force the sphere closest distance to above zero to prevent
  390. // divide by zero in the quick reject
  391. _sphere_closest_dist = MAX(_sphere_closest_dist, 0.001);
  392. // sphere self occlusion.
  393. // we could avoid testing the closest sphere, but the complexity isn't worth any speed benefit
  394. for (int n = 0; n < _num_spheres; n++) {
  395. const Occlusion::Sphere &sphere = _spheres[n];
  396. // is it occluded by another sphere?
  397. if (cull_sphere(sphere.pos, sphere.radius, n)) {
  398. // yes, unordered remove
  399. _num_spheres--;
  400. _spheres[n] = _spheres[_num_spheres];
  401. _sphere_distances[n] = _sphere_distances[_num_spheres];
  402. // repeat this n
  403. n--;
  404. }
  405. }
  406. // record whether to do any occlusion culling at all..
  407. _occluders_present = _num_spheres || _num_polys;
  408. }
  409. void PortalOcclusionCuller::precalc_poly_edge_planes(const Vector3 &p_pt_camera) {
  410. for (int n = 0; n < _num_polys; n++) {
  411. const SortPoly &sortpoly = _polys[n];
  412. const Occlusion::PolyPlane &spoly = sortpoly.poly;
  413. PreCalcedPoly &dpoly = _precalced_poly[n];
  414. dpoly.edge_planes.num_planes = spoly.num_verts;
  415. for (int e = 0; e < spoly.num_verts; e++) {
  416. // point a and b of the edge
  417. const Vector3 &pt_a = spoly.verts[e];
  418. const Vector3 &pt_b = spoly.verts[(e + 1) % spoly.num_verts];
  419. // edge plane to camera
  420. dpoly.edge_planes.planes[e] = Plane(p_pt_camera, pt_a, pt_b);
  421. }
  422. dpoly.num_holes = 0;
  423. // holes
  424. if (sortpoly.flags & SortPoly::SPF_HAS_HOLES) {
  425. // get the mesh poly and the holes
  426. const VSOccluder_Poly &mesh = _portal_renderer->get_pool_occluder_world_poly(sortpoly.mesh_source_id);
  427. dpoly.num_holes = mesh.num_holes;
  428. for (int h = 0; h < mesh.num_holes; h++) {
  429. uint32_t hid = mesh.hole_pool_ids[h];
  430. const VSOccluder_Hole &hole = _portal_renderer->get_pool_occluder_world_hole(hid);
  431. // copy the verts to the precalced poly,
  432. // we will need these later for whittling polys.
  433. // We could alternatively link back to the original verts, but that gets messy.
  434. dpoly.hole_polys[h] = hole;
  435. int hole_num_verts = hole.num_verts;
  436. const Vector3 *hverts = hole.verts;
  437. // number of planes equals number of verts forming edges
  438. dpoly.hole_edge_planes[h].num_planes = hole_num_verts;
  439. for (int e = 0; e < hole_num_verts; e++) {
  440. const Vector3 &pt_a = hverts[e];
  441. const Vector3 &pt_b = hverts[(e + 1) % hole_num_verts];
  442. dpoly.hole_edge_planes[h].planes[e] = Plane(p_pt_camera, pt_a, pt_b);
  443. } // for e
  444. } // for h
  445. } // if has holes
  446. }
  447. }
  448. void PortalOcclusionCuller::whittle_polys() {
  449. //#define GODOT_OCCLUSION_FLASH_POLYS
  450. #ifdef GODOT_OCCLUSION_FLASH_POLYS
  451. if (((Engine::get_singleton()->get_frames_drawn() / 4) % 2) == 0) {
  452. return;
  453. }
  454. #endif
  455. bool repeat = true;
  456. while (repeat) {
  457. repeat = false;
  458. // Check for complete occlusion of polys by a closer poly.
  459. // Such polys can be completely removed from checks.
  460. for (int n = 0; n < _num_polys; n++) {
  461. // ensure we test each occluder once and only once
  462. // (as this routine will repeat each time an occluded poly is found)
  463. SortPoly &sort_poly = _polys[n];
  464. if (!(sort_poly.flags & SortPoly::SPF_TESTED_AS_OCCLUDER)) {
  465. sort_poly.flags |= SortPoly::SPF_TESTED_AS_OCCLUDER;
  466. } else {
  467. continue;
  468. }
  469. const Occlusion::PolyPlane &poly = _polys[n].poly;
  470. const Plane &occluder_plane = poly.plane;
  471. const PreCalcedPoly &pcp = _precalced_poly[n];
  472. // the goodness of fit is the screen space area at the moment,
  473. // so we can use it as a quick reject .. polys behind occluders will always
  474. // be smaller area than the occluder.
  475. real_t occluder_area = _polys[n].goodness_of_fit;
  476. // check each other poly as an occludee
  477. for (int t = 0; t < _num_polys; t++) {
  478. if (n == t) {
  479. continue;
  480. }
  481. // quick reject based on screen space area.
  482. // if the area of the test poly is larger, it can't be completely behind
  483. // the occluder.
  484. bool quick_reject_entire_occludee = _polys[t].goodness_of_fit > occluder_area;
  485. const Occlusion::PolyPlane &test_poly = _polys[t].poly;
  486. PreCalcedPoly &pcp_test = _precalced_poly[t];
  487. // We have two considerations:
  488. // (1) Entire poly is occluded
  489. // (2) If not (1), then maybe a hole is occluded
  490. bool completely_reject = false;
  491. if (!quick_reject_entire_occludee && is_poly_inside_occlusion_volume(test_poly, occluder_plane, pcp.edge_planes)) {
  492. completely_reject = true;
  493. // we must also test against all holes if some are present
  494. for (int h = 0; h < pcp.num_holes; h++) {
  495. if (is_poly_touching_hole(test_poly, pcp.hole_edge_planes[h])) {
  496. completely_reject = false;
  497. break;
  498. }
  499. }
  500. if (completely_reject) {
  501. // yes .. we can remove this poly .. but do not muck up the iteration of the list
  502. //print_line("poly is occluded " + itos(t));
  503. #ifdef TOOLS_ENABLED
  504. // this condition should never happen, we should never be checking occludee against itself
  505. DEV_ASSERT(_polys[t].poly_source_id != _polys[n].poly_source_id);
  506. #endif
  507. // unordered remove
  508. _polys[t] = _polys[_num_polys - 1];
  509. _precalced_poly[t] = _precalced_poly[_num_polys - 1];
  510. _num_polys--;
  511. // no NOT repeat the test poly if it was copied from n, i.e. the occludee would
  512. // be the same as the occluder
  513. if (_num_polys != n) {
  514. // repeat this test poly as it will be the next
  515. t--;
  516. }
  517. // If we end up removing a poly BEFORE n, the replacement poly (from the unordered remove)
  518. // will never get tested as an occluder. So we have to account for this by rerunning the routine.
  519. repeat = true;
  520. } // allow due to holes
  521. } // if poly inside occlusion volume
  522. // if we did not completely reject, there could be holes that could be rejected
  523. if (!completely_reject) {
  524. if (pcp_test.num_holes) {
  525. for (int h = 0; h < pcp_test.num_holes; h++) {
  526. const Occlusion::Poly &hole_poly = pcp_test.hole_polys[h];
  527. // is the hole within the occluder?
  528. if (is_poly_inside_occlusion_volume(hole_poly, occluder_plane, pcp.edge_planes)) {
  529. // if the hole touching a hole in the occluder? if so we can't eliminate it
  530. bool allow = true;
  531. for (int oh = 0; oh < pcp.num_holes; oh++) {
  532. if (is_poly_touching_hole(hole_poly, pcp.hole_edge_planes[oh])) {
  533. allow = false;
  534. break;
  535. }
  536. }
  537. if (allow) {
  538. // Unordered remove the hole. No need to repeat the whole while loop I don't think?
  539. // As this just makes it more efficient at runtime, it doesn't make the further whittling more accurate.
  540. pcp_test.num_holes--;
  541. pcp_test.hole_edge_planes[h] = pcp_test.hole_edge_planes[pcp_test.num_holes];
  542. pcp_test.hole_polys[h] = pcp_test.hole_polys[pcp_test.num_holes];
  543. h--; // repeat this as the unordered remove has placed a new member into h slot
  544. } // allow
  545. } // hole is within
  546. }
  547. } // has holes
  548. } // did not completely reject
  549. } // for t through occludees
  550. } // for n through occluders
  551. } // while repeat
  552. // order polys by distance to camera / area? NYI
  553. }
  554. bool PortalOcclusionCuller::calculate_poly_goodness_of_fit(const VSOccluder_Poly &p_opoly, real_t &r_fit) {
  555. // transform each of the poly points, find the area in screen space
  556. // The points must be homogeneous coordinates, i.e. BEFORE
  557. // the perspective divide, in clip space. They will have the perspective
  558. // divide applied after clipping, to calculate the area.
  559. // We therefore store them as planes to store the w coordinate as d.
  560. Plane xpoints[Occlusion::PolyPlane::MAX_POLY_VERTS];
  561. int num_verts = p_opoly.poly.num_verts;
  562. for (int n = 0; n < num_verts; n++) {
  563. // source and dest in homogeneous coords
  564. Plane source(p_opoly.poly.verts[n], 1.0f);
  565. Plane &dest = xpoints[n];
  566. dest = _matrix_camera.xform4(source);
  567. }
  568. // find screen space area
  569. real_t area = _clipper.clip_and_find_poly_area(xpoints, num_verts);
  570. if (area <= 0.0f) {
  571. return false;
  572. }
  573. r_fit = area;
  574. return true;
  575. }
  576. bool PortalOcclusionCuller::_is_poly_of_interest_to_split_plane(const Plane *p_poly_split_plane, int p_poly_id) const {
  577. const Occlusion::PolyPlane &poly = _polys[p_poly_id].poly;
  578. int over = 0;
  579. int under = 0;
  580. // we need an epsilon because adjacent polys that just
  581. // join with a wall may have small floating point error ahead
  582. // of the splitting plane.
  583. const real_t epsilon = 0.005f;
  584. for (int n = 0; n < poly.num_verts; n++) {
  585. // point a and b of the edge
  586. const Vector3 &pt = poly.verts[n];
  587. real_t dist = p_poly_split_plane->distance_to(pt);
  588. if (dist > epsilon) {
  589. over++;
  590. } else {
  591. under++;
  592. }
  593. }
  594. // return whether straddles the plane
  595. return over && under;
  596. }
  597. bool PortalOcclusionCuller::cull_aabb_to_polys_ex(const AABB &p_aabb) const {
  598. _log("\n", 0);
  599. _log("* cull_aabb_to_polys_ex " + String(Variant(p_aabb)), 0);
  600. Plane plane;
  601. for (int n = 0; n < _num_polys; n++) {
  602. _log("\tchecking poly " + itos(n), 0);
  603. const SortPoly &sortpoly = _polys[n];
  604. const Occlusion::PolyPlane &poly = sortpoly.poly;
  605. // occludee must be on opposite side to camera
  606. real_t omin, omax;
  607. p_aabb.project_range_in_plane(poly.plane, omin, omax);
  608. if (omax > -0.2f) {
  609. _log("\t\tAABB is in front of occluder, ignoring", 0);
  610. continue;
  611. }
  612. // test against each edge of the poly, and expand the edge
  613. bool hit = true;
  614. const PreCalcedPoly &pcp = _precalced_poly[n];
  615. for (int e = 0; e < pcp.edge_planes.num_planes; e++) {
  616. // edge plane to camera
  617. plane = pcp.edge_planes.planes[e];
  618. p_aabb.project_range_in_plane(plane, omin, omax);
  619. if (omax > 0.0f) {
  620. hit = false;
  621. break;
  622. }
  623. }
  624. // if it hit, check against holes
  625. if (hit && pcp.num_holes) {
  626. for (int h = 0; h < pcp.num_holes; h++) {
  627. const PlaneSet &hole = pcp.hole_edge_planes[h];
  628. // if the AABB is totally outside any edge, it is safe for a hit
  629. bool safe = false;
  630. for (int e = 0; e < hole.num_planes; e++) {
  631. // edge plane to camera
  632. plane = hole.planes[e];
  633. p_aabb.project_range_in_plane(plane, omin, omax);
  634. // if inside the hole, no longer a hit on this poly
  635. if (omin > 0.0f) {
  636. safe = true;
  637. break;
  638. }
  639. } // for e
  640. if (!safe) {
  641. hit = false;
  642. }
  643. if (!hit) {
  644. break;
  645. }
  646. } // for h
  647. } // if has holes
  648. // hit?
  649. if (hit) {
  650. return true;
  651. }
  652. }
  653. _log("\tno hit", 0);
  654. return false;
  655. }
  656. bool PortalOcclusionCuller::cull_aabb_to_polys(const AABB &p_aabb) const {
  657. if (!_num_polys) {
  658. return false;
  659. }
  660. return cull_aabb_to_polys_ex(p_aabb);
  661. }
  662. bool PortalOcclusionCuller::cull_sphere_to_polys(const Vector3 &p_occludee_center, real_t p_occludee_radius) const {
  663. if (!_num_polys) {
  664. return false;
  665. }
  666. Plane plane;
  667. for (int n = 0; n < _num_polys; n++) {
  668. const Occlusion::PolyPlane &poly = _polys[n].poly;
  669. // test against each edge of the poly, and expand the edge
  670. bool hit = true;
  671. // occludee must be on opposite side to camera
  672. real_t dist = poly.plane.distance_to(p_occludee_center);
  673. if (dist > -p_occludee_radius) {
  674. continue;
  675. }
  676. for (int e = 0; e < poly.num_verts; e++) {
  677. plane = Plane(_pt_camera, poly.verts[e], poly.verts[(e + 1) % poly.num_verts]);
  678. // de-expand
  679. plane.d -= p_occludee_radius;
  680. if (plane.is_point_over(p_occludee_center)) {
  681. hit = false;
  682. break;
  683. }
  684. }
  685. // hit?
  686. if (hit) {
  687. return true;
  688. }
  689. }
  690. return false;
  691. }
  692. bool PortalOcclusionCuller::cull_sphere_to_spheres(const Vector3 &p_occludee_center, real_t p_occludee_radius, const Vector3 &p_ray_dir, real_t p_dist_to_occludee, int p_ignore_sphere) const {
  693. // maybe not required
  694. if (!_num_spheres) {
  695. return false;
  696. }
  697. // prevent divide by zero, and the occludee cannot be occluded if we are WITHIN
  698. // its bounding sphere... so no need to check
  699. if (p_dist_to_occludee < _sphere_closest_dist) {
  700. return false;
  701. }
  702. // this can probably be done cheaper with dot products but the math might be a bit fiddly to get right
  703. for (int s = 0; s < _num_spheres; s++) {
  704. // first get the sphere distance
  705. real_t occluder_dist_to_cam = _sphere_distances[s];
  706. if (p_dist_to_occludee < occluder_dist_to_cam) {
  707. // can't occlude
  708. continue;
  709. }
  710. // the perspective adjusted occludee radius
  711. real_t adjusted_occludee_radius = p_occludee_radius * (occluder_dist_to_cam / p_dist_to_occludee);
  712. const Occlusion::Sphere &occluder_sphere = _spheres[s];
  713. real_t occluder_radius = occluder_sphere.radius - adjusted_occludee_radius;
  714. if (occluder_radius > 0.0) {
  715. occluder_radius = occluder_radius * occluder_radius;
  716. // distance to hit
  717. real_t dist;
  718. if (occluder_sphere.intersect_ray(_pt_camera, p_ray_dir, dist, occluder_radius)) {
  719. if ((dist < p_dist_to_occludee) && (s != p_ignore_sphere)) {
  720. // occluded
  721. return true;
  722. }
  723. }
  724. } // expanded occluder radius is more than 0
  725. }
  726. return false;
  727. }
  728. bool PortalOcclusionCuller::cull_sphere(const Vector3 &p_occludee_center, real_t p_occludee_radius, int p_ignore_sphere, bool p_cull_to_polys) const {
  729. if (!_occluders_present) {
  730. return false;
  731. }
  732. // ray from origin to the occludee
  733. Vector3 ray_dir = p_occludee_center - _pt_camera;
  734. real_t dist_to_occludee_raw = ray_dir.length();
  735. // account for occludee radius
  736. real_t dist_to_occludee = dist_to_occludee_raw - p_occludee_radius;
  737. // ignore occlusion for closeup, and avoid divide by zero
  738. if (dist_to_occludee_raw < 0.1) {
  739. return false;
  740. }
  741. // normalize ray
  742. // hopefully by this point, dist_to_occludee_raw cannot possibly be zero due to above check
  743. ray_dir *= 1.0 / dist_to_occludee_raw;
  744. if (cull_sphere_to_spheres(p_occludee_center, p_occludee_radius, ray_dir, dist_to_occludee, p_ignore_sphere)) {
  745. return true;
  746. }
  747. if (p_cull_to_polys && cull_sphere_to_polys(p_occludee_center, p_occludee_radius)) {
  748. return true;
  749. }
  750. return false;
  751. }
  752. PortalOcclusionCuller::PortalOcclusionCuller() {
  753. _max_spheres = GLOBAL_GET("rendering/misc/occlusion_culling/max_active_spheres");
  754. _max_polys = GLOBAL_GET("rendering/misc/occlusion_culling/max_active_polygons");
  755. }
  756. void PortalOcclusionCuller::log(String p_string, int p_depth) const {
  757. if (_debug_log) {
  758. for (int n = 0; n < p_depth; n++) {
  759. p_string = "\t\t\t" + p_string;
  760. }
  761. print_line(p_string);
  762. }
  763. }
  764. #undef _log
  765. #undef _log_prepare