Portals - fix import of portal normal + small bug fixes

When converting portal meshes during import, indices were not being taken into account, which could lead to incorrect estimation of the portal direction. This PR now copes with either indexed or non-indexed portal meshes.

Added a bug fix to cope with portals pointing almost directly straight up or down, which could cause problems with the lookat transform.

Added the ability for named portals to link to short room names (in addition to postfix room names).
This commit is contained in:
lawnjelly 2021-09-02 08:39:42 +01:00
parent 98e1b730c8
commit 116e2ce799
3 changed files with 40 additions and 17 deletions

View File

@ -360,6 +360,7 @@ bool Portal::create_from_mesh_instance(const MeshInstance *p_mi) {
Array arrays = rmesh->surface_get_arrays(0); Array arrays = rmesh->surface_get_arrays(0);
PoolVector<Vector3> vertices = arrays[VS::ARRAY_VERTEX]; PoolVector<Vector3> vertices = arrays[VS::ARRAY_VERTEX];
PoolVector<int> indices = arrays[VS::ARRAY_INDEX];
// get the model space verts and find center // get the model space verts and find center
int num_source_points = vertices.size(); int num_source_points = vertices.size();
@ -391,9 +392,28 @@ bool Portal::create_from_mesh_instance(const MeshInstance *p_mi) {
} }
} }
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 // 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 // tells us the normal and hence which way the world space portal should be facing
_sort_verts_clockwise(_portal_plane_convention, pts_world); _sort_verts_clockwise(normal, pts_world);
// back calculate the plane from *all* the portal points, this will give us a nice average plane // 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) // (in case of wonky portals where artwork isn't bang on)
@ -401,7 +421,14 @@ bool Portal::create_from_mesh_instance(const MeshInstance *p_mi) {
// change the portal transform to match our plane and the center of the portal // change the portal transform to match our plane and the center of the portal
Transform tr_global; Transform tr_global;
tr_global.set_look_at(Vector3(0, 0, 0), _plane.normal, Vector3(0, 1, 0));
// 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; tr_global.origin = _pt_center_world;
// We can't directly set this global transform on the portal, because the parent node may already // We can't directly set this global transform on the portal, because the parent node may already
@ -558,23 +585,12 @@ void Portal::_sanitize_points() {
_update_aabb(); _update_aabb();
} }
void Portal::_sort_verts_clockwise(bool portal_plane_convention, Vector<Vector3> &r_verts) { void Portal::_sort_verts_clockwise(const Vector3 &p_portal_normal, Vector<Vector3> &r_verts) {
// cannot sort less than 3 verts // cannot sort less than 3 verts
if (r_verts.size() < 3) { if (r_verts.size() < 3) {
return; return;
} }
// assume first 3 points determine the desired normal, if these first 3 points are garbage,
// the routine will not work.
Plane portal_plane;
if (portal_plane_convention) {
portal_plane = Plane(r_verts[0], r_verts[2], r_verts[1]);
} else {
portal_plane = Plane(r_verts[0], r_verts[1], r_verts[2]);
}
const Vector3 &portal_normal = portal_plane.normal;
// find centroid // find centroid
int num_points = r_verts.size(); int num_points = r_verts.size();
_pt_center_world = Vector3(0, 0, 0); _pt_center_world = Vector3(0, 0, 0);
@ -590,7 +606,7 @@ void Portal::_sort_verts_clockwise(bool portal_plane_convention, Vector<Vector3>
Vector3 a = r_verts[n] - _pt_center_world; Vector3 a = r_verts[n] - _pt_center_world;
a.normalize(); a.normalize();
Plane p = Plane(r_verts[n], _pt_center_world, _pt_center_world + portal_normal); Plane p = Plane(r_verts[n], _pt_center_world, _pt_center_world + p_portal_normal);
double smallest_angle = -1; double smallest_angle = -1;
int smallest = -1; int smallest = -1;
@ -623,7 +639,7 @@ void Portal::_sort_verts_clockwise(bool portal_plane_convention, Vector<Vector3>
// the wrong way. // the wrong way.
Plane plane = Plane(r_verts[0], r_verts[1], r_verts[2]); Plane plane = Plane(r_verts[0], r_verts[1], r_verts[2]);
if (portal_normal.dot(plane.normal) < 0.0f) { if (p_portal_normal.dot(plane.normal) < 0.0) {
// reverse winding order of verts // reverse winding order of verts
r_verts.invert(); r_verts.invert();
} }

View File

@ -110,7 +110,7 @@ private:
void _sanitize_points(); void _sanitize_points();
void _update_aabb(); void _update_aabb();
static Vector3 _vec2to3(const Vector2 &p_pt) { return Vector3(p_pt.x, p_pt.y, 0.0); } static Vector3 _vec2to3(const Vector2 &p_pt) { return Vector3(p_pt.x, p_pt.y, 0.0); }
void _sort_verts_clockwise(bool portal_plane_convention, Vector<Vector3> &r_verts); void _sort_verts_clockwise(const Vector3 &p_portal_normal, Vector<Vector3> &r_verts);
Plane _plane_from_points_newell(const Vector<Vector3> &p_pts); 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 resolve_links(const LocalVector<Room *, int32_t> &p_rooms, const RID &p_from_room_rid);
void _changed(); void _changed();

View File

@ -873,7 +873,14 @@ void RoomManager::_second_pass_portals(Spatial *p_roomlist, LocalVector<Portal *
String string_link_room = string_link_room_shortname + "-room"; String string_link_room = string_link_room_shortname + "-room";
if (string_link_room_shortname != "") { if (string_link_room_shortname != "") {
// try the room name plus the postfix first, this will be the most common case during import
Room *linked_room = Object::cast_to<Room>(p_roomlist->find_node(string_link_room, true, false)); Room *linked_room = Object::cast_to<Room>(p_roomlist->find_node(string_link_room, true, false));
// try the short name as a last ditch attempt
if (!linked_room) {
linked_room = Object::cast_to<Room>(p_roomlist->find_node(string_link_room_shortname, true, false));
}
if (linked_room) { if (linked_room) {
NodePath path = portal->get_path_to(linked_room); NodePath path = portal->get_path_to(linked_room);
portal->set_linked_room_internal(path); portal->set_linked_room_internal(path);