Merge pull request #50477 from lawnjelly/portals_autolink_order

Portals - fix autolink sprawling, refine logs
This commit is contained in:
Rémi Verschelde 2021-07-15 15:22:53 +02:00 committed by GitHub
commit 26d0c90370
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 56 deletions

View file

@ -4546,7 +4546,7 @@ void PortalGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
if (portal) {
// warnings
if (portal->_warning_outside_room_aabb || portal->_warning_facing_wrong_way) {
if (portal->_warning_outside_room_aabb || portal->_warning_facing_wrong_way || portal->_warning_autolink_failed) {
Ref<Material> icon = get_material("portal_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View file

@ -158,6 +158,7 @@ private:
// warnings
bool _warning_outside_room_aabb = false;
bool _warning_facing_wrong_way = false;
bool _warning_autolink_failed = false;
#endif
// this is read from the gizmo

View file

@ -582,11 +582,17 @@ void RoomManager::rooms_convert() {
_second_pass_rooms(roomgroups, portals);
// third pass
// autolink portals that are not already manually linked
// and finalize the portals
_third_pass_portals(_roomlist, portals);
// finalize the room hulls
// Find the statics AGAIN and only this time add them to the PortalRenderer.
// We need to do this twice because the room points determine the room bound...
// but the bound is needed for autolinking,
// and the autolinking needs to be done BEFORE adding to the PortalRenderer so that
// the static objects will correctly sprawl. It is a chicken and egg situation.
// Also finalize the room hulls.
_third_pass_rooms(portals);
bool generate_pvs = false;
@ -658,7 +664,9 @@ void RoomManager::_second_pass_room(Room *p_room, const LocalVector<RoomGroup *>
} else if (_name_starts_with(child, "Bound", true)) {
manual_bound_found = _convert_manual_bound(p_room, child, p_portals);
} else {
_find_statics_recursive(p_room, child, room_pts);
// don't add the instances to the portal renderer on the first pass of _find_statics,
// just find the geometry points in order to make sure the bound is correct.
_find_statics_recursive(p_room, child, room_pts, false);
}
}
}
@ -687,6 +695,11 @@ void RoomManager::_second_pass_room(Room *p_room, const LocalVector<RoomGroup *>
int portal_id = p_room->_portals[n];
Portal *portal = p_portals[portal_id];
// only checking portals out from source room
if (portal->_linkedroom_ID[0] != p_room->_room_ID) {
continue;
}
// don't add portals to the world bound that are internal to this room!
if (portal->is_portal_internal(p_room->_room_ID)) {
continue;
@ -781,6 +794,26 @@ void RoomManager::_third_pass_rooms(const LocalVector<Portal *> &p_portals) {
for (int n = 0; n < _rooms.size(); n++) {
Room *room = _rooms[n];
String room_short_name = _find_name_after(room, "ROOM");
convert_log("ROOM\t" + room_short_name);
// log output the portals associated with this room
for (int p = 0; p < room->_portals.size(); p++) {
const Portal &portal = *p_portals[room->_portals[p]];
String in_or_out = (portal._linkedroom_ID[0] == room->_room_ID) ? "POUT" : "PIN ";
convert_log("\t\t" + in_or_out + "\t" + portal.get_name());
}
// do a second pass finding the statics, where they are
// finally added to the rooms in the portal_renderer.
Vector<Vector3> room_pts;
// the true indicates we DO want to add to the portal renderer this second time
// we call _find_statics_recursive
_find_statics_recursive(room, room, room_pts, true);
if (!_convert_room_hull_final(room, p_portals)) {
found_errors = true;
}
@ -900,7 +933,7 @@ void RoomManager::_third_pass_portals(Spatial *p_roomlist, LocalVector<Portal *>
if (!outside) {
// great, we found a linked room!
convert_log("\tauto linked portal from room " + source_room->get_name() + " to room " + room->get_name(), 1);
convert_log("\t\tAUTOLINK OK from " + source_room->get_name() + " to " + room->get_name(), 1);
portal->_linkedroom_ID[1] = r;
// add the portal to the portals list for the receiving room
@ -919,8 +952,13 @@ void RoomManager::_third_pass_portals(Spatial *p_roomlist, LocalVector<Portal *>
// error condition
if (!autolink_found) {
WARN_PRINT("Portal autolink failed for Portal from " + source_room->get_name());
WARN_PRINT("Portal AUTOLINK failed for " + portal->get_name() + " from " + source_room->get_name());
_warning_portal_autolink_failed = true;
#ifdef TOOLS_ENABLED
portal->_warning_autolink_failed = true;
portal->update_gizmo();
#endif
}
} // for portal
}
@ -1040,8 +1078,6 @@ void RoomManager::_convert_room(Spatial *p_node, LocalVector<Portal *> &r_portal
}
}
convert_log("convert_room : " + string_full_name, 1);
// make sure the room is blank, especially if already created
room->clear();
@ -1087,8 +1123,11 @@ void RoomManager::_check_portal_for_warnings(Portal *p_portal, const AABB &p_roo
// far outside the room?
const Vector3 &pos = p_portal->get_global_transform().origin;
if (p_portal->_warning_outside_room_aabb != (!bb.has_point(pos))) {
p_portal->_warning_outside_room_aabb = !p_portal->_warning_outside_room_aabb;
bool old_outside = p_portal->_warning_outside_room_aabb;
p_portal->_warning_outside_room_aabb = !bb.has_point(pos);
if (p_portal->_warning_outside_room_aabb != old_outside) {
changed = true;
}
@ -1099,8 +1138,11 @@ void RoomManager::_check_portal_for_warnings(Portal *p_portal, const AABB &p_roo
// facing wrong way?
Vector3 offset = pos - bb.get_center();
real_t dot = offset.dot(p_portal->_plane.normal);
if (p_portal->_warning_facing_wrong_way != (dot < 0.0)) {
p_portal->_warning_facing_wrong_way = !p_portal->_warning_facing_wrong_way;
bool old_facing = p_portal->_warning_facing_wrong_way;
p_portal->_warning_facing_wrong_way = dot < 0.0;
if (p_portal->_warning_facing_wrong_way != old_facing) {
changed = true;
}
@ -1108,13 +1150,22 @@ void RoomManager::_check_portal_for_warnings(Portal *p_portal, const AABB &p_roo
WARN_PRINT(String(p_portal->get_name()) + " possibly facing the wrong way.");
}
// handled later
p_portal->_warning_autolink_failed = false;
if (changed) {
p_portal->update_gizmo();
}
#endif
}
void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector<Vector3> &r_room_pts) {
void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector<Vector3> &r_room_pts, bool p_add_to_portal_renderer) {
// don't process portal MeshInstances that are being deleted
// (and replaced by proper Portal nodes)
if (p_node->is_queued_for_deletion()) {
return;
}
bool ignore = false;
VisualInstance *vi = Object::cast_to<VisualInstance>(p_node);
@ -1131,21 +1182,36 @@ void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector<
}
if (!ignore) {
// lights
Light *light = Object::cast_to<Light>(p_node);
if (light) {
convert_log("\tfound Light " + light->get_name());
// We'll have a done flag. This isn't strictly speaking necessary
// because the types should be mutually exclusive, but this would
// break if something changes the inheritance hierarchy of the nodes
// at a later date, so having a done flag makes it more robust.
bool done = false;
Light *light = Object::cast_to<Light>(p_node);
if (!done && light) {
done = true;
// lights (don't affect bound, so aren't added in first pass)
if (p_add_to_portal_renderer) {
Vector<Vector3> dummy_pts;
VisualServer::get_singleton()->room_add_instance(p_room->_room_rid, light->get_instance(), light->get_transformed_aabb(), dummy_pts);
convert_log("\t\t\tLIGT\t" + light->get_name());
}
}
GeometryInstance *gi = Object::cast_to<GeometryInstance>(p_node);
if (gi) {
if (!done && gi) {
done = true;
// MeshInstance is the most interesting type for portalling, so we handle this explicitly
MeshInstance *mi = Object::cast_to<MeshInstance>(p_node);
if (mi) {
convert_log("\tfound MeshInst " + mi->get_name());
if (p_add_to_portal_renderer) {
convert_log("\t\t\tMESH\t" + mi->get_name());
}
Vector<Vector3> object_pts;
AABB aabb;
@ -1161,15 +1227,20 @@ void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector<
r_room_pts.append_array(object_pts);
}
if (p_add_to_portal_renderer) {
VisualServer::get_singleton()->room_add_instance(p_room->_room_rid, mi->get_instance(), mi->get_transformed_aabb(), object_pts);
}
} // if bound found points
} else {
// geometry instance but not a mesh instance .. just use AABB
convert_log("\tfound GeomInst " + gi->get_name());
// Vector<Vector3> dummy_pts;
// VisualServer::get_singleton()->room_add_instance(p_room->_room_rid, gi->get_instance(), gi->get_transformed_aabb(), dummy_pts);
// geometry instance but not a mesh instance ..
if (p_add_to_portal_renderer) {
convert_log("\t\t\tGEOM\t" + gi->get_name());
}
Vector<Vector3> object_pts;
AABB aabb;
// attempt to recognise this GeometryInstance and read back the geometry
if (_bound_findpoints_geom_instance(gi, object_pts, aabb)) {
// need to keep track of room bound
// NOTE the is_visible check MAY cause problems if conversion run on nodes that
@ -1179,17 +1250,22 @@ void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector<
r_room_pts.append_array(object_pts);
}
if (p_add_to_portal_renderer) {
VisualServer::get_singleton()->room_add_instance(p_room->_room_rid, gi->get_instance(), gi->get_transformed_aabb(), object_pts);
}
} // if bound found points
}
} // if gi
VisibilityNotifier *vn = Object::cast_to<VisibilityNotifier>(p_node);
if (vn && ((vn->get_portal_mode() == CullInstance::PORTAL_MODE_DYNAMIC) || (vn->get_portal_mode() == CullInstance::PORTAL_MODE_STATIC))) {
convert_log("\tfound VisibilityNotifier " + vn->get_name());
if (!done && vn && ((vn->get_portal_mode() == CullInstance::PORTAL_MODE_DYNAMIC) || (vn->get_portal_mode() == CullInstance::PORTAL_MODE_STATIC))) {
done = true;
if (p_add_to_portal_renderer) {
AABB world_aabb = vn->get_global_transform().xform(vn->get_aabb());
VisualServer::get_singleton()->room_add_ghost(p_room->_room_rid, vn->get_instance_id(), world_aabb);
convert_log("\t\t\tVIS \t" + vn->get_name());
}
}
} // if not ignore
@ -1198,7 +1274,7 @@ void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector<
Spatial *child = Object::cast_to<Spatial>(p_node->get_child(n));
if (child) {
_find_statics_recursive(p_room, child, r_room_pts);
_find_statics_recursive(p_room, child, r_room_pts, p_add_to_portal_renderer);
}
}
}
@ -1353,7 +1429,9 @@ bool RoomManager::_convert_room_hull_final(Room *p_room, const LocalVector<Porta
Geometry::MeshData md_simplified;
_build_simplified_bound(p_room, md_simplified, p_room->_planes, num_portals_added);
if (num_planes_before_simplification != p_room->_planes.size()) {
convert_log("\t\t\tcontained " + itos(num_planes_before_simplification) + " planes before simplification, " + itos(p_room->_planes.size()) + " planes after.");
}
// make a copy of the mesh data for debugging
// note this could be avoided in release builds? NYI
@ -1455,9 +1533,6 @@ bool RoomManager::_add_plane_if_unique(const Room *p_room, LocalVector<Plane, in
}
void RoomManager::_convert_portal(Room *p_room, Spatial *p_node, LocalVector<Portal *> &portals) {
String string_full_name = p_node->get_name();
convert_log("convert_portal : " + string_full_name);
Portal *portal = Object::cast_to<Portal>(p_node);
// if not a gportal already, convert the node type

View file

@ -157,7 +157,7 @@ private:
bool _convert_manual_bound(Room *p_room, Spatial *p_node, const LocalVector<Portal *> &p_portals);
void _check_portal_for_warnings(Portal *p_portal, const AABB &p_room_aabb_without_portals);
void _find_statics_recursive(Room *p_room, Spatial *p_node, Vector<Vector3> &r_room_pts);
void _find_statics_recursive(Room *p_room, Spatial *p_node, Vector<Vector3> &r_room_pts, bool p_add_to_portal_renderer);
bool _convert_room_hull_preliminary(Room *p_room, const Vector<Vector3> &p_room_pts, const LocalVector<Portal *> &p_portals);
bool _bound_findpoints_mesh_instance(MeshInstance *p_mi, Vector<Vector3> &r_room_pts, AABB &r_aabb);
@ -201,7 +201,7 @@ private:
Error _build_convex_hull(const Vector<Vector3> &p_points, Geometry::MeshData &r_mesh, real_t p_epsilon = 3.0 * UNIT_EPSILON);
// output strings during conversion process
void convert_log(String p_string, int p_priority = 0) { debug_print_line(p_string, p_priority); }
void convert_log(String p_string, int p_priority = 0) { debug_print_line(p_string, 1); }
// only prints when user has set 'debug' in the room manager inspector
// also does not show in non editor builds

View file

@ -315,7 +315,7 @@ void PortalRenderer::portal_link(PortalHandle p_portal, RoomHandle p_room_from,
room_to._contains_internal_rooms = true;
}
_log("portal_link from room " + itos(room_from._room_ID) + " to room " + itos(room_to._room_ID));
// _log("portal_link from room " + itos(room_from._room_ID) + " to room " + itos(room_to._room_ID));
room_from._portal_ids.push_back(portal._portal_id);
@ -510,7 +510,9 @@ OcclusionHandle PortalRenderer::room_add_ghost(RoomHandle p_room, ObjectID p_obj
// create a bitfield to indicate which rooms have been
// visited already, to prevent visiting rooms multiple times
_bitfield_rooms.blank();
sprawl_static_ghost(ghost_id, p_aabb, p_room);
if (sprawl_static_ghost(ghost_id, p_aabb, p_room)) {
_log("\t\tSPRAWLED");
}
}
return OCCLUSION_HANDLE_ROOM_BIT;
@ -547,9 +549,13 @@ OcclusionHandle PortalRenderer::room_add_instance(RoomHandle p_room, RID p_insta
_bitfield_rooms.blank();
if (p_object_pts.size()) {
sprawl_static_geometry(static_id, st, st.source_room_id, p_object_pts);
if (sprawl_static_geometry(static_id, st, st.source_room_id, p_object_pts)) {
_log("\t\tSPRAWLED");
}
} else {
sprawl_static(static_id, st, st.source_room_id);
if (sprawl_static(static_id, st, st.source_room_id)) {
_log("\t\tSPRAWLED");
}
}
}
@ -701,14 +707,16 @@ void PortalRenderer::rooms_finalize(bool p_generate_pvs, bool p_cull_using_pvs,
print_line("Room conversion complete. " + itos(_room_pool_ids.size()) + " rooms, " + itos(_portal_pool_ids.size()) + " portals.");
}
void PortalRenderer::sprawl_static_geometry(int p_static_id, const VSStatic &p_static, int p_room_id, const Vector<Vector3> &p_object_pts) {
bool PortalRenderer::sprawl_static_geometry(int p_static_id, const VSStatic &p_static, int p_room_id, const Vector<Vector3> &p_object_pts) {
// set, and if room already done, ignore
if (!_bitfield_rooms.check_and_set(p_room_id))
return;
return false;
VSRoom &room = get_room(p_room_id);
room._static_ids.push_back(p_static_id);
bool sprawled = false;
// go through portals
for (int p = 0; p < room._portal_ids.size(); p++) {
const VSPortal &portal = get_portal(room._portal_ids[p]);
@ -716,22 +724,26 @@ void PortalRenderer::sprawl_static_geometry(int p_static_id, const VSStatic &p_s
int room_to_id = portal.geometry_crosses_portal(p_room_id, p_static.aabb, p_object_pts);
if (room_to_id != -1) {
_log(String(Variant(p_static.aabb)) + " crosses portal");
// _log(String(Variant(p_static.aabb)) + " crosses portal");
sprawl_static_geometry(p_static_id, p_static, room_to_id, p_object_pts);
}
sprawled = true;
}
}
void PortalRenderer::sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int p_room_id) {
return sprawled;
}
bool PortalRenderer::sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int p_room_id) {
// set, and if room already done, ignore
if (!_bitfield_rooms.check_and_set(p_room_id)) {
return;
return false;
}
VSRoom &room = get_room(p_room_id);
room._static_ghost_ids.push_back(p_ghost_id);
bool sprawled = false;
// go through portals
for (int p = 0; p < room._portal_ids.size(); p++) {
const VSPortal &portal = get_portal(room._portal_ids[p]);
@ -739,22 +751,26 @@ void PortalRenderer::sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int
int room_to_id = portal.crosses_portal(p_room_id, p_aabb, true);
if (room_to_id != -1) {
_log(String(Variant(p_aabb)) + " crosses portal");
// _log(String(Variant(p_aabb)) + " crosses portal");
sprawl_static_ghost(p_ghost_id, p_aabb, room_to_id);
}
sprawled = true;
}
}
void PortalRenderer::sprawl_static(int p_static_id, const VSStatic &p_static, int p_room_id) {
return sprawled;
}
bool PortalRenderer::sprawl_static(int p_static_id, const VSStatic &p_static, int p_room_id) {
// set, and if room already done, ignore
if (!_bitfield_rooms.check_and_set(p_room_id)) {
return;
return false;
}
VSRoom &room = get_room(p_room_id);
room._static_ids.push_back(p_static_id);
bool sprawled = false;
// go through portals
for (int p = 0; p < room._portal_ids.size(); p++) {
const VSPortal &portal = get_portal(room._portal_ids[p]);
@ -762,11 +778,13 @@ void PortalRenderer::sprawl_static(int p_static_id, const VSStatic &p_static, in
int room_to_id = portal.crosses_portal(p_room_id, p_static.aabb, true);
if (room_to_id != -1) {
_log(String(Variant(p_static.aabb)) + " crosses portal");
// _log(String(Variant(p_static.aabb)) + " crosses portal");
sprawl_static(p_static_id, p_static, room_to_id);
sprawled = true;
}
}
return sprawled;
}
void PortalRenderer::_load_finalize_roaming() {

View file

@ -221,9 +221,9 @@ private:
return _rooms_lookup_bsp.find_room_within(*this, p_pos, p_previous_room_id);
}
void sprawl_static(int p_static_id, const VSStatic &p_static, int p_room_id);
void sprawl_static_geometry(int p_static_id, const VSStatic &p_static, int p_room_id, const Vector<Vector3> &p_object_pts);
void sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int p_room_id);
bool sprawl_static(int p_static_id, const VSStatic &p_static, int p_room_id);
bool sprawl_static_geometry(int p_static_id, const VSStatic &p_static, int p_room_id, const Vector<Vector3> &p_object_pts);
bool sprawl_static_ghost(int p_ghost_id, const AABB &p_aabb, int p_room_id);
void _load_finalize_roaming();
void sprawl_roaming(uint32_t p_mover_pool_id, MovingBase &r_moving, int p_room_id, bool p_moving_or_ghost);