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.
This commit is contained in:
lawnjelly 2021-07-15 14:42:53 +01:00
parent 51f8247871
commit 06d66488c2
4 changed files with 109 additions and 47 deletions

View File

@ -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());
}

View File

@ -84,6 +84,9 @@ private:
template <typename T>
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<Plane, int32_t> _planes;

View File

@ -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<Portal *
}
}
void RoomManager::_third_pass_portals(Spatial *p_roomlist, LocalVector<Portal *> &r_portals) {
convert_log("_third_pass_portals");
void RoomManager::_autolink_portals(Spatial *p_roomlist, LocalVector<Portal *> &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<Portal *>
// 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<Room>(p_node)) {
// end the recursion here
return nullptr;
bool RoomManager::_check_roomlist_validity(Node *p_node) {
if (Room::detect_nodes_of_type<RoomManager>(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<RoomGroup>(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<Portal *> &r_portals, LocalVector<RoomGroup *> &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<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)
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<Vector3> 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<VisualInstance>(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<Spatial>(p_node->get_child(n));
if (child) {
_autoplace_recursive(child);
}
}
}
void RoomManager::_process_static(Room *p_room, Spatial *p_node, Vector<Vector3> &r_room_pts, bool p_add_to_portal_renderer) {
bool ignore = false;
VisualInstance *vi = Object::cast_to<VisualInstance>(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<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;
}
_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<Spatial>(p_node->get_child(n));

View File

@ -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<Portal *> &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<Vector3> &r_room_pts, bool p_add_to_portal_renderer);
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);
@ -164,16 +166,21 @@ private:
bool _bound_findpoints_geom_instance(GeometryInstance *p_gi, Vector<Vector3> &r_room_pts, AABB &r_aabb);
// THIRD PASS
void _third_pass_portals(Spatial *p_roomlist, LocalVector<Portal *> &r_portals);
void _autolink_portals(Spatial *p_roomlist, LocalVector<Portal *> &r_portals);
void _third_pass_rooms(const LocalVector<Portal *> &p_portals);
bool _convert_room_hull_final(Room *p_room, const LocalVector<Portal *> &p_portals);
void _build_simplified_bound(const Room *p_room, Geometry::MeshData &r_md, LocalVector<Plane, int32_t> &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<Plane, int32_t> &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<Vector3> &p_points, Geometry::MeshData &r_mesh);
#ifdef TOOLS_ENABLED