From 06d66488c2db29bf5600df93362c0947731b9166 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Thu, 15 Jul 2021 14:42:53 +0100 Subject: [PATCH] Portals - Add the ability to autoplace static objects In order to make level building easier, the system can now support STATIC and DYNAMIC objects in the roomlist that are not placed in rooms. The system will automatically place them in the appropriate room. --- scene/3d/room.cpp | 14 +++++ scene/3d/room.h | 3 + scene/3d/room_manager.cpp | 128 ++++++++++++++++++++++++-------------- scene/3d/room_manager.h | 11 +++- 4 files changed, 109 insertions(+), 47 deletions(-) diff --git a/scene/3d/room.cpp b/scene/3d/room.cpp index 463b1bc5694..82c5b97cd6d 100644 --- a/scene/3d/room.cpp +++ b/scene/3d/room.cpp @@ -108,6 +108,20 @@ Room::~Room() { } } +bool Room::contains_point(const Vector3 &p_pt) const { + if (!_aabb.has_point(p_pt)) { + return false; + } + + for (int n = 0; n < _planes.size(); n++) { + if (_planes[n].is_point_over(p_pt)) { + return false; + } + } + + return true; +} + void Room::set_room_simplify(real_t p_value) { _simplify_info.set_simplify(p_value, _aabb.get_longest_axis_size()); } diff --git a/scene/3d/room.h b/scene/3d/room.h index e8065a6c875..2af4cdcc857 100644 --- a/scene/3d/room.h +++ b/scene/3d/room.h @@ -84,6 +84,9 @@ private: template static bool detect_nodes_using_lambda(const Node *p_node, T p_lambda, bool p_ignore_first_node = true); + // note this is client side, and does not use the final planes stored in the PortalRenderer + bool contains_point(const Vector3 &p_pt) const; + // planes forming convex hull of room LocalVector _planes; diff --git a/scene/3d/room_manager.cpp b/scene/3d/room_manager.cpp index a81bb4752a3..f0ae7ce050a 100644 --- a/scene/3d/room_manager.cpp +++ b/scene/3d/room_manager.cpp @@ -557,9 +557,7 @@ void RoomManager::rooms_convert() { // first check that the roomlist is valid, and the user hasn't made // a silly scene tree - Node *invalid_node = _check_roomlist_validity_recursive(_roomlist); - if (invalid_node) { - show_warning("RoomList contains invalid node", "RoomList should only contain Rooms, RoomGroups and Spatials.\nInvalid node : " + invalid_node->get_name()); + if (!_check_roomlist_validity(_roomlist)) { return; } @@ -585,7 +583,7 @@ void RoomManager::rooms_convert() { // autolink portals that are not already manually linked // and finalize the portals - _third_pass_portals(_roomlist, portals); + _autolink_portals(_roomlist, portals); // 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... @@ -595,6 +593,11 @@ void RoomManager::rooms_convert() { // Also finalize the room hulls. _third_pass_rooms(portals); + // now we run autoplace to place any statics that have not been explicitly placed in rooms. + // These will by definition not affect the room bounds, but is convenient for users to edit + // levels in a more freeform manner + _autoplace_recursive(_roomlist); + bool generate_pvs = false; bool pvs_cull = false; switch (_pvs_mode) { @@ -866,8 +869,8 @@ void RoomManager::_second_pass_portals(Spatial *p_roomlist, LocalVector &r_portals) { - convert_log("_third_pass_portals"); +void RoomManager::_autolink_portals(Spatial *p_roomlist, LocalVector &r_portals) { + convert_log("_autolink_portals"); for (unsigned int n = 0; n < r_portals.size(); n++) { Portal *portal = r_portals[n]; @@ -965,44 +968,13 @@ void RoomManager::_third_pass_portals(Spatial *p_roomlist, LocalVector // to prevent users creating mistakes for themselves, we limit what can be put into the room list branch. // returns invalid node, or NULL -Node *RoomManager::_check_roomlist_validity_recursive(Node *p_node) { - bool ok = false; - - // is this a room? - if (_name_starts_with(p_node, "Room") || _node_is_type(p_node)) { - // end the recursion here - return nullptr; +bool RoomManager::_check_roomlist_validity(Node *p_node) { + if (Room::detect_nodes_of_type(p_node, false)) { + show_warning("RoomList should not be the RoomManager,\nor contain a RoomManager as child or grandchild."); + return false; } - // is this a roomgroup? - if (_name_starts_with(p_node, "RoomGroup") || _node_is_type(p_node)) { - // end the recursion here - return nullptr; - } - - // now we are getting dodgy. - // is it a Spatial? (and not a derived) - if (p_node->get_class_name() == "Spatial") { - ok = true; - } - - if (!ok) { - // return the invalid node - return p_node; - } - - // recurse - for (int n = 0; n < p_node->get_child_count(); n++) { - Node *child = p_node->get_child(n); - if (child) { - Node *invalid_node = _check_roomlist_validity_recursive(child); - if (invalid_node) { - return invalid_node; - } - } - } - - return nullptr; + return true; } void RoomManager::_convert_rooms_recursive(Spatial *p_node, LocalVector &r_portals, LocalVector &r_roomgroups, int p_roomgroup) { @@ -1159,13 +1131,69 @@ void RoomManager::_check_portal_for_warnings(Portal *p_portal, const AABB &p_roo #endif } -void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector &r_room_pts, bool p_add_to_portal_renderer) { - // don't process portal MeshInstances that are being deleted - // (and replaced by proper Portal nodes) +bool RoomManager::_autoplace_object(VisualInstance *p_vi) { + // note we could alternatively use the portal_renderer to do this more efficiently + // (as it has a BSP) but at a cost of returning result from the visual server + AABB bb = p_vi->get_transformed_aabb(); + Vector3 centre = bb.get_center(); + + // in order to deal with internal rooms, we can't just stop at the first + // room the point is within, as there could be later rooms with a higher priority + int best_priority = -INT32_MAX; + Room *best_room = nullptr; + + for (int n = 0; n < _rooms.size(); n++) { + Room *room = _rooms[n]; + + if (room->contains_point(centre)) { + if (room->_room_priority > best_priority) { + best_priority = room->_room_priority; + best_room = room; + } + } + } + + if (best_room) { + // just dummies, we won't use these this time + Vector room_pts; + + // we can reuse this function + _process_static(best_room, p_vi, room_pts, true); + return true; + } + + return false; +} + +void RoomManager::_autoplace_recursive(Spatial *p_node) { if (p_node->is_queued_for_deletion()) { return; } + VisualInstance *vi = Object::cast_to(p_node); + + // we are only interested in VIs with static or dynamic mode + if (vi) { + switch (vi->get_portal_mode()) { + default: { + } break; + case CullInstance::PORTAL_MODE_DYNAMIC: + case CullInstance::PORTAL_MODE_STATIC: { + _autoplace_object(vi); + } break; + } + } + + for (int n = 0; n < p_node->get_child_count(); n++) { + Spatial *child = Object::cast_to(p_node->get_child(n)); + + if (child) { + _autoplace_recursive(child); + } + } +} + +void RoomManager::_process_static(Room *p_room, Spatial *p_node, Vector &r_room_pts, bool p_add_to_portal_renderer) { bool ignore = false; VisualInstance *vi = Object::cast_to(p_node); @@ -1269,6 +1297,16 @@ void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector< } } // if not ignore +} + +void RoomManager::_find_statics_recursive(Room *p_room, Spatial *p_node, Vector &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; + } + + _process_static(p_room, p_node, r_room_pts, p_add_to_portal_renderer); for (int n = 0; n < p_node->get_child_count(); n++) { Spatial *child = Object::cast_to(p_node->get_child(n)); diff --git a/scene/3d/room_manager.h b/scene/3d/room_manager.h index a4aeb56f45d..f7369e4cc59 100644 --- a/scene/3d/room_manager.h +++ b/scene/3d/room_manager.h @@ -39,6 +39,7 @@ class Portal; class RoomGroup; class MeshInstance; class GeometryInstance; +class VisualInstance; #define GODOT_PORTAL_DELINEATOR String("_") #define GODOT_PORTAL_WILDCARD String("*") @@ -157,6 +158,7 @@ private: bool _convert_manual_bound(Room *p_room, Spatial *p_node, const LocalVector &p_portals); void _check_portal_for_warnings(Portal *p_portal, const AABB &p_room_aabb_without_portals); + void _process_static(Room *p_room, Spatial *p_node, Vector &r_room_pts, bool p_add_to_portal_renderer); void _find_statics_recursive(Room *p_room, Spatial *p_node, Vector &r_room_pts, bool p_add_to_portal_renderer); bool _convert_room_hull_preliminary(Room *p_room, const Vector &p_room_pts, const LocalVector &p_portals); @@ -164,16 +166,21 @@ private: bool _bound_findpoints_geom_instance(GeometryInstance *p_gi, Vector &r_room_pts, AABB &r_aabb); // THIRD PASS - void _third_pass_portals(Spatial *p_roomlist, LocalVector &r_portals); + void _autolink_portals(Spatial *p_roomlist, LocalVector &r_portals); void _third_pass_rooms(const LocalVector &p_portals); bool _convert_room_hull_final(Room *p_room, const LocalVector &p_portals); void _build_simplified_bound(const Room *p_room, Geometry::MeshData &r_md, LocalVector &r_planes, int p_num_portal_planes); + // AUTOPLACE - automatically place STATIC and DYNAMICs that are not within a room + // into the most appropriate room, and sprawl + void _autoplace_recursive(Spatial *p_node); + bool _autoplace_object(VisualInstance *p_vi); + // misc bool _add_plane_if_unique(const Room *p_room, LocalVector &r_planes, const Plane &p); void _update_portal_margins(Spatial *p_node, real_t p_margin); - Node *_check_roomlist_validity_recursive(Node *p_node); + bool _check_roomlist_validity(Node *p_node); void _cleanup_after_conversion(); Error _build_room_convex_hull(const Room *p_room, const Vector &p_points, Geometry::MeshData &r_mesh); #ifdef TOOLS_ENABLED