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