| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*  portal_renderer.cpp                                                  */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							|  |  |  | /*                      https://godotengine.org                          */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							|  |  |  | /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* Permission is hereby granted, free of charge, to any person obtaining */ | 
					
						
							|  |  |  | /* a copy of this software and associated documentation files (the       */ | 
					
						
							|  |  |  | /* "Software"), to deal in the Software without restriction, including   */ | 
					
						
							|  |  |  | /* without limitation the rights to use, copy, modify, merge, publish,   */ | 
					
						
							|  |  |  | /* distribute, sublicense, and/or sell copies of the Software, and to    */ | 
					
						
							|  |  |  | /* permit persons to whom the Software is furnished to do so, subject to */ | 
					
						
							|  |  |  | /* the following conditions:                                             */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* The above copyright notice and this permission notice shall be        */ | 
					
						
							|  |  |  | /* included in all copies or substantial portions of the Software.       */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | 
					
						
							|  |  |  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | 
					
						
							|  |  |  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | 
					
						
							|  |  |  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | 
					
						
							|  |  |  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | 
					
						
							|  |  |  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | 
					
						
							|  |  |  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "portal_renderer.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "portal_pvs_builder.h"
 | 
					
						
							|  |  |  | #include "servers/visual/visual_server_globals.h"
 | 
					
						
							|  |  |  | #include "servers/visual/visual_server_scene.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 15:44:43 +01:00
										 |  |  | bool PortalRenderer::use_occlusion_culling = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | OcclusionHandle PortalRenderer::instance_moving_create(VSInstance *p_instance, RID p_instance_rid, bool p_global, AABB p_aabb) { | 
					
						
							|  |  |  | 	uint32_t pool_id = 0; | 
					
						
							|  |  |  | 	Moving *moving = _moving_pool.request(pool_id); | 
					
						
							|  |  |  | 	moving->global = p_global; | 
					
						
							|  |  |  | 	moving->pool_id = pool_id; | 
					
						
							|  |  |  | 	moving->instance = p_instance; | 
					
						
							|  |  |  | 	moving->room_id = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef PORTAL_RENDERER_STORE_MOVING_RIDS
 | 
					
						
							|  |  |  | 	moving->instance_rid = p_instance_rid; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add to the appropriate list
 | 
					
						
							|  |  |  | 	if (p_global) { | 
					
						
							|  |  |  | 		moving->list_id = _moving_list_global.size(); | 
					
						
							|  |  |  | 		_moving_list_global.push_back(pool_id); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// do we need a roaming master list? not sure yet
 | 
					
						
							|  |  |  | 		moving->list_id = _moving_list_roaming.size(); | 
					
						
							|  |  |  | 		_moving_list_roaming.push_back(pool_id); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	OcclusionHandle handle = pool_id + 1; | 
					
						
							|  |  |  | 	instance_moving_update(handle, p_aabb); | 
					
						
							|  |  |  | 	return handle; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::instance_moving_update(OcclusionHandle p_handle, const AABB &p_aabb, bool p_force_reinsert) { | 
					
						
							|  |  |  | 	// we can ignore these, they are statics / dynamics, and don't need updating
 | 
					
						
							|  |  |  | 	// .. these should have been filtered out before calling the visual server...
 | 
					
						
							|  |  |  | 	DEV_ASSERT(!_occlusion_handle_is_in_room(p_handle)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 	Moving &moving = _moving_pool[p_handle]; | 
					
						
							|  |  |  | 	moving.exact_aabb = p_aabb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// globals (e.g. interface elements) need their aabb updated irrespective of whether the system is loaded
 | 
					
						
							|  |  |  | 	if (!_loaded || moving.global) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// quick reject for most roaming cases
 | 
					
						
							|  |  |  | 	if (!p_force_reinsert && moving.expanded_aabb.encloses(p_aabb)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// using an expanded aabb allows us to make 'no op' moves
 | 
					
						
							|  |  |  | 	// where the new aabb is within the expanded
 | 
					
						
							|  |  |  | 	moving.expanded_aabb = p_aabb.grow(_roaming_expansion_margin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if we got to here, it is roaming (moving between rooms)
 | 
					
						
							|  |  |  | 	// remove from current rooms
 | 
					
						
							|  |  |  | 	_moving_remove_from_rooms(p_handle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add to new rooms
 | 
					
						
							|  |  |  | 	Vector3 center = p_aabb.position + (p_aabb.size * 0.5); | 
					
						
							|  |  |  | 	int new_room = find_room_within(center, moving.room_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	moving.room_id = new_room; | 
					
						
							|  |  |  | 	if (new_room != -1) { | 
					
						
							|  |  |  | 		_bitfield_rooms.blank(); | 
					
						
							|  |  |  | 		sprawl_roaming(p_handle, moving, new_room, true); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::_rghost_remove_from_rooms(uint32_t p_pool_id) { | 
					
						
							|  |  |  | 	RGhost &moving = _rghost_pool[p_pool_id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if we have unloaded the rooms and we try this, it will crash
 | 
					
						
							|  |  |  | 	if (_loaded) { | 
					
						
							|  |  |  | 		for (int n = 0; n < moving._rooms.size(); n++) { | 
					
						
							|  |  |  | 			VSRoom &room = get_room(moving._rooms[n]); | 
					
						
							|  |  |  | 			room.remove_rghost(p_pool_id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// moving is now in no rooms
 | 
					
						
							|  |  |  | 	moving._rooms.clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 15:44:43 +01:00
										 |  |  | void PortalRenderer::_occluder_remove_from_rooms(uint32_t p_pool_id) { | 
					
						
							|  |  |  | 	VSOccluder &occ = _occluder_pool[p_pool_id]; | 
					
						
							|  |  |  | 	if (_loaded && (occ.room_id != -1)) { | 
					
						
							|  |  |  | 		VSRoom &room = get_room(occ.room_id); | 
					
						
							|  |  |  | 		room.remove_occluder(p_pool_id); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | void PortalRenderer::_moving_remove_from_rooms(uint32_t p_moving_pool_id) { | 
					
						
							|  |  |  | 	Moving &moving = _moving_pool[p_moving_pool_id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if we have unloaded the rooms and we try this, it will crash
 | 
					
						
							|  |  |  | 	if (_loaded) { | 
					
						
							|  |  |  | 		for (int n = 0; n < moving._rooms.size(); n++) { | 
					
						
							|  |  |  | 			VSRoom &room = get_room(moving._rooms[n]); | 
					
						
							|  |  |  | 			room.remove_roamer(p_moving_pool_id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// moving is now in no rooms
 | 
					
						
							|  |  |  | 	moving._rooms.clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::_debug_print_global_list() { | 
					
						
							|  |  |  | 	_log("globals:"); | 
					
						
							|  |  |  | 	for (int n = 0; n < _moving_list_global.size(); n++) { | 
					
						
							|  |  |  | 		uint32_t id = _moving_list_global[n]; | 
					
						
							|  |  |  | 		const Moving &moving = _moving_pool[id]; | 
					
						
							|  |  |  | 		_log("\t" + _addr_to_string(&moving)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::_log(String p_string, int p_priority) { | 
					
						
							|  |  |  | 	// change this for more debug output ..
 | 
					
						
							|  |  |  | 	// not selectable at runtime yet.
 | 
					
						
							|  |  |  | 	if (p_priority >= 1) { | 
					
						
							|  |  |  | 		print_line(p_string); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		print_verbose(p_string); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::instance_moving_destroy(OcclusionHandle p_handle) { | 
					
						
							|  |  |  | 	// deleting an instance that is assigned to a room (STATIC or DYNAMIC)
 | 
					
						
							|  |  |  | 	// is special, it must set the PortalRenderer into unloaded state, because
 | 
					
						
							|  |  |  | 	// there will now be a dangling reference to the instance that was destroyed.
 | 
					
						
							|  |  |  | 	// The alternative is to remove the reference, but this is not currently supported
 | 
					
						
							|  |  |  | 	// (it would mean rejigging rooms etc)
 | 
					
						
							|  |  |  | 	if (_occlusion_handle_is_in_room(p_handle)) { | 
					
						
							|  |  |  | 		_ensure_unloaded(); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Moving *moving = &_moving_pool[p_handle]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if a roamer, remove from any current rooms
 | 
					
						
							|  |  |  | 	if (!moving->global) { | 
					
						
							|  |  |  | 		_moving_remove_from_rooms(p_handle); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// remove from list (and keep in sync)
 | 
					
						
							|  |  |  | 	uint32_t list_id = moving->list_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (moving->global) { | 
					
						
							|  |  |  | 		_moving_list_global.remove_unordered(list_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// keep the replacement moving in sync with the correct list Id
 | 
					
						
							|  |  |  | 		if (list_id < (uint32_t)_moving_list_global.size()) { | 
					
						
							|  |  |  | 			uint32_t replacement_id = _moving_list_global[list_id]; | 
					
						
							|  |  |  | 			Moving &replacement = _moving_pool[replacement_id]; | 
					
						
							|  |  |  | 			replacement.list_id = list_id; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		_moving_list_roaming.remove_unordered(list_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// keep the replacement moving in sync with the correct list Id
 | 
					
						
							|  |  |  | 		if (list_id < (uint32_t)_moving_list_roaming.size()) { | 
					
						
							|  |  |  | 			uint32_t replacement_id = _moving_list_roaming[list_id]; | 
					
						
							|  |  |  | 			Moving &replacement = _moving_pool[replacement_id]; | 
					
						
							|  |  |  | 			replacement.list_id = list_id; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	moving->destroy(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// can now free the moving
 | 
					
						
							|  |  |  | 	_moving_pool.free(p_handle); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PortalHandle PortalRenderer::portal_create() { | 
					
						
							|  |  |  | 	uint32_t pool_id = 0; | 
					
						
							|  |  |  | 	VSPortal *portal = _portal_pool.request(pool_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// explicit constructor
 | 
					
						
							|  |  |  | 	portal->create(); | 
					
						
							|  |  |  | 	portal->_portal_id = _portal_pool_ids.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_portal_pool_ids.push_back(pool_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// plus one based handles, 0 is unset
 | 
					
						
							|  |  |  | 	pool_id++; | 
					
						
							|  |  |  | 	return pool_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::portal_destroy(PortalHandle p_portal) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_portal); | 
					
						
							|  |  |  | 	_ensure_unloaded(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// plus one based
 | 
					
						
							|  |  |  | 	p_portal--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// remove from list of valid portals
 | 
					
						
							|  |  |  | 	VSPortal &portal = _portal_pool[p_portal]; | 
					
						
							|  |  |  | 	int portal_id = portal._portal_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// we need to replace the last element in the list
 | 
					
						
							|  |  |  | 	_portal_pool_ids.remove_unordered(portal_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// and reset the id of the portal that was the replacement
 | 
					
						
							|  |  |  | 	if (portal_id < _portal_pool_ids.size()) { | 
					
						
							|  |  |  | 		int replacement_pool_id = _portal_pool_ids[portal_id]; | 
					
						
							|  |  |  | 		VSPortal &replacement = _portal_pool[replacement_pool_id]; | 
					
						
							|  |  |  | 		replacement._portal_id = portal_id; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// explicitly run destructor
 | 
					
						
							|  |  |  | 	_portal_pool[p_portal].destroy(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// return to the pool
 | 
					
						
							|  |  |  | 	_portal_pool.free(p_portal); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-29 11:57:32 +01:00
										 |  |  | void PortalRenderer::portal_set_geometry(PortalHandle p_portal, const Vector<Vector3> &p_points, real_t p_margin) { | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	ERR_FAIL_COND(!p_portal); | 
					
						
							|  |  |  | 	p_portal--; // plus 1 based
 | 
					
						
							|  |  |  | 	VSPortal &portal = _portal_pool[p_portal]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	portal._pts_world = p_points; | 
					
						
							| 
									
										
										
										
											2021-07-29 11:57:32 +01:00
										 |  |  | 	portal._margin = p_margin; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (portal._pts_world.size() < 3) { | 
					
						
							|  |  |  | 		WARN_PRINT("Portal must have at least 3 vertices"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create plane from points
 | 
					
						
							|  |  |  | 	// Allow averaging in case of wonky portals.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// first calculate average normal
 | 
					
						
							|  |  |  | 	Vector3 average_normal = Vector3(0, 0, 0); | 
					
						
							|  |  |  | 	for (int t = 2; t < (int)portal._pts_world.size(); t++) { | 
					
						
							|  |  |  | 		Plane p = Plane(portal._pts_world[0], portal._pts_world[t - 1], portal._pts_world[t]); | 
					
						
							|  |  |  | 		average_normal += p.normal; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// average normal
 | 
					
						
							|  |  |  | 	average_normal /= portal._pts_world.size() - 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// detect user error
 | 
					
						
							|  |  |  | 	ERR_FAIL_COND_MSG(average_normal.length() < 0.1, "Nonsense portal detected, normals should be consistent"); | 
					
						
							|  |  |  | 	if (average_normal.length() < 0.7) { | 
					
						
							|  |  |  | 		WARN_PRINT("Wonky portal detected, you may see culling errors"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// calc average point
 | 
					
						
							|  |  |  | 	Vector3 average_pt = Vector3(0, 0, 0); | 
					
						
							|  |  |  | 	for (unsigned int n = 0; n < portal._pts_world.size(); n++) { | 
					
						
							|  |  |  | 		average_pt += portal._pts_world[n]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	average_pt /= portal._pts_world.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 17:57:30 +01:00
										 |  |  | 	// record the center for use in PVS
 | 
					
						
							|  |  |  | 	portal._pt_center = average_pt; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 15:44:43 +01:00
										 |  |  | 	// calculate bounding sphere radius
 | 
					
						
							|  |  |  | 	portal._bounding_sphere_radius = 0.0; | 
					
						
							|  |  |  | 	for (unsigned int n = 0; n < portal._pts_world.size(); n++) { | 
					
						
							|  |  |  | 		real_t sl = (portal._pts_world[n] - average_pt).length_squared(); | 
					
						
							|  |  |  | 		if (sl > portal._bounding_sphere_radius) { | 
					
						
							|  |  |  | 			portal._bounding_sphere_radius = sl; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	portal._bounding_sphere_radius = Math::sqrt(portal._bounding_sphere_radius); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// use the average point and normal to derive the plane
 | 
					
						
							|  |  |  | 	portal._plane = Plane(average_pt, average_normal); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// aabb
 | 
					
						
							|  |  |  | 	AABB &bb = portal._aabb; | 
					
						
							|  |  |  | 	bb.position = p_points[0]; | 
					
						
							|  |  |  | 	bb.size = Vector3(0, 0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 1; n < p_points.size(); n++) { | 
					
						
							|  |  |  | 		bb.expand_to(p_points[n]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::portal_link(PortalHandle p_portal, RoomHandle p_room_from, RoomHandle p_room_to, bool p_two_way) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_portal); | 
					
						
							|  |  |  | 	p_portal--; // plus 1 based
 | 
					
						
							|  |  |  | 	VSPortal &portal = _portal_pool[p_portal]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_room_from); | 
					
						
							|  |  |  | 	p_room_from--; | 
					
						
							|  |  |  | 	VSRoom &room_from = _room_pool[p_room_from]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_room_to); | 
					
						
							|  |  |  | 	p_room_to--; | 
					
						
							|  |  |  | 	VSRoom &room_to = _room_pool[p_room_to]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	portal._linkedroom_ID[0] = room_from._room_ID; | 
					
						
							|  |  |  | 	portal._linkedroom_ID[1] = room_to._room_ID; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// is the portal internal? internal portals are treated differently
 | 
					
						
							|  |  |  | 	portal._internal = room_from._priority > room_to._priority; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if it is internal, mark the outer room as containing an internal room.
 | 
					
						
							|  |  |  | 	// this is used for rooms lookup.
 | 
					
						
							|  |  |  | 	if (portal._internal) { | 
					
						
							|  |  |  | 		room_to._contains_internal_rooms = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 	// _log("portal_link from room " + itos(room_from._room_ID) + " to room " + itos(room_to._room_ID));
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	room_from._portal_ids.push_back(portal._portal_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// one way portals simply aren't added to the destination room, so they don't get seen through
 | 
					
						
							|  |  |  | 	if (p_two_way) { | 
					
						
							|  |  |  | 		room_to._portal_ids.push_back(portal._portal_id); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::portal_set_active(PortalHandle p_portal, bool p_active) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_portal); | 
					
						
							|  |  |  | 	p_portal--; // plus 1 based
 | 
					
						
							|  |  |  | 	VSPortal &portal = _portal_pool[p_portal]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	portal._active = p_active; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RoomGroupHandle PortalRenderer::roomgroup_create() { | 
					
						
							|  |  |  | 	uint32_t pool_id = 0; | 
					
						
							|  |  |  | 	VSRoomGroup *rg = _roomgroup_pool.request(pool_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// explicit constructor
 | 
					
						
							|  |  |  | 	rg->create(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// plus one based handles, 0 is unset
 | 
					
						
							|  |  |  | 	pool_id++; | 
					
						
							|  |  |  | 	return pool_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::roomgroup_prepare(RoomGroupHandle p_roomgroup, ObjectID p_roomgroup_object_id) { | 
					
						
							|  |  |  | 	// plus one based
 | 
					
						
							|  |  |  | 	p_roomgroup--; | 
					
						
							|  |  |  | 	VSRoomGroup &rg = _roomgroup_pool[p_roomgroup]; | 
					
						
							|  |  |  | 	rg._godot_instance_ID = p_roomgroup_object_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::roomgroup_destroy(RoomGroupHandle p_roomgroup) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_roomgroup); | 
					
						
							|  |  |  | 	_ensure_unloaded(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// plus one based
 | 
					
						
							|  |  |  | 	p_roomgroup--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	VSRoomGroup &rg = _roomgroup_pool[p_roomgroup]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// explicitly run destructor
 | 
					
						
							|  |  |  | 	rg.destroy(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// return to the pool
 | 
					
						
							|  |  |  | 	_roomgroup_pool.free(p_roomgroup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::roomgroup_add_room(RoomGroupHandle p_roomgroup, RoomHandle p_room) { | 
					
						
							|  |  |  | 	// plus one based
 | 
					
						
							|  |  |  | 	p_roomgroup--; | 
					
						
							|  |  |  | 	VSRoomGroup &rg = _roomgroup_pool[p_roomgroup]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	p_room--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add to room group
 | 
					
						
							|  |  |  | 	rg._room_ids.push_back(p_room); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add the room group to the room
 | 
					
						
							|  |  |  | 	VSRoom &room = _room_pool[p_room]; | 
					
						
							|  |  |  | 	room._roomgroup_ids.push_back(p_roomgroup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cull Instances
 | 
					
						
							|  |  |  | RGhostHandle PortalRenderer::rghost_create(ObjectID p_object_id, const AABB &p_aabb) { | 
					
						
							|  |  |  | 	uint32_t pool_id = 0; | 
					
						
							|  |  |  | 	RGhost *moving = _rghost_pool.request(pool_id); | 
					
						
							|  |  |  | 	moving->pool_id = pool_id; | 
					
						
							|  |  |  | 	moving->object_id = p_object_id; | 
					
						
							|  |  |  | 	moving->room_id = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RGhostHandle handle = pool_id + 1; | 
					
						
							|  |  |  | 	rghost_update(handle, p_aabb); | 
					
						
							|  |  |  | 	return handle; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::rghost_update(RGhostHandle p_handle, const AABB &p_aabb, bool p_force_reinsert) { | 
					
						
							|  |  |  | 	if (!_loaded) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 	RGhost &moving = _rghost_pool[p_handle]; | 
					
						
							|  |  |  | 	moving.exact_aabb = p_aabb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// quick reject for most roaming cases
 | 
					
						
							|  |  |  | 	if (!p_force_reinsert && moving.expanded_aabb.encloses(p_aabb)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// using an expanded aabb allows us to make 'no op' moves
 | 
					
						
							|  |  |  | 	// where the new aabb is within the expanded
 | 
					
						
							|  |  |  | 	moving.expanded_aabb = p_aabb.grow(_roaming_expansion_margin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if we got to here, it is roaming (moving between rooms)
 | 
					
						
							|  |  |  | 	// remove from current rooms
 | 
					
						
							|  |  |  | 	_rghost_remove_from_rooms(p_handle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add to new rooms
 | 
					
						
							|  |  |  | 	Vector3 center = p_aabb.position + (p_aabb.size * 0.5); | 
					
						
							|  |  |  | 	int new_room = find_room_within(center, moving.room_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	moving.room_id = new_room; | 
					
						
							|  |  |  | 	if (new_room != -1) { | 
					
						
							|  |  |  | 		_bitfield_rooms.blank(); | 
					
						
							|  |  |  | 		sprawl_roaming(p_handle, moving, new_room, false); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::rghost_destroy(RGhostHandle p_handle) { | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RGhost *moving = &_rghost_pool[p_handle]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if a roamer, remove from any current rooms
 | 
					
						
							|  |  |  | 	_rghost_remove_from_rooms(p_handle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	moving->destroy(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// can now free the moving
 | 
					
						
							|  |  |  | 	_rghost_pool.free(p_handle); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 15:44:43 +01:00
										 |  |  | OccluderHandle PortalRenderer::occluder_create(VSOccluder::Type p_type) { | 
					
						
							|  |  |  | 	uint32_t pool_id = 0; | 
					
						
							|  |  |  | 	VSOccluder *occ = _occluder_pool.request(pool_id); | 
					
						
							|  |  |  | 	occ->create(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// specific type
 | 
					
						
							|  |  |  | 	occ->type = p_type; | 
					
						
							|  |  |  | 	CRASH_COND(p_type == VSOccluder::OT_UNDEFINED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	OccluderHandle handle = pool_id + 1; | 
					
						
							|  |  |  | 	return handle; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::occluder_set_active(OccluderHandle p_handle, bool p_active) { | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 	VSOccluder &occ = _occluder_pool[p_handle]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (occ.active == p_active) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	occ.active = p_active; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// this will take care of adding or removing from rooms
 | 
					
						
							|  |  |  | 	occluder_refresh_room_within(p_handle); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::occluder_set_transform(OccluderHandle p_handle, const Transform &p_xform) { | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 	VSOccluder &occ = _occluder_pool[p_handle]; | 
					
						
							|  |  |  | 	occ.xform = p_xform; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mark as dirty as the world space spheres will be out of date
 | 
					
						
							|  |  |  | 	occ.dirty = true; | 
					
						
							|  |  |  | 	occluder_refresh_room_within(p_handle); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::occluder_refresh_room_within(uint32_t p_occluder_pool_id) { | 
					
						
							|  |  |  | 	VSOccluder &occ = _occluder_pool[p_occluder_pool_id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// if we aren't loaded, the room within can't be valid
 | 
					
						
							|  |  |  | 	if (!_loaded) { | 
					
						
							|  |  |  | 		occ.room_id = -1; | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// inactive?
 | 
					
						
							|  |  |  | 	if (!occ.active) { | 
					
						
							|  |  |  | 		// remove from any rooms present in
 | 
					
						
							|  |  |  | 		if (occ.room_id != -1) { | 
					
						
							|  |  |  | 			_occluder_remove_from_rooms(p_occluder_pool_id); | 
					
						
							|  |  |  | 			occ.room_id = -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// prevent checks with no significant changes
 | 
					
						
							|  |  |  | 	Vector3 offset = occ.xform.origin - occ.pt_center; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// could possibly make this epsilon editable?
 | 
					
						
							|  |  |  | 	// is highly world size dependent.
 | 
					
						
							|  |  |  | 	if ((offset.length_squared() < 0.01) && (occ.room_id != -1)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// standardize on the node origin for now
 | 
					
						
							|  |  |  | 	occ.pt_center = occ.xform.origin; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int new_room = find_room_within(occ.pt_center, occ.room_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (new_room != occ.room_id) { | 
					
						
							|  |  |  | 		_occluder_remove_from_rooms(p_occluder_pool_id); | 
					
						
							|  |  |  | 		occ.room_id = new_room; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (new_room != -1) { | 
					
						
							|  |  |  | 			VSRoom &room = get_room(new_room); | 
					
						
							|  |  |  | 			room.add_occluder(p_occluder_pool_id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::occluder_update_spheres(OccluderHandle p_handle, const Vector<Plane> &p_spheres) { | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 	VSOccluder &occ = _occluder_pool[p_handle]; | 
					
						
							|  |  |  | 	ERR_FAIL_COND(occ.type != VSOccluder::OT_SPHERE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// first deal with the situation where the number of spheres has changed (rare)
 | 
					
						
							|  |  |  | 	if (occ.list_ids.size() != p_spheres.size()) { | 
					
						
							|  |  |  | 		// not the most efficient, but works...
 | 
					
						
							|  |  |  | 		// remove existing
 | 
					
						
							|  |  |  | 		for (int n = 0; n < occ.list_ids.size(); n++) { | 
					
						
							|  |  |  | 			uint32_t id = occ.list_ids[n]; | 
					
						
							|  |  |  | 			_occluder_sphere_pool.free(id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		occ.list_ids.clear(); | 
					
						
							|  |  |  | 		// create new
 | 
					
						
							|  |  |  | 		for (int n = 0; n < p_spheres.size(); n++) { | 
					
						
							|  |  |  | 			uint32_t id; | 
					
						
							|  |  |  | 			VSOccluder_Sphere *sphere = _occluder_sphere_pool.request(id); | 
					
						
							|  |  |  | 			sphere->create(); | 
					
						
							|  |  |  | 			occ.list_ids.push_back(id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// new positions
 | 
					
						
							|  |  |  | 	for (int n = 0; n < occ.list_ids.size(); n++) { | 
					
						
							|  |  |  | 		uint32_t id = occ.list_ids[n]; | 
					
						
							|  |  |  | 		VSOccluder_Sphere &sphere = _occluder_sphere_pool[id]; | 
					
						
							|  |  |  | 		sphere.local.from_plane(p_spheres[n]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mark as dirty as the world space spheres will be out of date
 | 
					
						
							|  |  |  | 	occ.dirty = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::occluder_destroy(OccluderHandle p_handle) { | 
					
						
							|  |  |  | 	p_handle--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// depending on the occluder type, remove the spheres etc
 | 
					
						
							|  |  |  | 	VSOccluder &occ = _occluder_pool[p_handle]; | 
					
						
							|  |  |  | 	switch (occ.type) { | 
					
						
							|  |  |  | 		case VSOccluder::OT_SPHERE: { | 
					
						
							|  |  |  | 			occluder_update_spheres(p_handle + 1, Vector<Plane>()); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		default: { | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_occluder_remove_from_rooms(p_handle); | 
					
						
							|  |  |  | 	_occluder_pool.free(p_handle); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | // Rooms
 | 
					
						
							|  |  |  | RoomHandle PortalRenderer::room_create() { | 
					
						
							|  |  |  | 	uint32_t pool_id = 0; | 
					
						
							|  |  |  | 	VSRoom *room = _room_pool.request(pool_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// explicit constructor
 | 
					
						
							|  |  |  | 	room->create(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// keep our own internal list of rooms
 | 
					
						
							|  |  |  | 	room->_room_ID = _room_pool_ids.size(); | 
					
						
							|  |  |  | 	_room_pool_ids.push_back(pool_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// plus one based handles, 0 is unset
 | 
					
						
							|  |  |  | 	pool_id++; | 
					
						
							|  |  |  | 	return pool_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::room_destroy(RoomHandle p_room) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_room); | 
					
						
							|  |  |  | 	_ensure_unloaded(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// plus one based
 | 
					
						
							|  |  |  | 	p_room--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// remove from list of valid rooms
 | 
					
						
							|  |  |  | 	VSRoom &room = _room_pool[p_room]; | 
					
						
							|  |  |  | 	int room_id = room._room_ID; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// we need to replace the last element in the list
 | 
					
						
							|  |  |  | 	_room_pool_ids.remove_unordered(room_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// and reset the id of the portal that was the replacement
 | 
					
						
							|  |  |  | 	if (room_id < _room_pool_ids.size()) { | 
					
						
							|  |  |  | 		int replacement_pool_id = _room_pool_ids[room_id]; | 
					
						
							|  |  |  | 		VSRoom &replacement = _room_pool[replacement_pool_id]; | 
					
						
							|  |  |  | 		replacement._room_ID = room_id; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// explicitly run destructor
 | 
					
						
							|  |  |  | 	_room_pool[p_room].destroy(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// return to the pool
 | 
					
						
							|  |  |  | 	_room_pool.free(p_room); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OcclusionHandle PortalRenderer::room_add_ghost(RoomHandle p_room, ObjectID p_object_id, const AABB &p_aabb) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(!p_room, 0); | 
					
						
							|  |  |  | 	p_room--; // plus one based
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	VSStaticGhost ghost; | 
					
						
							|  |  |  | 	ghost.object_id = p_object_id; | 
					
						
							|  |  |  | 	_static_ghosts.push_back(ghost); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// sprawl immediately
 | 
					
						
							|  |  |  | 	// precreate a useful bitfield of rooms for use in sprawling
 | 
					
						
							|  |  |  | 	if ((int)_bitfield_rooms.get_num_bits() != get_num_rooms()) { | 
					
						
							|  |  |  | 		_bitfield_rooms.create(get_num_rooms()); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// only can do if rooms exist
 | 
					
						
							|  |  |  | 	if (get_num_rooms()) { | 
					
						
							|  |  |  | 		// the last one was just added
 | 
					
						
							|  |  |  | 		int ghost_id = _static_ghosts.size() - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// create a bitfield to indicate which rooms have been
 | 
					
						
							|  |  |  | 		// visited already, to prevent visiting rooms multiple times
 | 
					
						
							|  |  |  | 		_bitfield_rooms.blank(); | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 		if (sprawl_static_ghost(ghost_id, p_aabb, p_room)) { | 
					
						
							|  |  |  | 			_log("\t\tSPRAWLED"); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return OCCLUSION_HANDLE_ROOM_BIT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OcclusionHandle PortalRenderer::room_add_instance(RoomHandle p_room, RID p_instance, const AABB &p_aabb, bool p_dynamic, const Vector<Vector3> &p_object_pts) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(!p_room, 0); | 
					
						
							|  |  |  | 	p_room--; // plus one based
 | 
					
						
							|  |  |  | 	VSRoom &room = _room_pool[p_room]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	VSStatic stat; | 
					
						
							|  |  |  | 	stat.instance = p_instance; | 
					
						
							|  |  |  | 	stat.source_room_id = room._room_ID; | 
					
						
							|  |  |  | 	stat.dynamic = p_dynamic; | 
					
						
							|  |  |  | 	stat.aabb = p_aabb; | 
					
						
							|  |  |  | 	_statics.push_back(stat); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// sprawl immediately
 | 
					
						
							|  |  |  | 	// precreate a useful bitfield of rooms for use in sprawling
 | 
					
						
							|  |  |  | 	if ((int)_bitfield_rooms.get_num_bits() != get_num_rooms()) { | 
					
						
							|  |  |  | 		_bitfield_rooms.create(get_num_rooms()); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// only can do if rooms exist
 | 
					
						
							|  |  |  | 	if (get_num_rooms()) { | 
					
						
							|  |  |  | 		// the last one was just added
 | 
					
						
							|  |  |  | 		int static_id = _statics.size() - 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// pop last static
 | 
					
						
							|  |  |  | 		const VSStatic &st = _statics[static_id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// create a bitfield to indicate which rooms have been
 | 
					
						
							|  |  |  | 		// visited already, to prevent visiting rooms multiple times
 | 
					
						
							|  |  |  | 		_bitfield_rooms.blank(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (p_object_pts.size()) { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			if (sprawl_static_geometry(static_id, st, st.source_room_id, p_object_pts)) { | 
					
						
							|  |  |  | 				_log("\t\tSPRAWLED"); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			if (sprawl_static(static_id, st, st.source_room_id)) { | 
					
						
							|  |  |  | 				_log("\t\tSPRAWLED"); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return OCCLUSION_HANDLE_ROOM_BIT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::room_prepare(RoomHandle p_room, int32_t p_priority) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_room); | 
					
						
							|  |  |  | 	p_room--; // plus one based
 | 
					
						
							|  |  |  | 	VSRoom &room = _room_pool[p_room]; | 
					
						
							|  |  |  | 	room._priority = p_priority; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::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) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!p_room); | 
					
						
							|  |  |  | 	p_room--; // plus one based
 | 
					
						
							|  |  |  | 	VSRoom &room = _room_pool[p_room]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	room._planes = p_convex; | 
					
						
							|  |  |  | 	room._verts = p_verts; | 
					
						
							|  |  |  | 	room._aabb = p_aabb; | 
					
						
							|  |  |  | 	room._godot_instance_ID = p_room_object_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::_add_portal_to_convex_hull(LocalVector<Plane, int32_t> &p_planes, const Plane &p) { | 
					
						
							|  |  |  | 	for (int n = 0; n < p_planes.size(); n++) { | 
					
						
							|  |  |  | 		Plane &o = p_planes[n]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// this is a fudge factor for how close the portal can be to an existing plane
 | 
					
						
							|  |  |  | 		// to be to be considered the same ...
 | 
					
						
							|  |  |  | 		// to prevent needless extra checks.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// the epsilons should probably be more exact here than for the convex hull simplification, as it is
 | 
					
						
							|  |  |  | 		// fairly crucial that the portal planes are reasonably accurate for determining the hull.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// and because the portal plane is more important, we will REPLACE the existing similar plane
 | 
					
						
							|  |  |  | 		// with the portal plane.
 | 
					
						
							|  |  |  | 		const real_t d = 0.03; // 0.08f
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (Math::abs(p.d - o.d) > d) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		real_t dot = p.normal.dot(o.normal); | 
					
						
							|  |  |  | 		if (dot < 0.99) // 0.98f
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// match!
 | 
					
						
							|  |  |  | 		// replace the existing plane
 | 
					
						
							|  |  |  | 		o = p; | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// there is no existing plane that is similar, create a new one especially for the portal
 | 
					
						
							|  |  |  | 	p_planes.push_back(p); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::_rooms_add_portals_to_convex_hulls() { | 
					
						
							|  |  |  | 	for (int n = 0; n < get_num_rooms(); n++) { | 
					
						
							|  |  |  | 		VSRoom &room = get_room(n); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (int p = 0; p < room._portal_ids.size(); p++) { | 
					
						
							|  |  |  | 			const VSPortal &portal = get_portal(room._portal_ids[p]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// everything depends on whether the portal is incoming or outgoing.
 | 
					
						
							|  |  |  | 			// if incoming we reverse the logic.
 | 
					
						
							|  |  |  | 			int outgoing = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			int room_a_id = portal._linkedroom_ID[0]; | 
					
						
							|  |  |  | 			if (room_a_id != n) { | 
					
						
							|  |  |  | 				outgoing = 0; | 
					
						
							|  |  |  | 				DEV_ASSERT(portal._linkedroom_ID[1] == n); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// do not add internal portals to the convex hull of outer rooms!
 | 
					
						
							|  |  |  | 			if (!outgoing && portal._internal) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// add the portal plane
 | 
					
						
							|  |  |  | 			Plane portal_plane = portal._plane; | 
					
						
							|  |  |  | 			if (!outgoing) { | 
					
						
							|  |  |  | 				portal_plane = -portal_plane; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// add if sufficiently different from existing convex hull planes
 | 
					
						
							|  |  |  | 			_add_portal_to_convex_hull(room._planes, portal_plane); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 19:31:02 +01:00
										 |  |  | void PortalRenderer::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) { | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	_gameplay_monitor.set_params(p_use_secondary_pvs, p_use_signals); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// portals should also bound the rooms, the room geometry may extend past the portal
 | 
					
						
							|  |  |  | 	_rooms_add_portals_to_convex_hulls(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the trace results can never have more hits than the number of static objects
 | 
					
						
							|  |  |  | 	_trace_results.create(_statics.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// precreate a useful bitfield of rooms for use in sprawling, if not created already
 | 
					
						
							|  |  |  | 	// (may not be necessary but just in case, rooms with no statics etc)
 | 
					
						
							|  |  |  | 	_bitfield_rooms.create(_room_pool_ids.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the rooms looksup is a pre-calced grid structure for faster lookup of the nearest room
 | 
					
						
							|  |  |  | 	// from position
 | 
					
						
							|  |  |  | 	_rooms_lookup_bsp.create(*this); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// calculate the roaming expansion margin based on the average room size
 | 
					
						
							|  |  |  | 	Vector3 total_size = Vector3(0, 0, 0); | 
					
						
							|  |  |  | 	for (int n = 0; n < get_num_rooms(); n++) { | 
					
						
							|  |  |  | 		total_size += get_room(n)._aabb.size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (get_num_rooms()) { | 
					
						
							|  |  |  | 		total_size /= get_num_rooms(); | 
					
						
							|  |  |  | 		AABB temp; | 
					
						
							|  |  |  | 		temp.size = total_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// longest axis of average room * fudge factor
 | 
					
						
							|  |  |  | 		_roaming_expansion_margin = temp.get_longest_axis_size() * 0.08; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// calculate PVS
 | 
					
						
							|  |  |  | 	if (p_generate_pvs) { | 
					
						
							|  |  |  | 		PVSBuilder pvs; | 
					
						
							| 
									
										
										
										
											2021-08-08 19:31:02 +01:00
										 |  |  | 		pvs.calculate_pvs(*this, p_pvs_filename, _tracer.get_depth_limit(), p_use_simple_pvs, p_log_pvs_generation); | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 		_cull_using_pvs = p_cull_using_pvs; // hard code to on for test
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		_cull_using_pvs = false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_loaded = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// all the roaming objects need to be sprawled into the rooms
 | 
					
						
							|  |  |  | 	// (they may have been created before the rooms)
 | 
					
						
							|  |  |  | 	_load_finalize_roaming(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// allow deleting any intermediate data
 | 
					
						
							|  |  |  | 	for (int n = 0; n < get_num_rooms(); n++) { | 
					
						
							|  |  |  | 		get_room(n).cleanup_after_conversion(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// this should probably have some thread protection, but I doubt it matters
 | 
					
						
							|  |  |  | 	// as this will worst case give wrong result for a frame
 | 
					
						
							|  |  |  | 	Engine::get_singleton()->set_portals_active(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	print_line("Room conversion complete. " + itos(_room_pool_ids.size()) + " rooms, " + itos(_portal_pool_ids.size()) + " portals."); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | bool PortalRenderer::sprawl_static_geometry(int p_static_id, const VSStatic &p_static, int p_room_id, const Vector<Vector3> &p_object_pts) { | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// set, and if room already done, ignore
 | 
					
						
							|  |  |  | 	if (!_bitfield_rooms.check_and_set(p_room_id)) | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	VSRoom &room = get_room(p_room_id); | 
					
						
							|  |  |  | 	room._static_ids.push_back(p_static_id); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 	bool sprawled = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// go through portals
 | 
					
						
							|  |  |  | 	for (int p = 0; p < room._portal_ids.size(); p++) { | 
					
						
							|  |  |  | 		const VSPortal &portal = get_portal(room._portal_ids[p]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int room_to_id = portal.geometry_crosses_portal(p_room_id, p_static.aabb, p_object_pts); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (room_to_id != -1) { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			// _log(String(Variant(p_static.aabb)) + " crosses portal");
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 			sprawl_static_geometry(p_static_id, p_static, room_to_id, p_object_pts); | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			sprawled = true; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return sprawled; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | bool PortalRenderer::sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int p_room_id) { | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// set, and if room already done, ignore
 | 
					
						
							|  |  |  | 	if (!_bitfield_rooms.check_and_set(p_room_id)) { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	VSRoom &room = get_room(p_room_id); | 
					
						
							|  |  |  | 	room._static_ghost_ids.push_back(p_ghost_id); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 	bool sprawled = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// go through portals
 | 
					
						
							|  |  |  | 	for (int p = 0; p < room._portal_ids.size(); p++) { | 
					
						
							|  |  |  | 		const VSPortal &portal = get_portal(room._portal_ids[p]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int room_to_id = portal.crosses_portal(p_room_id, p_aabb, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (room_to_id != -1) { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			// _log(String(Variant(p_aabb)) + " crosses portal");
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 			sprawl_static_ghost(p_ghost_id, p_aabb, room_to_id); | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			sprawled = true; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return sprawled; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | bool PortalRenderer::sprawl_static(int p_static_id, const VSStatic &p_static, int p_room_id) { | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// set, and if room already done, ignore
 | 
					
						
							|  |  |  | 	if (!_bitfield_rooms.check_and_set(p_room_id)) { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	VSRoom &room = get_room(p_room_id); | 
					
						
							|  |  |  | 	room._static_ids.push_back(p_static_id); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 	bool sprawled = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 	// go through portals
 | 
					
						
							|  |  |  | 	for (int p = 0; p < room._portal_ids.size(); p++) { | 
					
						
							|  |  |  | 		const VSPortal &portal = get_portal(room._portal_ids[p]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int room_to_id = portal.crosses_portal(p_room_id, p_static.aabb, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (room_to_id != -1) { | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			// _log(String(Variant(p_static.aabb)) + " crosses portal");
 | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 			sprawl_static(p_static_id, p_static, room_to_id); | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 			sprawled = true; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-15 11:37:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return sprawled; | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::_load_finalize_roaming() { | 
					
						
							|  |  |  | 	for (int n = 0; n < _moving_list_roaming.size(); n++) { | 
					
						
							|  |  |  | 		uint32_t pool_id = _moving_list_roaming[n]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Moving &moving = _moving_pool[pool_id]; | 
					
						
							|  |  |  | 		const AABB &aabb = moving.exact_aabb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		OcclusionHandle handle = pool_id + 1; | 
					
						
							|  |  |  | 		instance_moving_update(handle, aabb, true); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 0; n < _rghost_pool.active_size(); n++) { | 
					
						
							|  |  |  | 		RGhost &moving = _rghost_pool.get_active(n); | 
					
						
							|  |  |  | 		const AABB &aabb = moving.exact_aabb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rghost_update(_rghost_pool.get_active_id(n) + 1, aabb, true); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-05 15:44:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 0; n < _occluder_pool.active_size(); n++) { | 
					
						
							|  |  |  | 		VSOccluder &occ = _occluder_pool.get_active(n); | 
					
						
							|  |  |  | 		int occluder_id = _occluder_pool.get_active_id(n); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// make sure occluder is in the correct room
 | 
					
						
							|  |  |  | 		occ.room_id = find_room_within(occ.pt_center, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (occ.room_id != -1) { | 
					
						
							|  |  |  | 			VSRoom &room = get_room(occ.room_id); | 
					
						
							|  |  |  | 			room.add_occluder(occluder_id); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::sprawl_roaming(uint32_t p_mover_pool_id, MovingBase &r_moving, int p_room_id, bool p_moving_or_ghost) { | 
					
						
							|  |  |  | 	// set, and if room already done, ignore
 | 
					
						
							|  |  |  | 	if (!_bitfield_rooms.check_and_set(p_room_id)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add to the room
 | 
					
						
							|  |  |  | 	VSRoom &room = get_room(p_room_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (p_moving_or_ghost) { | 
					
						
							|  |  |  | 		room.add_roamer(p_mover_pool_id); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		room.add_rghost(p_mover_pool_id); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add the room	to the mover
 | 
					
						
							|  |  |  | 	r_moving._rooms.push_back(p_room_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// go through portals
 | 
					
						
							|  |  |  | 	for (int p = 0; p < room._portal_ids.size(); p++) { | 
					
						
							|  |  |  | 		const VSPortal &portal = get_portal(room._portal_ids[p]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int room_to_id = portal.crosses_portal(p_room_id, r_moving.expanded_aabb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (room_to_id != -1) { | 
					
						
							|  |  |  | 			// _log(String(Variant(p_static.aabb)) + " crosses portal");
 | 
					
						
							|  |  |  | 			sprawl_roaming(p_mover_pool_id, r_moving, room_to_id, p_moving_or_ghost); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This gets called when you delete an instance the the room system depends on
 | 
					
						
							|  |  |  | void PortalRenderer::_ensure_unloaded() { | 
					
						
							|  |  |  | 	if (_loaded) { | 
					
						
							|  |  |  | 		_loaded = false; | 
					
						
							|  |  |  | 		_log("Portal system unloaded.", 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// this should probably have some thread protection, but I doubt it matters
 | 
					
						
							|  |  |  | 		// as this will worst case give wrong result for a frame
 | 
					
						
							|  |  |  | 		Engine::get_singleton()->set_portals_active(false); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::rooms_and_portals_clear() { | 
					
						
							|  |  |  | 	_loaded = false; | 
					
						
							|  |  |  | 	_statics.clear(); | 
					
						
							|  |  |  | 	_static_ghosts.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the rooms and portals should remove their id when they delete themselves
 | 
					
						
							|  |  |  | 	// from the scene tree by calling room_destroy and portal_destroy ...
 | 
					
						
							|  |  |  | 	// therefore there should be no need to clear these here
 | 
					
						
							|  |  |  | 	// _room_pool_ids.clear();
 | 
					
						
							|  |  |  | 	// _portal_pool_ids.clear();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_rooms_lookup_bsp.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// clear the portals out of each existing room
 | 
					
						
							|  |  |  | 	for (int n = 0; n < get_num_rooms(); n++) { | 
					
						
							|  |  |  | 		VSRoom &room = get_room(n); | 
					
						
							|  |  |  | 		room.rooms_and_portals_clear(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 0; n < get_num_portals(); n++) { | 
					
						
							|  |  |  | 		VSPortal &portal = get_portal(n); | 
					
						
							|  |  |  | 		portal.rooms_and_portals_clear(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// when the rooms_and_portals_clear message is sent,
 | 
					
						
							|  |  |  | 	// we want to remove all references to old rooms in the moving
 | 
					
						
							|  |  |  | 	// objects, to prevent dangling references.
 | 
					
						
							|  |  |  | 	for (int n = 0; n < get_num_moving_globals(); n++) { | 
					
						
							|  |  |  | 		Moving &moving = get_pool_moving(_moving_list_global[n]); | 
					
						
							|  |  |  | 		moving.rooms_and_portals_clear(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (int n = 0; n < _moving_list_roaming.size(); n++) { | 
					
						
							|  |  |  | 		Moving &moving = get_pool_moving(_moving_list_roaming[n]); | 
					
						
							|  |  |  | 		moving.rooms_and_portals_clear(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 0; n < _rghost_pool.active_size(); n++) { | 
					
						
							|  |  |  | 		RGhost &moving = _rghost_pool.get_active(n); | 
					
						
							|  |  |  | 		moving.rooms_and_portals_clear(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_pvs.clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::rooms_override_camera(bool p_override, const Vector3 &p_point, const Vector<Plane> *p_convex) { | 
					
						
							|  |  |  | 	_override_camera = p_override; | 
					
						
							|  |  |  | 	_override_camera_pos = p_point; | 
					
						
							|  |  |  | 	if (p_convex) { | 
					
						
							|  |  |  | 		_override_camera_planes = *p_convex; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PortalRenderer::rooms_update_gameplay_monitor(const Vector<Vector3> &p_camera_positions) { | 
					
						
							|  |  |  | 	// is the pvs loaded?
 | 
					
						
							|  |  |  | 	if (!_loaded || !_pvs.is_loaded()) { | 
					
						
							|  |  |  | 		if (!_pvs.is_loaded()) { | 
					
						
							|  |  |  | 			WARN_PRINT_ONCE("RoomManager PVS is required for this functionality"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int *source_rooms = (int *)alloca(sizeof(int) * p_camera_positions.size()); | 
					
						
							|  |  |  | 	int num_source_rooms = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 0; n < p_camera_positions.size(); n++) { | 
					
						
							|  |  |  | 		int source_room_id = find_room_within(p_camera_positions[n]); | 
					
						
							|  |  |  | 		if (source_room_id == -1) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		source_rooms[num_source_rooms++] = source_room_id; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_gameplay_monitor.update_gameplay(*this, source_rooms, num_source_rooms); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int PortalRenderer::cull_convex_implementation(const Vector3 &p_point, const Vector<Plane> &p_convex, VSInstance **p_result_array, int p_result_max, uint32_t p_mask, int32_t &r_previous_room_id_hint) { | 
					
						
							|  |  |  | 	// start room
 | 
					
						
							|  |  |  | 	int start_room_id = find_room_within(p_point, r_previous_room_id_hint); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// return the previous room hint
 | 
					
						
							|  |  |  | 	r_previous_room_id_hint = start_room_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (start_room_id == -1) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// planes must be in CameraMatrix order
 | 
					
						
							|  |  |  | 	DEV_ASSERT(p_convex.size() == 6); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	LocalVector<Plane> planes; | 
					
						
							|  |  |  | 	planes = p_convex; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_trace_results.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!_debug_sprawl) { | 
					
						
							|  |  |  | 		_tracer.trace(*this, p_point, planes, start_room_id, _trace_results); //, near_and_far_planes);
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		_tracer.trace_debug_sprawl(*this, p_point, start_room_id, _trace_results); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int num_results = _trace_results.visible_static_ids.size(); | 
					
						
							|  |  |  | 	int out_count = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int n = 0; n < num_results; n++) { | 
					
						
							|  |  |  | 		uint32_t static_id = _trace_results.visible_static_ids[n]; | 
					
						
							|  |  |  | 		RID static_rid = _statics[static_id].instance; | 
					
						
							|  |  |  | 		VSInstance *instance = VSG::scene->_instance_get_from_rid(static_rid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (VSG::scene->_instance_cull_check(instance, p_mask)) { | 
					
						
							|  |  |  | 			p_result_array[out_count++] = instance; | 
					
						
							|  |  |  | 			if (out_count >= p_result_max) { | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// results could be full up already
 | 
					
						
							|  |  |  | 	if (out_count >= p_result_max) { | 
					
						
							|  |  |  | 		return out_count; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// add the roaming results
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// cap to the maximum results
 | 
					
						
							|  |  |  | 	int num_roam_hits = _trace_results.visible_roamer_pool_ids.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// translate
 | 
					
						
							|  |  |  | 	for (int n = 0; n < num_roam_hits; n++) { | 
					
						
							|  |  |  | 		const Moving &moving = get_pool_moving(_trace_results.visible_roamer_pool_ids[n]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (VSG::scene->_instance_cull_check(moving.instance, p_mask)) { | 
					
						
							|  |  |  | 			p_result_array[out_count++] = moving.instance; | 
					
						
							|  |  |  | 			if (out_count >= p_result_max) { | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// results could be full up already
 | 
					
						
							|  |  |  | 	if (out_count >= p_result_max) { | 
					
						
							|  |  |  | 		return out_count; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-31 07:53:17 +01:00
										 |  |  | 	out_count = _tracer.trace_globals(planes, p_result_array, out_count, p_result_max, p_mask, _override_camera); | 
					
						
							| 
									
										
										
										
											2021-02-04 10:43:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return out_count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | String PortalRenderer::_rid_to_string(RID p_rid) { | 
					
						
							|  |  |  | 	return _addr_to_string(p_rid.get_data()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | String PortalRenderer::_addr_to_string(const void *p_addr) { | 
					
						
							|  |  |  | 	return String::num_uint64((uint64_t)p_addr, 16); | 
					
						
							|  |  |  | } |