Removed room and portal nodes.

This commit is contained in:
Relintai 2023-12-15 09:45:58 +01:00
parent f944578178
commit b248f94cdc
9 changed files with 0 additions and 4156 deletions

View File

@ -1,707 +0,0 @@
/*************************************************************************/
/* portal.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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.h"
#include "core/config/engine.h"
#include "mesh_instance.h"
#include "room.h"
#include "room_group.h"
#include "room_manager.h"
#include "scene/main/viewport.h"
#include "scene/resources/mesh/mesh.h"
#include "scene/resources/world_3d.h"
#include "servers/rendering_server.h"
bool Portal::_portal_plane_convention = false;
bool Portal::_settings_gizmo_show_margins = true;
Portal::Portal() {
clear();
_settings_active = true;
_settings_two_way = true;
_internal = false;
_linkedroom_ID[0] = -1;
_linkedroom_ID[1] = -1;
_pts_world.clear();
_pts_local.clear();
_pts_local_raw.resize(0);
_pt_center_world = Vector3();
_plane = Plane();
_margin = 1.0;
_use_default_margin = true;
// the visual server portal lifetime is linked to the lifetime of this object
_portal_rid = RID_PRIME(RenderingServer::get_singleton()->portal_create());
#ifdef TOOLS_ENABLED
_room_manager_pandemonium_ID = 0;
#endif
// portals are defined COUNTER clockwise,
// because they point OUTWARD from the room in the direction
// of the normal
PoolVector<Vector2> points;
points.resize(4);
points.set(0, Vector2(1, -1));
points.set(1, Vector2(1, 1));
points.set(2, Vector2(-1, 1));
points.set(3, Vector2(-1, -1));
set_points(points); // default shape
}
Portal::~Portal() {
if (_portal_rid != RID()) {
RenderingServer::get_singleton()->free(_portal_rid);
}
}
String Portal::get_configuration_warning() const {
String warning = Spatial::get_configuration_warning();
auto lambda = [](const Node *p_node) {
return static_cast<bool>((Object::cast_to<RoomManager>(p_node) || Object::cast_to<Room>(p_node) || Object::cast_to<RoomGroup>(p_node)));
};
if (Room::detect_nodes_using_lambda(this, lambda)) {
if (Room::detect_nodes_of_type<RoomManager>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("The RoomManager should not be a child or grandchild of a Portal.");
}
if (Room::detect_nodes_of_type<Room>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("A Room should not be a child or grandchild of a Portal.");
}
if (Room::detect_nodes_of_type<RoomGroup>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("A RoomGroup should not be a child or grandchild of a Portal.");
}
}
return warning;
}
void Portal::set_point(int p_idx, const Vector2 &p_point) {
ERR_FAIL_INDEX_MSG(p_idx, _pts_local_raw.size(), "Index out of bounds. Call set_points() to set the size of the array.");
_pts_local_raw.set(p_idx, p_point);
_sanitize_points();
update_gizmos();
}
void Portal::set_points(const PoolVector<Vector2> &p_points) {
_pts_local_raw = p_points;
_sanitize_points();
if (is_inside_tree()) {
portal_update();
update_gizmos();
}
}
PoolVector<Vector2> Portal::get_points() const {
return _pts_local_raw;
}
// extra editor links to the room manager to allow unloading
// on change, or re-converting
void Portal::_changed() {
#ifdef TOOLS_ENABLED
RoomManager *rm = RoomManager::active_room_manager;
if (!rm) {
return;
}
rm->_rooms_changed("changed Portal " + get_name());
#endif
}
void Portal::clear() {
_internal = false;
_linkedroom_ID[0] = -1;
_linkedroom_ID[1] = -1;
_importing_portal = false;
}
void Portal::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_WORLD: {
ERR_FAIL_COND(get_world_3d().is_null());
// defer full creation of the visual server portal to when the editor portal is in the scene tree
RenderingServer::get_singleton()->portal_set_scenario(_portal_rid, get_world_3d()->get_scenario());
// we can't calculate world points until we have entered the tree
portal_update();
update_gizmos();
} break;
case NOTIFICATION_EXIT_WORLD: {
// partially destroy the visual server portal when the editor portal exits the scene tree
RenderingServer::get_singleton()->portal_set_scenario(_portal_rid, RID());
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
// keep the world points and the visual server up to date
portal_update();
// In theory we shouldn't need to update the gizmo when the transform
// changes .. HOWEVER, the portal margin is displayed in world space units,
// back transformed to model space.
// If the Z scale is changed by the user, the portal margin length can become incorrect
// and needs 'resyncing' to the global scale of the portal node.
// We really only need to do this when Z scale is changed, but it is easier codewise
// to always change it, unless we have evidence this is a performance problem.
update_gizmos();
} break;
}
}
void Portal::set_portal_active(bool p_active) {
_settings_active = p_active;
RenderingServer::get_singleton()->portal_set_active(_portal_rid, p_active);
}
bool Portal::get_portal_active() const {
return _settings_active;
}
void Portal::set_use_default_margin(bool p_use) {
_use_default_margin = p_use;
update_gizmos();
}
bool Portal::get_use_default_margin() const {
return _use_default_margin;
}
void Portal::set_portal_margin(real_t p_margin) {
_margin = p_margin;
if (!_use_default_margin) {
// give visual feedback in the editor for the portal margin zone
update_gizmos();
}
}
real_t Portal::get_portal_margin() const {
return _margin;
}
void Portal::resolve_links(const LocalVector<Room *, int32_t> &p_rooms, const RID &p_from_room_rid) {
Room *linkedroom = nullptr;
if (has_node(_settings_path_linkedroom)) {
linkedroom = Object::cast_to<Room>(get_node(_settings_path_linkedroom));
// only allow linking to rooms that are part of the roomlist
// (already recognised).
// If we don't check this, it will start trying to link to Room nodes that are invalid,
// and crash.
if (linkedroom && (p_rooms.find(linkedroom) == -1)) {
// invalid room
WARN_PRINT("Portal attempting to link to Room outside the roomlist : " + linkedroom->get_name());
linkedroom = nullptr;
}
// this should not happen, but just in case
if (linkedroom && (linkedroom->_room_ID >= p_rooms.size())) {
WARN_PRINT("Portal attempting to link to invalid Room : " + linkedroom->get_name());
linkedroom = nullptr;
}
}
if (linkedroom) {
_linkedroom_ID[1] = linkedroom->_room_ID;
// send to visual server
RenderingServer::get_singleton()->portal_link(_portal_rid, p_from_room_rid, linkedroom->_room_rid, _settings_two_way);
} else {
_linkedroom_ID[1] = -1;
}
}
void Portal::set_linked_room_internal(const NodePath &link_path) {
_settings_path_linkedroom = link_path;
}
bool Portal::try_set_unique_name(const String &p_name) {
SceneTree *scene_tree = get_tree();
if (!scene_tree) {
// should not happen in the editor
return false;
}
Viewport *root = scene_tree->get_root();
if (!root) {
return false;
}
Node *found = root->find_node(p_name, true, false);
// if the name does not already exist in the scene tree, we can use it
if (!found) {
set_name(p_name);
return true;
}
// we are trying to set the same name this node already has...
if (found == this) {
// noop
return true;
}
return false;
}
void Portal::set_linked_room(const NodePath &link_path) {
_settings_path_linkedroom = link_path;
// see if the link looks legit
Room *linkedroom = nullptr;
if (has_node(link_path)) {
linkedroom = Object::cast_to<Room>(get_node(link_path));
if (linkedroom) {
if (linkedroom != get_parent()) {
// was ok
} else {
WARN_PRINT("Linked room cannot be the parent room of a portal.");
}
} else {
WARN_PRINT("Linked room path is not a room.");
}
}
_changed();
}
NodePath Portal::get_linked_room() const {
return _settings_path_linkedroom;
}
void Portal::flip() {
// flip portal
Transform tr = get_transform();
Basis flip_basis = Basis(Vector3(0, Math_PI, 0));
tr.basis *= flip_basis;
set_transform(tr);
_pts_local.clear();
_pts_world.clear();
// flip the raw verts
Vector<Vector2> raw;
raw.resize(_pts_local_raw.size());
for (int n = 0; n < _pts_local_raw.size(); n++) {
const Vector2 &pt = _pts_local_raw[n];
raw.set(n, Vector2(-pt.x, pt.y));
}
// standardize raw verts winding
Geometry::sort_polygon_winding(raw, false);
for (int n = 0; n < raw.size(); n++) {
_pts_local_raw.set(n, raw[n]);
}
_sanitize_points();
portal_update();
update_gizmos();
}
bool Portal::create_from_mesh_instance(const MeshInstance *p_mi) {
ERR_FAIL_COND_V(!p_mi, false);
_pts_local.clear();
_pts_world.clear();
Ref<Mesh> rmesh = p_mi->get_mesh();
ERR_FAIL_COND_V(!rmesh.is_valid(), false);
if (rmesh->get_surface_count() == 0) {
WARN_PRINT(vformat("Portal '%s' has no surfaces, ignoring", get_name()));
return false;
}
Array arrays = rmesh->surface_get_arrays(0);
PoolVector<Vector3> vertices = arrays[RS::ARRAY_VERTEX];
PoolVector<int> indices = arrays[RS::ARRAY_INDEX];
// get the model space verts and find center
int num_source_points = vertices.size();
ERR_FAIL_COND_V(num_source_points < 3, false);
const Transform &tr_source = p_mi->get_global_transform();
Vector<Vector3> pts_world;
for (int n = 0; n < num_source_points; n++) {
Vector3 pt = tr_source.xform(vertices[n]);
// test for duplicates.
// Some geometry may contain duplicate verts in portals
// which will muck up the winding etc...
bool duplicate = false;
for (int m = 0; m < pts_world.size(); m++) {
Vector3 diff = pt - pts_world[m];
// hopefully this epsilon will do in nearly all cases
if (diff.length() < 0.001) {
duplicate = true;
break;
}
}
if (!duplicate) {
pts_world.push_back(pt);
}
}
ERR_FAIL_COND_V(pts_world.size() < 3, false);
// create the normal from 3 vertices .. either indexed, or use the first 3
Vector3 three_pts[3];
if (indices.size() >= 3) {
for (int n = 0; n < 3; n++) {
ERR_FAIL_COND_V(indices[n] >= num_source_points, false);
three_pts[n] = tr_source.xform(vertices[indices[n]]);
}
} else {
for (int n = 0; n < 3; n++) {
three_pts[n] = pts_world[n];
}
}
Vector3 normal = Plane(three_pts[0], three_pts[1], three_pts[2]).normal;
if (_portal_plane_convention) {
normal = -normal;
}
// get the verts sorted with winding, assume that the triangle initial winding
// tells us the normal and hence which way the world space portal should be facing
_sort_verts_clockwise(normal, pts_world);
// back calculate the plane from *all* the portal points, this will give us a nice average plane
// (in case of wonky portals where artwork isn't bang on)
_plane = _plane_from_points_newell(pts_world);
// change the portal transform to match our plane and the center of the portal
Transform tr_global;
// prevent warnings when poly normal matches the up vector
Vector3 up(0, 1, 0);
if (Math::abs(_plane.normal.dot(up)) > 0.9) {
up = Vector3(1, 0, 0);
}
tr_global.set_look_at(Vector3(0, 0, 0), _plane.normal, up);
tr_global.origin = _pt_center_world;
// We can't directly set this global transform on the portal, because the parent node may already
// have a transform applied, so we need to account for this and give a corrected local transform
// for the portal, such that the end result global transform will be correct.
// find the difference between this new global transform and the transform of the parent
// then use this for the new local transform of the portal
Spatial *parent = Object::cast_to<Spatial>(get_parent());
ERR_FAIL_COND_V(!parent, false);
Transform tr_inverse_parent = parent->get_global_transform().affine_inverse();
Transform new_local_transform = tr_inverse_parent * tr_global;
set_transform(new_local_transform);
// now back calculate the local space coords of the portal from the world space coords.
// The local space will be used in future for editing and as a 'master' store of the verts.
_pts_local_raw.resize(pts_world.size());
// back transform from global space to local space
Transform tr = tr_global.affine_inverse();
for (int n = 0; n < pts_world.size(); n++) {
// pt3 is now in local space
Vector3 pt3 = tr.xform(pts_world[n]);
// only the x and y required
_pts_local_raw.set(n, Vector2(pt3.x, pt3.y));
// The z coordinate should be approx zero
// DEV_ASSERT(Math::abs(pt3.z) < 0.1);
}
_sanitize_points();
portal_update();
return true;
}
void Portal::_update_aabb() {
_aabb_local = AABB();
if (_pts_local.size()) {
Vector3 begin = _vec2to3(_pts_local[0]);
Vector3 end = begin;
for (int n = 1; n < _pts_local.size(); n++) {
Vector3 pt = _vec2to3(_pts_local[n]);
if (pt.x < begin.x) {
begin.x = pt.x;
}
if (pt.y < begin.y) {
begin.y = pt.y;
}
if (pt.z < begin.z) {
begin.z = pt.z;
}
if (pt.x > end.x) {
end.x = pt.x;
}
if (pt.y > end.y) {
end.y = pt.y;
}
if (pt.z > end.z) {
end.z = pt.z;
}
}
_aabb_local.position = begin;
_aabb_local.size = end - begin;
}
}
void Portal::portal_update() {
// first calculate the plane from the transform
// (portals are standardized outward from source room once sanitized,
// irrespective of the user portal plane convention)
const Transform &tr = get_global_transform();
_plane = Plane(0.0, 0.0, -1.0, 0.0);
_plane = tr.xform(_plane);
// after becoming a portal, the centre world IS the transform origin
_pt_center_world = tr.origin;
// recalculates world points from the local space
int num_points = _pts_local.size();
if (_pts_world.size() != num_points) {
_pts_world.resize(num_points);
}
for (int n = 0; n < num_points; n++) {
_pts_world.set(n, tr.xform(_vec2to3(_pts_local[n])));
}
// no need to check winding order, the points are pre-sanitized only when they change
// extension margin to prevent objects too easily sprawling
real_t margin = get_active_portal_margin();
RenderingServer::get_singleton()->portal_set_geometry(_portal_rid, _pts_world, margin);
}
real_t Portal::get_active_portal_margin() const {
if (_use_default_margin) {
return RoomManager::_get_default_portal_margin();
}
return _margin;
}
void Portal::_sanitize_points() {
// remove duplicates? NYI maybe not necessary
Vector<Vector2> raw;
raw.resize(_pts_local_raw.size());
for (int n = 0; n < _pts_local_raw.size(); n++) {
raw.set(n, _pts_local_raw[n]);
}
// this function may get rid of some concave points due to user editing ..
// may not be necessary, no idea how fast it is
_pts_local = Geometry::convex_hull_2d(raw);
// some peculiarity of convex_hull_2d function, it duplicates the last point for some reason
if (_pts_local.size() > 1) {
_pts_local.resize(_pts_local.size() - 1);
}
// sort winding, the system expects counter clockwise polys
Geometry::sort_polygon_winding(_pts_local, false);
// a bit of a bodge, but a small epsilon pulling in the portal edges towards the center
// can hide walls in the opposite room that abutt the portal (due to floating point error)
// find 2d center
Vector2 center;
for (int n = 0; n < _pts_local.size(); n++) {
center += _pts_local[n];
}
center /= _pts_local.size();
const real_t pull_in = 0.0001;
for (int n = 0; n < _pts_local.size(); n++) {
Vector2 offset = _pts_local[n] - center;
real_t l = offset.length();
// don't apply the pull in for tiny holes
if (l > (pull_in * 2.0)) {
real_t fract = (l - pull_in) / l;
offset *= fract;
_pts_local.set(n, center + offset);
}
}
_update_aabb();
}
void Portal::_sort_verts_clockwise(const Vector3 &p_portal_normal, Vector<Vector3> &r_verts) {
// cannot sort less than 3 verts
if (r_verts.size() < 3) {
return;
}
// find centroid
int num_points = r_verts.size();
_pt_center_world = Vector3(0, 0, 0);
for (int n = 0; n < num_points; n++) {
_pt_center_world += r_verts[n];
}
_pt_center_world /= num_points;
/////////////////////////////////////////
// now algorithm
for (int n = 0; n < num_points - 2; n++) {
Vector3 a = r_verts[n] - _pt_center_world;
a.normalize();
Plane p = Plane(r_verts[n], _pt_center_world, _pt_center_world + p_portal_normal);
double smallest_angle = -1;
int smallest = -1;
for (int m = n + 1; m < num_points; m++) {
if (p.distance_to(r_verts[m]) > 0.0) {
Vector3 b = r_verts[m] - _pt_center_world;
b.normalize();
double angle = a.dot(b);
if (angle > smallest_angle) {
smallest_angle = angle;
smallest = m;
}
} // which side
} // for m
// swap smallest and n+1 vert
if (smallest != -1) {
Vector3 temp = r_verts[smallest];
r_verts.set(smallest, r_verts[n + 1]);
r_verts.set(n + 1, temp);
}
} // for n
// the vertices are now sorted, but may be in the opposite order to that wanted.
// we detect this by calculating the normal of the poly, then flipping the order if the normal is pointing
// the wrong way.
Plane plane = Plane(r_verts[0], r_verts[1], r_verts[2]);
if (p_portal_normal.dot(plane.normal) < 0.0) {
// reverse winding order of verts
r_verts.invert();
}
}
Plane Portal::_plane_from_points_newell(const Vector<Vector3> &p_pts) {
int num_points = p_pts.size();
if (num_points < 3) {
return Plane();
}
Vector3 normal;
Vector3 center;
for (int i = 0; i < num_points; i++) {
int j = (i + 1) % num_points;
const Vector3 &pi = p_pts[i];
const Vector3 &pj = p_pts[j];
center += pi;
normal.x += (((pi.z) + (pj.z)) * ((pj.y) - (pi.y)));
normal.y += (((pi.x) + (pj.x)) * ((pj.z) - (pi.z)));
normal.z += (((pi.y) + (pj.y)) * ((pj.x) - (pi.x)));
}
normal.normalize();
center /= num_points;
_pt_center_world = center;
// point and normal
return Plane(center, normal);
}
void Portal::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_portal_active", "p_active"), &Portal::set_portal_active);
ClassDB::bind_method(D_METHOD("get_portal_active"), &Portal::get_portal_active);
ClassDB::bind_method(D_METHOD("set_two_way", "p_two_way"), &Portal::set_two_way);
ClassDB::bind_method(D_METHOD("is_two_way"), &Portal::is_two_way);
ClassDB::bind_method(D_METHOD("set_use_default_margin", "p_use"), &Portal::set_use_default_margin);
ClassDB::bind_method(D_METHOD("get_use_default_margin"), &Portal::get_use_default_margin);
ClassDB::bind_method(D_METHOD("set_portal_margin", "p_margin"), &Portal::set_portal_margin);
ClassDB::bind_method(D_METHOD("get_portal_margin"), &Portal::get_portal_margin);
ClassDB::bind_method(D_METHOD("set_linked_room", "p_room"), &Portal::set_linked_room);
ClassDB::bind_method(D_METHOD("get_linked_room"), &Portal::get_linked_room);
ClassDB::bind_method(D_METHOD("set_points", "points"), &Portal::set_points);
ClassDB::bind_method(D_METHOD("get_points"), &Portal::get_points);
ClassDB::bind_method(D_METHOD("set_point", "index", "position"), &Portal::set_point);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "portal_active"), "set_portal_active", "get_portal_active");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "two_way"), "set_two_way", "is_two_way");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "linked_room", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Room"), "set_linked_room", "get_linked_room");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_default_margin"), "set_use_default_margin", "get_use_default_margin");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "portal_margin", PROPERTY_HINT_RANGE, "0.0,10.0,0.01"), "set_portal_margin", "get_portal_margin");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
}

View File

@ -1,190 +0,0 @@
#ifndef PORTAL_H
#define PORTAL_H
/*************************************************************************/
/* portal.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "core/containers/local_vector.h"
#include "core/containers/rid.h"
#include "scene/main/spatial.h"
class RoomManager;
class MeshInstance;
class Room;
class Portal : public Spatial {
GDCLASS(Portal, Spatial);
RID _portal_rid;
friend class RoomManager;
friend class PortalGizmoPlugin;
friend class PortalEditorPlugin;
friend class PortalSpatialGizmo;
public:
// ui interface .. will have no effect after room conversion
void set_linked_room(const NodePath &link_path);
NodePath get_linked_room() const;
// open and close doors
void set_portal_active(bool p_active);
bool get_portal_active() const;
// whether the portal can be seen through in both directions or not
void set_two_way(bool p_two_way) {
_settings_two_way = p_two_way;
_changed();
}
bool is_two_way() const { return _settings_two_way; }
// call during each conversion
void clear();
// whether to use the room manager default
void set_use_default_margin(bool p_use);
bool get_use_default_margin() const;
// custom portal margin (per portal) .. only valid if use_default_margin is off
void set_portal_margin(real_t p_margin);
real_t get_portal_margin() const;
// either the default margin or the custom portal margin, depending on the setting
real_t get_active_portal_margin() const;
// the raw points are used for the IDE Inspector, and also to allow the user
// to edit the geometry of the portal at runtime (they can also just change the portal node transform)
void set_points(const PoolVector<Vector2> &p_points);
PoolVector<Vector2> get_points() const;
// primarily for the gizmo
void set_point(int p_idx, const Vector2 &p_point);
String get_configuration_warning() const;
Portal();
~Portal();
// whether the convention is that the normal of the portal points outward (false) or inward (true)
// normally I'd recommend portal normal faces outward. But you may make a booboo, so this can work
// with either convention.
static bool _portal_plane_convention;
private:
// updates world coords when the transform changes, and updates the visual server
void portal_update();
void set_linked_room_internal(const NodePath &link_path);
bool try_set_unique_name(const String &p_name);
bool is_portal_internal(int p_room_outer) const { return _internal && (_linkedroom_ID[0] != p_room_outer); }
bool create_from_mesh_instance(const MeshInstance *p_mi);
void flip();
void _sanitize_points();
void _update_aabb();
static Vector3 _vec2to3(const Vector2 &p_pt) { return Vector3(p_pt.x, p_pt.y, 0.0); }
void _sort_verts_clockwise(const Vector3 &p_portal_normal, Vector<Vector3> &r_verts);
Plane _plane_from_points_newell(const Vector<Vector3> &p_pts);
void resolve_links(const LocalVector<Room *, int32_t> &p_rooms, const RID &p_from_room_rid);
void _changed();
// nodepath to the room this outgoing portal leads to
NodePath _settings_path_linkedroom;
// portal can be turned on and off at runtime, for e.g.
// opening and closing a door
bool _settings_active;
// user can choose not to include the portal in the convex hull of the room
// during conversion
bool _settings_include_in_bound;
// portals can be seen through one way or two way
bool _settings_two_way;
// room from and to, ID in the room manager
int _linkedroom_ID[2];
// whether the portal is from a room within a room
bool _internal;
// normal determined by winding order
Vector<Vector3> _pts_world;
// points in local space of the plane,
// not necessary in correct winding order
// (as they can be edited by the user)
// Note: these are saved by the IDE
PoolVector<Vector2> _pts_local_raw;
// sanitized
Vector<Vector2> _pts_local;
AABB _aabb_local;
// center of the world points
Vector3 _pt_center_world;
// portal plane in world space, always pointing OUTWARD from the source room
Plane _plane;
// extension margin
real_t _margin;
bool _use_default_margin;
// during conversion, we need to know
// whether this portal is being imported from a mesh
// and is using an explicitly named link room with prefix.
// If this is not the case, and it is already a Pandemonium Portal node,
// we will either use the assigned nodepath, or autolink.
bool _importing_portal = false;
// for editing
#ifdef TOOLS_ENABLED
ObjectID _room_manager_pandemonium_ID;
// 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
static bool _settings_gizmo_show_margins;
public:
// makes sure portals are not converted more than once per
// call to rooms_convert
int _conversion_tick = -1;
protected:
static void _bind_methods();
void _notification(int p_what);
};
#endif

View File

@ -1,294 +0,0 @@
/*************************************************************************/
/* room.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "room.h"
#include "portal.h"
#include "room_group.h"
#include "room_manager.h"
#include "scene/resources/world_3d.h"
#include "servers/rendering_server.h"
void Room::SimplifyInfo::set_simplify(real_t p_value, real_t p_room_size) {
_plane_simplify = CLAMP(p_value, 0.0, 1.0);
// just for reference in case we later want to use degrees...
// _plane_simplify_dot = Math::cos(Math::deg2rad(_plane_simplify_degrees));
// _plane_simplify_dot = _plane_simplify;
// _plane_simplify_dot *= _plane_simplify_dot;
// _plane_simplify_dot = 1.0 - _plane_simplify_dot;
// distance based on size of room
// _plane_simplify_dist = p_room_size * 0.1 * _plane_simplify;
// _plane_simplify_dist = MAX(_plane_simplify_dist, 0.08);
// test fix
_plane_simplify_dot = 0.99;
_plane_simplify_dist = 0.08;
// print_verbose("plane simplify dot : " + String(Variant(_plane_simplify_dot)));
// print_verbose("plane simplify dist : " + String(Variant(_plane_simplify_dist)));
}
bool Room::SimplifyInfo::add_plane_if_unique(LocalVector<Plane, int32_t> &r_planes, const Plane &p) const {
for (int n = 0; n < r_planes.size(); n++) {
const Plane &o = r_planes[n];
// this is a fudge factor for how close planes can be to be considered the same ...
// to prevent ridiculous amounts of planes
const real_t d = _plane_simplify_dist; // 0.08f
if (Math::abs(p.d - o.d) > d) {
continue;
}
real_t dot = p.normal.dot(o.normal);
if (dot < _plane_simplify_dot) // 0.98f
{
continue;
}
// match!
return false;
}
r_planes.push_back(p);
return true;
}
void Room::clear() {
_room_ID = -1;
_planes.clear();
_preliminary_planes.clear();
_roomgroups.clear();
_portals.clear();
_bound_mesh_data.edges.clear();
_bound_mesh_data.faces.clear();
_bound_mesh_data.vertices.clear();
_aabb = AABB();
#ifdef TOOLS_ENABLED
_gizmo_overlap_zones.clear();
#endif
}
Room::Room() {
_room_rid = RID_PRIME(RenderingServer::get_singleton()->room_create());
}
Room::~Room() {
if (_room_rid != RID()) {
RenderingServer::get_singleton()->free(_room_rid);
}
}
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());
}
void Room::set_use_default_simplify(bool p_use) {
_use_default_simplify = p_use;
}
void Room::set_point(int p_idx, const Vector3 &p_point) {
ERR_FAIL_INDEX_MSG(p_idx, _bound_pts.size(), "Index out of bounds. Call set_points() to set the size of the array.");
_bound_pts.set(p_idx, p_point);
#ifdef TOOLS_ENABLED
_changed(true);
#endif
}
void Room::set_points(const PoolVector<Vector3> &p_points) {
_bound_pts = p_points;
#ifdef TOOLS_ENABLED
if (p_points.size()) {
_changed(true);
}
#endif
}
PoolVector<Vector3> Room::get_points() const {
return _bound_pts;
}
PoolVector<Vector3> Room::generate_points() {
PoolVector<Vector3> pts_returned;
#ifdef TOOLS_ENABLED
// do a rooms convert to make sure the planes are up to date
RoomManager *rm = RoomManager::active_room_manager;
if (rm) {
rm->rooms_convert();
}
if (!_planes.size()) {
return pts_returned;
}
// scale an epsilon using 10.0 for a normal sized room
real_t scaled_epsilon = _aabb.get_longest_axis_size() / 10.0;
scaled_epsilon = MAX(scaled_epsilon * 0.01, 0.001);
LocalVector<Vector3, int32_t> pts;
pts = Geometry::compute_convex_mesh_points(&_planes[0], _planes.size(), scaled_epsilon);
// eliminate duplicates
for (int n = 0; n < pts.size(); n++) {
const Vector3 &a = pts[n];
for (int m = n + 1; m < pts.size(); m++) {
const Vector3 &b = pts[m];
if (a.is_equal_approx(b, scaled_epsilon)) {
// remove b
pts.remove_unordered(m);
m--; // repeat m as the new m is the old last
}
}
}
// convert vector to poolvector
pts_returned.resize(pts.size());
Transform tr = get_global_transform();
tr.affine_invert();
for (int n = 0; n < pts.size(); n++) {
// the points should be saved in LOCAL space,
// so that if we move the room afterwards, the bound points
// will also move in relation to the room.
pts_returned.set(n, tr.xform(pts[n]));
}
#endif
return pts_returned;
}
String Room::get_configuration_warning() const {
String warning = Spatial::get_configuration_warning();
auto lambda = [](const Node *p_node) {
return static_cast<bool>((Object::cast_to<Room>(p_node) || Object::cast_to<RoomManager>(p_node) || Object::cast_to<RoomGroup>(p_node)));
};
if (detect_nodes_using_lambda(this, lambda)) {
if (detect_nodes_of_type<Room>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("A Room cannot have another Room as a child or grandchild.");
}
if (detect_nodes_of_type<RoomManager>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("The RoomManager should not be placed inside a Room.");
}
if (detect_nodes_of_type<RoomGroup>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("A RoomGroup should not be placed inside a Room.");
}
}
if (_planes.size() > 80) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("Room convex hull contains a large number of planes.\nConsider simplifying the room bound in order to increase performance.");
}
return warning;
}
// extra editor links to the room manager to allow unloading
// on change, or re-converting
void Room::_changed(bool p_regenerate_bounds) {
#ifdef TOOLS_ENABLED
RoomManager *rm = RoomManager::active_room_manager;
if (!rm) {
return;
}
if (p_regenerate_bounds) {
rm->_room_regenerate_bound(this);
}
rm->_rooms_changed("changed Room " + get_name());
#endif
}
void Room::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_WORLD: {
ERR_FAIL_COND(get_world_3d().is_null());
RenderingServer::get_singleton()->room_set_scenario(_room_rid, get_world_3d()->get_scenario());
} break;
case NOTIFICATION_EXIT_WORLD: {
RenderingServer::get_singleton()->room_set_scenario(_room_rid, RID());
} break;
}
}
void Room::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_default_simplify", "p_use"), &Room::set_use_default_simplify);
ClassDB::bind_method(D_METHOD("get_use_default_simplify"), &Room::get_use_default_simplify);
ClassDB::bind_method(D_METHOD("set_room_simplify", "p_value"), &Room::set_room_simplify);
ClassDB::bind_method(D_METHOD("get_room_simplify"), &Room::get_room_simplify);
ClassDB::bind_method(D_METHOD("set_points", "points"), &Room::set_points);
ClassDB::bind_method(D_METHOD("get_points"), &Room::get_points);
ClassDB::bind_method(D_METHOD("set_point", "index", "position"), &Room::set_point);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_default_simplify"), "set_use_default_simplify", "get_use_default_simplify");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "room_simplify", PROPERTY_HINT_RANGE, "0.0,1.0,0.005"), "set_room_simplify", "get_room_simplify");
ADD_GROUP("Bound", "");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "points"), "set_points", "get_points");
}

View File

@ -1,175 +0,0 @@
#ifndef ROOM_H
#define ROOM_H
/*************************************************************************/
/* room.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "core/containers/local_vector.h"
#include "core/math/geometry.h"
#include "core/containers/rid.h"
#include "scene/main/spatial.h"
class Portal;
class Room : public Spatial {
GDCLASS(Room, Spatial);
friend class RoomManager;
friend class RoomGroup;
friend class Portal;
friend class RoomGizmoPlugin;
friend class RoomEditorPlugin;
friend class RoomSpatialGizmo;
RID _room_rid;
public:
struct SimplifyInfo {
SimplifyInfo() { set_simplify(0.5); }
void set_simplify(real_t p_value, real_t p_room_size = 0.0);
bool add_plane_if_unique(LocalVector<Plane, int32_t> &r_planes, const Plane &p) const;
real_t _plane_simplify = 0.5;
real_t _plane_simplify_dot = 0.98;
real_t _plane_simplify_dist = 0.08;
};
Room();
~Room();
void set_room_simplify(real_t p_value);
real_t get_room_simplify() const { return _simplify_info._plane_simplify; }
// whether to use the room manager default
void set_use_default_simplify(bool p_use);
bool get_use_default_simplify() const { return _use_default_simplify; }
void set_points(const PoolVector<Vector3> &p_points);
PoolVector<Vector3> get_points() const;
// primarily for the gizmo
void set_point(int p_idx, const Vector3 &p_point);
// editor only
PoolVector<Vector3> generate_points();
String get_configuration_warning() const;
private:
// call during each conversion
void clear();
void _changed(bool p_regenerate_bounds = false);
template <class T>
static bool detect_nodes_of_type(const Node *p_node, bool p_ignore_first_node = true);
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;
// preliminary planes are created during the first conversion pass,
// they do not include the portals, and are used for identifying auto
// linkage of rooms by portals
LocalVector<Plane, int32_t> _preliminary_planes;
Geometry::MeshData _bound_mesh_data;
AABB _aabb;
// editable points making up the bound
PoolVector<Vector3> _bound_pts;
#ifdef TOOLS_ENABLED
// to help with editing, when converting, we can generate overlap zones
// that occur between rooms. Ideally these should not occur, as rooms
// should be convex and non-overlapping. But if they do occur, they should
// be minimized.
Vector<Geometry::MeshData> _gizmo_overlap_zones;
#endif
// makes sure lrooms are not converted more than once per
// call to rooms_convert
int _conversion_tick = -1;
// room ID during conversion, used for matching portals links to rooms
int _room_ID;
// room priority allows rooms to be placed inside other rooms,
// such as a house on a landscape room.
// If the camera is inside more than one room, the higher priority room
// will *win* (e.g. house, rather than landscape)
int _room_priority = 0;
// a room may be in one or several roomgroups
LocalVector<int, int32_t> _roomgroups;
// list of portal ids from or to this room, just used in conversion to determine room bound
LocalVector<int, int32_t> _portals;
// each room now stores simplification data
SimplifyInfo _simplify_info;
bool _use_default_simplify = true;
protected:
static void _bind_methods();
void _notification(int p_what);
};
template <class T>
bool Room::detect_nodes_of_type(const Node *p_node, bool p_ignore_first_node) {
if (Object::cast_to<T>(p_node) && (!p_ignore_first_node)) {
return true;
}
for (int n = 0; n < p_node->get_child_count(); n++) {
if (detect_nodes_of_type<T>(p_node->get_child(n), false)) {
return true;
}
}
return false;
}
template <typename T>
bool Room::detect_nodes_using_lambda(const Node *p_node, T p_lambda, bool p_ignore_first_node) {
if (p_lambda(p_node) && !p_ignore_first_node) {
return true;
}
for (int n = 0; n < p_node->get_child_count(); n++) {
if (detect_nodes_using_lambda(p_node->get_child(n), p_lambda, false)) {
return true;
}
}
return false;
}
#endif

View File

@ -1,101 +0,0 @@
/*************************************************************************/
/* room_group.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "room_group.h"
#include "core/math/geometry.h"
#include "room.h"
#include "room_manager.h"
#include "scene/resources/mesh/mesh.h"
#include "scene/resources/world_3d.h"
#include "servers/rendering_server.h"
void RoomGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_roomgroup_priority", "p_priority"), &RoomGroup::set_roomgroup_priority);
ClassDB::bind_method(D_METHOD("get_roomgroup_priority"), &RoomGroup::get_roomgroup_priority);
ADD_PROPERTY(PropertyInfo(Variant::INT, "roomgroup_priority", PROPERTY_HINT_RANGE, "-16,16,1", PROPERTY_USAGE_DEFAULT), "set_roomgroup_priority", "get_roomgroup_priority");
}
RoomGroup::RoomGroup() {
_room_group_rid = RID_PRIME(RenderingServer::get_singleton()->roomgroup_create());
}
RoomGroup::~RoomGroup() {
if (_room_group_rid != RID()) {
RenderingServer::get_singleton()->free(_room_group_rid);
}
}
String RoomGroup::get_configuration_warning() const {
String warning = Spatial::get_configuration_warning();
if (Room::detect_nodes_of_type<RoomManager>(this)) {
if (!warning.empty()) {
warning += "\n\n";
}
warning += TTR("The RoomManager should not be placed inside a RoomGroup.");
}
return warning;
}
void RoomGroup::clear() {
_roomgroup_ID = -1;
}
void RoomGroup::add_room(Room *p_room) {
RenderingServer::get_singleton()->roomgroup_add_room(_room_group_rid, p_room->_room_rid);
}
// extra editor links to the room manager to allow unloading
// on change, or re-converting
void RoomGroup::_changed() {
#ifdef TOOLS_ENABLED
RoomManager *rm = RoomManager::active_room_manager;
if (!rm) {
return;
}
rm->_rooms_changed("changed RoomGroup " + get_name());
#endif
}
void RoomGroup::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_WORLD: {
ERR_FAIL_COND(get_world_3d().is_null());
RenderingServer::get_singleton()->roomgroup_set_scenario(_room_group_rid, get_world_3d()->get_scenario());
} break;
case NOTIFICATION_EXIT_WORLD: {
RenderingServer::get_singleton()->roomgroup_set_scenario(_room_group_rid, RID());
} break;
}
}

View File

@ -1,80 +0,0 @@
#ifndef ROOM_GROUP_H
#define ROOM_GROUP_H
/*************************************************************************/
/* room_group.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "core/containers/rid.h"
#include "scene/main/spatial.h"
class Room;
class RoomGroup : public Spatial {
GDCLASS(RoomGroup, Spatial);
friend class RoomManager;
RID _room_group_rid;
public:
RoomGroup();
~RoomGroup();
void add_room(Room *p_room);
void set_roomgroup_priority(int p_priority) {
_settings_priority = p_priority;
_changed();
}
int get_roomgroup_priority() const { return _settings_priority; }
String get_configuration_warning() const;
private:
void clear();
void _changed();
// roomgroup ID during conversion
int _roomgroup_ID;
// the roomgroup can be used to set a number of rooms to a different priority
// to allow a group of rooms WITHIN another room / rooms.
// This is for e.g. buildings on landscape.
int _settings_priority = 0;
// makes sure lrooms are not converted more than once per
// call to rooms_convert
int _conversion_tick = -1;
protected:
static void _bind_methods();
void _notification(int p_what);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,279 +0,0 @@
#ifndef ROOM_MANAGER_H
#define ROOM_MANAGER_H
/*************************************************************************/
/* room_manager.h */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 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 "core/containers/local_vector.h"
#include "room.h"
#include "scene/main/spatial.h"
class Portal;
class RoomGroup;
class MeshInstance;
class GeometryInstance;
class VisualInstance;
#define PANDEMONIUM_PORTAL_WILDCARD ('*')
class RoomManager : public Spatial {
GDCLASS(RoomManager, Spatial);
public:
enum PVSMode {
PVS_MODE_DISABLED,
PVS_MODE_PARTIAL,
PVS_MODE_FULL,
};
void set_roomlist_path(const NodePath &p_path);
NodePath get_roomlist_path() const {
return _settings_path_roomlist;
}
void set_preview_camera_path(const NodePath &p_path);
NodePath get_preview_camera_path() const {
return _settings_path_preview_camera;
}
void rooms_set_active(bool p_active);
bool rooms_get_active() const;
void set_show_margins(bool p_show);
bool get_show_margins() const;
void set_debug_sprawl(bool p_enable);
bool get_debug_sprawl() const;
void set_merge_meshes(bool p_enable);
bool get_merge_meshes() const;
void set_room_simplify(real_t p_value);
real_t get_room_simplify() const;
void set_default_portal_margin(real_t p_dist);
real_t get_default_portal_margin() const;
void set_overlap_warning_threshold(int p_value) { _overlap_warning_threshold = p_value; }
int get_overlap_warning_threshold() const { return (int)_overlap_warning_threshold; }
void set_portal_depth_limit(int p_limit);
int get_portal_depth_limit() const { return _settings_portal_depth_limit; }
void set_roaming_expansion_margin(real_t p_dist);
real_t get_roaming_expansion_margin() const { return _settings_roaming_expansion_margin; }
void set_pvs_mode(PVSMode p_mode);
PVSMode get_pvs_mode() const;
void set_pvs_filename(String p_filename);
String get_pvs_filename() const;
void set_use_secondary_pvs(bool p_enable) { _settings_use_secondary_pvs = p_enable; }
bool get_use_secondary_pvs() const { return _settings_use_secondary_pvs; }
void set_gameplay_monitor_enabled(bool p_enable) { _settings_gameplay_monitor_enabled = p_enable; }
bool get_gameplay_monitor_enabled() const { return _settings_gameplay_monitor_enabled; }
void rooms_convert();
void rooms_clear();
void rooms_flip_portals();
String get_configuration_warning() const;
// for internal use in the editor..
// either we can clear the rooms and unload,
// or reconvert.
void _rooms_changed(String p_reason);
#ifdef TOOLS_ENABLED
// for a preview, we allow the editor to change the bound
bool _room_regenerate_bound(Room *p_room);
#endif
RoomManager();
~RoomManager();
// an easy way of grabbing the active room manager for tools purposes
#ifdef TOOLS_ENABLED
static RoomManager *active_room_manager;
// static versions of functions for use from editor toolbars
static void static_rooms_set_active(bool p_active);
static bool static_rooms_get_active();
static bool static_rooms_get_active_and_loaded();
static void static_rooms_convert();
#endif
private:
// funcs
bool resolve_preview_camera_path();
void _preview_camera_update();
// conversion
// FIRST PASS
void _convert_rooms_recursive(Spatial *p_node, LocalVector<Portal *> &r_portals, LocalVector<RoomGroup *> &r_roomgroups, int p_roomgroup = -1);
void _convert_room(Spatial *p_node, LocalVector<Portal *> &r_portals, const LocalVector<RoomGroup *> &p_roomgroups, int p_roomgroup);
int _convert_roomgroup(Spatial *p_node, LocalVector<RoomGroup *> &r_roomgroups);
void _find_portals_recursive(Spatial *p_node, Room *p_room, LocalVector<Portal *> &r_portals);
void _convert_portal(Room *p_room, Spatial *p_node, LocalVector<Portal *> &portals);
// SECOND PASS
void _second_pass_portals(Spatial *p_roomlist, LocalVector<Portal *> &r_portals);
void _second_pass_rooms(const LocalVector<RoomGroup *> &p_roomgroups, const LocalVector<Portal *> &p_portals);
void _second_pass_room(Room *p_room, const LocalVector<RoomGroup *> &p_roomgroups, const LocalVector<Portal *> &p_portals);
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);
bool _bound_findpoints_mesh_instance(MeshInstance *p_mi, Vector<Vector3> &r_room_pts, AABB &r_aabb);
bool _bound_findpoints_geom_instance(GeometryInstance *p_gi, Vector<Vector3> &r_room_pts, AABB &r_aabb);
// THIRD PASS
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_gizmos(Spatial *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
void _generate_room_overlap_zones();
#endif
// merging
void _merge_meshes_in_room(Room *p_room);
void _list_mergeable_mesh_instances(Spatial *p_node, LocalVector<MeshInstance *, int32_t> &r_list);
void _merge_log(String p_string) { debug_print_line(p_string); }
bool _remove_redundant_dangling_nodes(Spatial *p_node);
// helper funcs
bool _name_ends_with(const Node *p_node, String p_postfix) const;
template <class NODE_TYPE>
NODE_TYPE *_resolve_path(NodePath p_path) const;
template <class NODE_TYPE>
bool _node_is_type(Node *p_node) const;
template <class T>
T *_change_node_type(Spatial *p_node, String p_prefix, bool p_delete = true);
void _update_gizmos_recursive(Node *p_node);
void _set_owner_recursive(Node *p_node, Node *p_owner);
void _flip_portals_recursive(Spatial *p_node);
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, 1); }
// only prints when user has set 'debug' in the room manager inspector
// also does not show in non editor builds
void debug_print_line(String p_string, int p_priority = 0);
void show_warning(const String &p_string, bool p_skippable = false, bool p_alert = true);
public:
static String _find_name_before(Node *p_node, String p_postfix, bool p_allow_no_postfix = false);
static real_t _get_default_portal_margin() { return _default_portal_margin; }
private:
// accessible from UI
NodePath _settings_path_roomlist;
NodePath _settings_path_preview_camera;
// resolved node
Spatial *_roomlist = nullptr;
bool _warning_misnamed_nodes_detected = false;
bool _warning_portal_link_room_not_found = false;
bool _warning_portal_autolink_failed = false;
bool _warning_room_overlap_detected = false;
// merge suitable meshes in rooms?
bool _settings_merge_meshes = false;
// remove redundant childless spatials after merging
bool _settings_remove_danglers = true;
bool _active = true;
// portals, room hulls etc
bool _show_debug = true;
bool _debug_sprawl = false;
// pvs
PVSMode _pvs_mode = PVS_MODE_PARTIAL;
String _pvs_filename;
bool _settings_use_secondary_pvs = false;
bool _settings_use_simple_pvs = false;
bool _settings_log_pvs_generation = false;
bool _settings_use_signals = true;
bool _settings_gameplay_monitor_enabled = false;
int _conversion_tick = 0;
// just used during conversion, could be invalidated
// later by user deleting rooms etc.
LocalVector<Room *, int32_t> _rooms;
// advanced params
static real_t _default_portal_margin;
real_t _overlap_warning_threshold = 1.0;
Room::SimplifyInfo _room_simplify_info;
int _settings_portal_depth_limit = 16;
real_t _settings_roaming_expansion_margin = 1.0;
// debug override camera
ObjectID _pandemonium_preview_camera_ID = -1;
// local version of the pandemonium camera frustum,
// to prevent updating the visual server (and causing
// a screen refresh) where not necessary.
Vector3 _pandemonium_camera_pos;
Vector<Plane> _pandemonium_camera_planes;
protected:
static void _bind_methods();
void _notification(int p_what);
void _refresh_from_project_settings();
};
VARIANT_ENUM_CAST(RoomManager::PVSMode);
#endif

View File

@ -190,14 +190,10 @@
#include "scene/3d/multimesh_instance.h" #include "scene/3d/multimesh_instance.h"
#include "scene/3d/occluder.h" #include "scene/3d/occluder.h"
#include "scene/3d/path.h" #include "scene/3d/path.h"
#include "scene/3d/portal.h"
#include "scene/3d/position_3d.h" #include "scene/3d/position_3d.h"
#include "scene/3d/proximity_group.h" #include "scene/3d/proximity_group.h"
#include "scene/3d/reflection_probe.h" #include "scene/3d/reflection_probe.h"
#include "scene/3d/remote_transform.h" #include "scene/3d/remote_transform.h"
#include "scene/3d/room.h"
#include "scene/3d/room_group.h"
#include "scene/3d/room_manager.h"
#include "scene/3d/spatial_velocity_tracker.h" #include "scene/3d/spatial_velocity_tracker.h"
#include "scene/3d/sprite_3d.h" #include "scene/3d/sprite_3d.h"
#include "scene/3d/visibility_notifier.h" #include "scene/3d/visibility_notifier.h"
@ -429,11 +425,7 @@ void register_scene_types() {
ClassDB::register_class<CPUParticles>(); ClassDB::register_class<CPUParticles>();
ClassDB::register_class<Position3D>(); ClassDB::register_class<Position3D>();
ClassDB::register_class<NavigationMesh>(); ClassDB::register_class<NavigationMesh>();
ClassDB::register_class<Room>();
ClassDB::register_class<RoomGroup>();
ClassDB::register_class<RoomManager>();
ClassDB::register_class<Occluder>(); ClassDB::register_class<Occluder>();
ClassDB::register_class<Portal>();
ClassDB::register_class<RootMotionView>(); ClassDB::register_class<RootMotionView>();
ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor