mirror of
https://github.com/Relintai/pandemonium_engine_minimal.git
synced 2025-01-04 20:59:36 +01:00
Removed room and portal nodes.
This commit is contained in:
parent
f944578178
commit
b248f94cdc
@ -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");
|
||||
}
|
@ -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
|
@ -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");
|
||||
}
|
175
scene/3d/room.h
175
scene/3d/room.h
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
@ -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
|
@ -190,14 +190,10 @@
|
||||
#include "scene/3d/multimesh_instance.h"
|
||||
#include "scene/3d/occluder.h"
|
||||
#include "scene/3d/path.h"
|
||||
#include "scene/3d/portal.h"
|
||||
#include "scene/3d/position_3d.h"
|
||||
#include "scene/3d/proximity_group.h"
|
||||
#include "scene/3d/reflection_probe.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/sprite_3d.h"
|
||||
#include "scene/3d/visibility_notifier.h"
|
||||
@ -429,11 +425,7 @@ void register_scene_types() {
|
||||
ClassDB::register_class<CPUParticles>();
|
||||
ClassDB::register_class<Position3D>();
|
||||
ClassDB::register_class<NavigationMesh>();
|
||||
ClassDB::register_class<Room>();
|
||||
ClassDB::register_class<RoomGroup>();
|
||||
ClassDB::register_class<RoomManager>();
|
||||
ClassDB::register_class<Occluder>();
|
||||
ClassDB::register_class<Portal>();
|
||||
|
||||
ClassDB::register_class<RootMotionView>();
|
||||
ClassDB::set_class_enabled("RootMotionView", false); //disabled by default, enabled by editor
|
||||
|
Loading…
Reference in New Issue
Block a user