portal_renderer.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /**************************************************************************/
  2. /* portal_renderer.h */
  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. #ifndef PORTAL_RENDERER_H
  31. #define PORTAL_RENDERER_H
  32. #include "core/math/camera_matrix.h"
  33. #include "core/math/geometry.h"
  34. #include "core/math/plane.h"
  35. #include "core/pooled_list.h"
  36. #include "core/vector.h"
  37. #include "portal_gameplay_monitor.h"
  38. #include "portal_pvs.h"
  39. #include "portal_resources.h"
  40. #include "portal_rooms_bsp.h"
  41. #include "portal_tracer.h"
  42. #include "portal_types.h"
  43. class Transform;
  44. struct VSStatic {
  45. // the lifetime of statics is not strictly monitored like moving objects
  46. // therefore we store a RID which could return NULL if the object has been deleted
  47. RID instance;
  48. AABB aabb;
  49. // statics are placed in a room, but they can optionally sprawl to other rooms
  50. // if large (like lights)
  51. uint32_t source_room_id;
  52. // dynamics will request their AABB each frame
  53. // from the visual server in case they have moved.
  54. // But they will NOT update the rooms they are in...
  55. // so this works well for e.g. moving platforms, but not for objects
  56. // that will move between rooms.
  57. uint32_t dynamic;
  58. };
  59. // static / dynamic visibility notifiers.
  60. // ghost objects are not culled, but are present in rooms
  61. // and expect to receive gameplay notifications
  62. struct VSStaticGhost {
  63. ObjectID object_id;
  64. uint32_t last_tick_hit = 0;
  65. uint32_t last_room_tick_hit = 0;
  66. };
  67. class PortalRenderer {
  68. public:
  69. // use most significant bit to store whether an instance is being used in the room system
  70. // in which case, deleting such an instance should deactivate the portal system to prevent
  71. // crashes due to dangling references to instances.
  72. static const uint32_t OCCLUSION_HANDLE_ROOM_BIT = 1 << 31;
  73. static bool use_occlusion_culling;
  74. struct MovingBase {
  75. // when the rooms_and_portals_clear message is sent,
  76. // we want to remove all references to old rooms in the moving
  77. // objects, to prevent dangling references.
  78. void rooms_and_portals_clear() { destroy(); }
  79. void destroy() {
  80. _rooms.clear();
  81. room_id = -1;
  82. last_tick_hit = 0;
  83. last_gameplay_tick_hit = 0;
  84. }
  85. // the expanded aabb allows objects to move on most frames
  86. // without needing to determine a change of room
  87. AABB expanded_aabb;
  88. // exact aabb of the object should be used for culling
  89. AABB exact_aabb;
  90. // which is the primary room this moving object is in
  91. // (it may sprawl into multiple rooms)
  92. int32_t room_id;
  93. // id in the allocation pool
  94. uint32_t pool_id;
  95. uint32_t last_tick_hit = 0;
  96. uint32_t last_gameplay_tick_hit = 0;
  97. // room ids of rooms this moving object is sprawled into
  98. LocalVector<uint32_t, int32_t> _rooms;
  99. };
  100. struct Moving : public MovingBase {
  101. // either roaming or global
  102. bool global;
  103. // in _moving_lists .. not the same as pool ID (handle)
  104. uint32_t list_id;
  105. // a void pointer, but this is ultimately a pointer to a VisualServerScene::Instance
  106. // (can't have direct pointer because it is a nested class...)
  107. VSInstance *instance;
  108. #ifdef PORTAL_RENDERER_STORE_MOVING_RIDS
  109. // primarily for testing
  110. RID instance_rid;
  111. #endif
  112. };
  113. // So far the only roaming ghosts are VisibilityNotifiers.
  114. // this will always be roaming... statics and dynamics are handled separately,
  115. // and global ghosts do not get created.
  116. struct RGhost : public MovingBase {
  117. ObjectID object_id;
  118. };
  119. PortalHandle portal_create();
  120. void portal_destroy(PortalHandle p_portal);
  121. void portal_set_geometry(PortalHandle p_portal, const Vector<Vector3> &p_points, real_t p_margin);
  122. void portal_link(PortalHandle p_portal, RoomHandle p_room_from, RoomHandle p_room_to, bool p_two_way);
  123. void portal_set_active(PortalHandle p_portal, bool p_active);
  124. RoomGroupHandle roomgroup_create();
  125. void roomgroup_prepare(RoomGroupHandle p_roomgroup, ObjectID p_roomgroup_object_id);
  126. void roomgroup_destroy(RoomGroupHandle p_roomgroup);
  127. void roomgroup_add_room(RoomGroupHandle p_roomgroup, RoomHandle p_room);
  128. // Rooms
  129. RoomHandle room_create();
  130. void room_destroy(RoomHandle p_room);
  131. OcclusionHandle room_add_instance(RoomHandle p_room, RID p_instance, const AABB &p_aabb, bool p_dynamic, const Vector<Vector3> &p_object_pts);
  132. OcclusionHandle room_add_ghost(RoomHandle p_room, ObjectID p_object_id, const AABB &p_aabb);
  133. void room_set_bound(RoomHandle p_room, ObjectID p_room_object_id, const Vector<Plane> &p_convex, const AABB &p_aabb, const Vector<Vector3> &p_verts);
  134. void room_prepare(RoomHandle p_room, int32_t p_priority);
  135. void rooms_and_portals_clear();
  136. void rooms_finalize(bool p_generate_pvs, bool p_cull_using_pvs, bool p_use_secondary_pvs, bool p_use_signals, String p_pvs_filename, bool p_use_simple_pvs, bool p_log_pvs_generation);
  137. void rooms_override_camera(bool p_override, const Vector3 &p_point, const Vector<Plane> *p_convex);
  138. void rooms_set_active(bool p_active) { _active = p_active; }
  139. void rooms_set_params(int p_portal_depth_limit, real_t p_roaming_expansion_margin) {
  140. _tracer.set_depth_limit(p_portal_depth_limit);
  141. _roaming_expansion_margin = p_roaming_expansion_margin;
  142. }
  143. void rooms_set_cull_using_pvs(bool p_enable) { _cull_using_pvs = p_enable; }
  144. void rooms_update_gameplay_monitor(const Vector<Vector3> &p_camera_positions);
  145. // for use in the editor only, to allow a cheap way of turning off portals
  146. // if there has been a change, e.g. moving a room etc.
  147. void rooms_unload(String p_reason) { _ensure_unloaded(p_reason); }
  148. bool rooms_is_loaded() const { return _loaded; }
  149. // debugging
  150. void set_debug_sprawl(bool p_active) { _debug_sprawl = p_active; }
  151. // this section handles moving objects - roaming (change rooms) and globals (not in any room)
  152. OcclusionHandle instance_moving_create(VSInstance *p_instance, RID p_instance_rid, bool p_global, AABB p_aabb);
  153. void instance_moving_update(OcclusionHandle p_handle, const AABB &p_aabb, bool p_force_reinsert = false);
  154. void instance_moving_destroy(OcclusionHandle p_handle);
  155. // spatial derived roamers (non VisualInstances that still need to be portal culled, especially VisibilityNotifiers)
  156. RGhostHandle rghost_create(ObjectID p_object_id, const AABB &p_aabb);
  157. void rghost_update(RGhostHandle p_handle, const AABB &p_aabb, bool p_force_reinsert = false);
  158. void rghost_destroy(RGhostHandle p_handle);
  159. // occluders
  160. OccluderInstanceHandle occluder_instance_create();
  161. void occluder_instance_link(OccluderInstanceHandle p_handle, OccluderResourceHandle p_resource_handle);
  162. void occluder_instance_set_transform(OccluderInstanceHandle p_handle, const Transform &p_xform);
  163. void occluder_instance_set_active(OccluderInstanceHandle p_handle, bool p_active);
  164. void occluder_instance_destroy(OccluderInstanceHandle p_handle, bool p_free = true);
  165. // editor only .. slow
  166. Geometry::MeshData occlusion_debug_get_current_polys() const { return _tracer.get_occlusion_culler().debug_get_current_polys(); }
  167. // note that this relies on a 'frustum' type cull, from a point, and that the planes are specified as in
  168. // CameraMatrix, i.e.
  169. // order PLANE_NEAR,PLANE_FAR,PLANE_LEFT,PLANE_TOP,PLANE_RIGHT,PLANE_BOTTOM
  170. int cull_convex(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, const Vector<Plane> &p_convex, VSInstance **p_result_array, int p_result_max, uint32_t p_mask, int32_t &r_previous_room_id_hint) {
  171. // combined camera matrix
  172. CameraMatrix cm = CameraMatrix(p_cam_transform.affine_inverse());
  173. cm = p_cam_projection * cm;
  174. Vector3 point = p_cam_transform.origin;
  175. Vector3 cam_dir = -p_cam_transform.basis.get_axis(2).normalized();
  176. if (!_override_camera)
  177. return cull_convex_implementation(point, cam_dir, cm, p_convex, p_result_array, p_result_max, p_mask, r_previous_room_id_hint);
  178. // override camera matrix NYI
  179. return cull_convex_implementation(_override_camera_pos, cam_dir, cm, _override_camera_planes, p_result_array, p_result_max, p_mask, r_previous_room_id_hint);
  180. }
  181. int cull_convex_implementation(const Vector3 &p_point, const Vector3 &p_cam_dir, const CameraMatrix &p_cam_matrix, const Vector<Plane> &p_convex, VSInstance **p_result_array, int p_result_max, uint32_t p_mask, int32_t &r_previous_room_id_hint);
  182. bool occlusion_is_active() const { return _occluder_instance_pool.active_size() && use_occlusion_culling; }
  183. // special function for occlusion culling only that does not use portals / rooms,
  184. // but allows using occluders with the main scene
  185. int occlusion_cull(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, const Vector<Plane> &p_convex, VSInstance **p_result_array, int p_num_results) {
  186. // inactive?
  187. if (!_occluder_instance_pool.active_size() || !use_occlusion_culling) {
  188. return p_num_results;
  189. }
  190. // combined camera matrix
  191. CameraMatrix cm = CameraMatrix(p_cam_transform.affine_inverse());
  192. cm = p_cam_projection * cm;
  193. Vector3 point = p_cam_transform.origin;
  194. Vector3 cam_dir = -p_cam_transform.basis.get_axis(2).normalized();
  195. return _tracer.occlusion_cull(*this, point, cam_dir, cm, p_convex, p_result_array, p_num_results);
  196. }
  197. bool is_active() const { return _active && _loaded; }
  198. VSStatic &get_static(int p_id) { return _statics[p_id]; }
  199. const VSStatic &get_static(int p_id) const { return _statics[p_id]; }
  200. int32_t get_num_rooms() const { return _room_pool_ids.size(); }
  201. VSRoom &get_room(int p_id) { return _room_pool[_room_pool_ids[p_id]]; }
  202. const VSRoom &get_room(int p_id) const { return _room_pool[_room_pool_ids[p_id]]; }
  203. int32_t get_num_portals() const { return _portal_pool_ids.size(); }
  204. VSPortal &get_portal(int p_id) { return _portal_pool[_portal_pool_ids[p_id]]; }
  205. const VSPortal &get_portal(int p_id) const { return _portal_pool[_portal_pool_ids[p_id]]; }
  206. int32_t get_num_moving_globals() const { return _moving_list_global.size(); }
  207. const Moving &get_moving_global(uint32_t p_id) const { return _moving_pool[_moving_list_global[p_id]]; }
  208. Moving &get_pool_moving(uint32_t p_pool_id) { return _moving_pool[p_pool_id]; }
  209. const Moving &get_pool_moving(uint32_t p_pool_id) const { return _moving_pool[p_pool_id]; }
  210. RGhost &get_pool_rghost(uint32_t p_pool_id) { return _rghost_pool[p_pool_id]; }
  211. const RGhost &get_pool_rghost(uint32_t p_pool_id) const { return _rghost_pool[p_pool_id]; }
  212. VSStaticGhost &get_static_ghost(uint32_t p_id) { return _static_ghosts[p_id]; }
  213. VSRoomGroup &get_roomgroup(uint32_t p_pool_id) { return _roomgroup_pool[p_pool_id]; }
  214. PVS &get_pvs() { return _pvs; }
  215. const PVS &get_pvs() const { return _pvs; }
  216. bool get_cull_using_pvs() const { return _cull_using_pvs; }
  217. // occluders
  218. const LocalVector<uint32_t, uint32_t> &get_occluders_active_list() const { return _occluder_instance_pool.get_active_list(); }
  219. const VSOccluder_Instance &get_pool_occluder_instance(uint32_t p_pool_id) const { return _occluder_instance_pool[p_pool_id]; }
  220. VSOccluder_Instance &get_pool_occluder_instance(uint32_t p_pool_id) { return _occluder_instance_pool[p_pool_id]; }
  221. const VSOccluder_Sphere &get_pool_occluder_world_sphere(uint32_t p_pool_id) const { return _occluder_world_sphere_pool[p_pool_id]; }
  222. const VSOccluder_Poly &get_pool_occluder_world_poly(uint32_t p_pool_id) const { return _occluder_world_poly_pool[p_pool_id]; }
  223. const VSOccluder_Hole &get_pool_occluder_world_hole(uint32_t p_pool_id) const { return _occluder_world_hole_pool[p_pool_id]; }
  224. VSOccluder_Hole &get_pool_occluder_world_hole(uint32_t p_pool_id) { return _occluder_world_hole_pool[p_pool_id]; }
  225. private:
  226. int find_room_within(const Vector3 &p_pos, int p_previous_room_id = -1) {
  227. return _rooms_lookup_bsp.find_room_within(*this, p_pos, p_previous_room_id);
  228. }
  229. bool sprawl_static(int p_static_id, const VSStatic &p_static, int p_room_id);
  230. bool sprawl_static_geometry(int p_static_id, const VSStatic &p_static, int p_room_id, const Vector<Vector3> &p_object_pts);
  231. bool sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int p_room_id);
  232. void _load_finalize_roaming();
  233. void sprawl_roaming(uint32_t p_mover_pool_id, MovingBase &r_moving, int p_room_id, bool p_moving_or_ghost);
  234. void _moving_remove_from_rooms(uint32_t p_moving_pool_id);
  235. void _rghost_remove_from_rooms(uint32_t p_pool_id);
  236. void _occluder_remove_from_rooms(uint32_t p_pool_id);
  237. void _ensure_unloaded(String p_reason = String());
  238. void _rooms_add_portals_to_convex_hulls();
  239. void _add_portal_to_convex_hull(LocalVector<Plane, int32_t> &p_planes, const Plane &p);
  240. void _debug_print_global_list();
  241. bool _occlusion_handle_is_in_room(OcclusionHandle p_h) const {
  242. return p_h == OCCLUSION_HANDLE_ROOM_BIT;
  243. }
  244. void _log(String p_string, int p_priority = 0);
  245. // note this is vulnerable to crashes, we must monitor for deletion of rooms
  246. LocalVector<uint32_t, int32_t> _room_pool_ids;
  247. LocalVector<uint32_t, int32_t> _portal_pool_ids;
  248. LocalVector<VSStatic, int32_t> _statics;
  249. LocalVector<VSStaticGhost, int32_t> _static_ghosts;
  250. // all rooms and portals are allocated from pools.
  251. PooledList<VSPortal> _portal_pool;
  252. PooledList<VSRoom> _room_pool;
  253. PooledList<VSRoomGroup> _roomgroup_pool;
  254. // moving objects, global and roaming
  255. PooledList<Moving> _moving_pool;
  256. TrackedPooledList<RGhost> _rghost_pool;
  257. LocalVector<uint32_t, int32_t> _moving_list_global;
  258. LocalVector<uint32_t, int32_t> _moving_list_roaming;
  259. // occluders
  260. TrackedPooledList<VSOccluder_Instance> _occluder_instance_pool;
  261. TrackedPooledList<VSOccluder_Sphere, uint32_t, true> _occluder_world_sphere_pool;
  262. TrackedPooledList<VSOccluder_Poly, uint32_t, true> _occluder_world_poly_pool;
  263. TrackedPooledList<VSOccluder_Hole, uint32_t, true> _occluder_world_hole_pool;
  264. PVS _pvs;
  265. bool _active = true;
  266. bool _loaded = false;
  267. bool _debug_sprawl = false;
  268. bool _show_debug = true;
  269. // if the pvs is generated, we can either cull using dynamic portals or PVS
  270. bool _cull_using_pvs = false;
  271. PortalTracer _tracer;
  272. PortalTracer::TraceResult _trace_results;
  273. PortalRoomsBSP _rooms_lookup_bsp;
  274. PortalGameplayMonitor _gameplay_monitor;
  275. // when moving roaming objects, we expand their bound
  276. // to prevent too many updates.
  277. real_t _roaming_expansion_margin = 1.0;
  278. // a bitfield to indicate which rooms have been
  279. // visited already in sprawling, to prevent visiting rooms multiple times
  280. BitFieldDynamic _bitfield_rooms;
  281. bool _override_camera = false;
  282. Vector3 _override_camera_pos;
  283. LocalVector<Plane, int32_t> _override_camera_planes;
  284. public:
  285. static String _rid_to_string(RID p_rid);
  286. static String _addr_to_string(const void *p_addr);
  287. void occluder_ensure_up_to_date_sphere(const PortalResources &p_resources, VSOccluder_Instance &r_occluder);
  288. void occluder_ensure_up_to_date_polys(const PortalResources &p_resources, VSOccluder_Instance &r_occluder);
  289. void occluder_refresh_room_within(uint32_t p_occluder_pool_id);
  290. PortalRenderer();
  291. };
  292. inline void PortalRenderer::occluder_ensure_up_to_date_sphere(const PortalResources &p_resources, VSOccluder_Instance &r_occluder) {
  293. // occluder is not bound to a resource, cannot be used
  294. if (r_occluder.resource_pool_id == UINT32_MAX) {
  295. return;
  296. }
  297. // get the resource
  298. const VSOccluder_Resource &res = p_resources.get_pool_occluder_resource(r_occluder.resource_pool_id);
  299. // dirty?
  300. if (r_occluder.revision == res.revision) {
  301. return;
  302. }
  303. r_occluder.revision = res.revision;
  304. // must be same type, if not an error has occurred
  305. ERR_FAIL_COND(res.type != r_occluder.type);
  306. // first make sure the instance has the correct number of world space spheres
  307. if (r_occluder.list_ids.size() != res.list_ids.size()) {
  308. // not the most efficient, but works...
  309. // remove existing
  310. for (int n = 0; n < r_occluder.list_ids.size(); n++) {
  311. uint32_t id = r_occluder.list_ids[n];
  312. _occluder_world_sphere_pool.free(id);
  313. }
  314. r_occluder.list_ids.clear();
  315. // create new
  316. for (int n = 0; n < res.list_ids.size(); n++) {
  317. uint32_t id;
  318. VSOccluder_Sphere *sphere = _occluder_world_sphere_pool.request(id);
  319. sphere->create();
  320. r_occluder.list_ids.push_back(id);
  321. }
  322. }
  323. const Transform &tr = r_occluder.xform;
  324. Vector3 scale3 = tr.basis.get_scale_abs();
  325. real_t scale = (scale3.x + scale3.y + scale3.z) / 3.0;
  326. // update the AABB
  327. Vector3 bb_min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
  328. Vector3 bb_max = Vector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
  329. // transform spheres
  330. for (int n = 0; n < r_occluder.list_ids.size(); n++) {
  331. uint32_t world_pool_id = r_occluder.list_ids[n];
  332. VSOccluder_Sphere &world_osphere = _occluder_world_sphere_pool[world_pool_id];
  333. const VSOccluder_Sphere &local_osphere = p_resources.get_pool_occluder_local_sphere(res.list_ids[n]);
  334. world_osphere.pos = tr.xform(local_osphere.pos);
  335. world_osphere.radius = local_osphere.radius * scale;
  336. Vector3 bradius = Vector3(world_osphere.radius, world_osphere.radius, world_osphere.radius);
  337. Vector3 bmin = world_osphere.pos - bradius;
  338. Vector3 bmax = world_osphere.pos + bradius;
  339. bb_min.x = MIN(bb_min.x, bmin.x);
  340. bb_min.y = MIN(bb_min.y, bmin.y);
  341. bb_min.z = MIN(bb_min.z, bmin.z);
  342. bb_max.x = MAX(bb_max.x, bmax.x);
  343. bb_max.y = MAX(bb_max.y, bmax.y);
  344. bb_max.z = MAX(bb_max.z, bmax.z);
  345. }
  346. r_occluder.aabb.position = bb_min;
  347. r_occluder.aabb.size = bb_max - bb_min;
  348. }
  349. inline void PortalRenderer::occluder_ensure_up_to_date_polys(const PortalResources &p_resources, VSOccluder_Instance &r_occluder) {
  350. // occluder is not bound to a resource, cannot be used
  351. if (r_occluder.resource_pool_id == UINT32_MAX) {
  352. return;
  353. }
  354. // get the resource
  355. const VSOccluder_Resource &res = p_resources.get_pool_occluder_resource(r_occluder.resource_pool_id);
  356. // dirty?
  357. if (r_occluder.revision == res.revision) {
  358. return;
  359. }
  360. r_occluder.revision = res.revision;
  361. // must be same type, if not an error has occurred
  362. ERR_FAIL_COND(res.type != r_occluder.type);
  363. // first make sure the instance has the correct number of world space spheres
  364. if (r_occluder.list_ids.size() != res.list_ids.size()) {
  365. // not the most efficient, but works...
  366. // remove existing
  367. for (int n = 0; n < r_occluder.list_ids.size(); n++) {
  368. uint32_t id = r_occluder.list_ids[n];
  369. _occluder_world_poly_pool.free(id);
  370. }
  371. r_occluder.list_ids.clear();
  372. // create new
  373. for (int n = 0; n < res.list_ids.size(); n++) {
  374. uint32_t id;
  375. VSOccluder_Poly *poly = _occluder_world_poly_pool.request(id);
  376. poly->create();
  377. r_occluder.list_ids.push_back(id);
  378. }
  379. }
  380. const Transform &tr = r_occluder.xform;
  381. for (int n = 0; n < r_occluder.list_ids.size(); n++) {
  382. uint32_t world_pool_id = r_occluder.list_ids[n];
  383. uint32_t local_pool_id = res.list_ids[n];
  384. VSOccluder_Poly &world_opoly = _occluder_world_poly_pool[world_pool_id];
  385. const VSOccluder_Poly &local_opoly = p_resources._occluder_local_poly_pool[local_pool_id];
  386. world_opoly.poly.num_verts = local_opoly.poly.num_verts;
  387. world_opoly.two_way = local_opoly.two_way;
  388. for (int i = 0; i < local_opoly.poly.num_verts; i++) {
  389. world_opoly.poly.verts[i] = tr.xform(local_opoly.poly.verts[i]);
  390. }
  391. world_opoly.poly.plane = tr.xform(local_opoly.poly.plane);
  392. // number of holes must be correct for each poly
  393. if (world_opoly.num_holes != local_opoly.num_holes) {
  394. // remove existing
  395. for (int h = 0; h < world_opoly.num_holes; h++) {
  396. uint32_t id = world_opoly.hole_pool_ids[h];
  397. _occluder_world_hole_pool.free(id);
  398. // not strictly necessary
  399. world_opoly.hole_pool_ids[h] = UINT32_MAX;
  400. }
  401. world_opoly.num_holes = local_opoly.num_holes;
  402. for (int h = 0; h < world_opoly.num_holes; h++) {
  403. uint32_t id;
  404. VSOccluder_Hole *hole = _occluder_world_hole_pool.request(id);
  405. hole->create();
  406. world_opoly.hole_pool_ids[h] = id;
  407. }
  408. }
  409. // holes
  410. for (int h = 0; h < world_opoly.num_holes; h++) {
  411. uint32_t world_hid = world_opoly.hole_pool_ids[h];
  412. uint32_t local_hid = local_opoly.hole_pool_ids[h];
  413. VSOccluder_Hole &world_hole = _occluder_world_hole_pool[world_hid];
  414. const VSOccluder_Hole &local_hole = p_resources._occluder_local_hole_pool[local_hid];
  415. world_hole.num_verts = local_hole.num_verts;
  416. for (int i = 0; i < world_hole.num_verts; i++) {
  417. world_hole.verts[i] = tr.xform(local_hole.verts[i]);
  418. }
  419. }
  420. }
  421. }
  422. #endif // PORTAL_RENDERER_H