diff --git a/doc/classes/Portal.xml b/doc/classes/Portal.xml
index da41b6dc0b4..9dd6f967ec3 100644
--- a/doc/classes/Portal.xml
+++ b/doc/classes/Portal.xml
@@ -12,6 +12,14 @@
+
+
+
+
+
+ Sets individual points. Primarily for use by the editor.
+
+
diff --git a/doc/classes/Room.xml b/doc/classes/Room.xml
index 92a4958ae10..170710168f4 100644
--- a/doc/classes/Room.xml
+++ b/doc/classes/Room.xml
@@ -13,6 +13,14 @@
+
+
+
+
+
+ Sets individual points. Primarily for use by the editor.
+
+
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index c5734be4486..66358db9783 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -4441,6 +4441,19 @@ RoomGizmoPlugin::RoomGizmoPlugin() {
create_material("room", color_room, false, true, false);
create_material("room_overlap", color_overlap, false, false, false);
+
+ create_handle_material("room_handle");
+}
+
+Ref RoomGizmoPlugin::create_gizmo(Spatial *p_spatial) {
+ Ref ref;
+
+ Room *room = Object::cast_to(p_spatial);
+ if (room) {
+ ref = Ref(memnew(RoomSpatialGizmo(room)));
+ }
+
+ return ref;
}
bool RoomGizmoPlugin::has_gizmo(Spatial *p_spatial) {
@@ -4459,68 +4472,173 @@ int RoomGizmoPlugin::get_priority() const {
return -1;
}
-void RoomGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
- p_gizmo->clear();
+//////////////////////
- Room *room = Object::cast_to(p_gizmo->get_spatial_node());
+String RoomSpatialGizmo::get_handle_name(int p_idx) const {
+ return "Point " + itos(p_idx);
+}
- if (room) {
- const Geometry::MeshData &md = room->_bound_mesh_data;
- if (!md.edges.size())
- return;
+Variant RoomSpatialGizmo::get_handle_value(int p_idx) {
+ if (!_room) {
+ return Vector3(0, 0, 0);
+ }
- Vector lines;
- Transform tr = room->get_global_transform();
- tr.affine_invert();
+ int num_points = _room->_bound_pts.size();
+ if (p_idx >= num_points) {
+ return Vector3(0, 0, 0);
+ }
- Ref material = get_material("room", p_gizmo);
- Ref material_overlap = get_material("room_overlap", p_gizmo);
- Color color(1, 1, 1, 1);
+ return _room->_bound_pts[p_idx];
+}
- for (int n = 0; n < md.edges.size(); n++) {
- Vector3 a = md.vertices[md.edges[n].a];
- Vector3 b = md.vertices[md.edges[n].b];
+void RoomSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+ if (!_room || (p_idx >= _room->_bound_pts.size())) {
+ return;
+ }
- // xform
- a = tr.xform(a);
- b = tr.xform(b);
+ Transform tr = _room->get_global_transform();
+ Transform tr_inv = tr.affine_inverse();
- lines.push_back(a);
- lines.push_back(b);
+ Vector3 pt_world = _room->_bound_pts[p_idx];
+ pt_world = tr.xform(pt_world);
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 camera_dir = p_camera->get_transform().basis.get_axis(2);
+
+ // find the smallest camera axis, we will only transform the handles on 2 axes max,
+ // to try and make things more user friendly (it is confusing trying to change 3d position
+ // from a 2d view)
+ int biggest_axis = 0;
+ real_t biggest = 0.0;
+ for (int n = 0; n < 3; n++) {
+ real_t val = Math::abs(camera_dir.get_axis(n));
+ if (val > biggest) {
+ biggest = val;
+ biggest_axis = n;
}
+ }
- p_gizmo->add_lines(lines, material, false, color);
+ {
+ Plane plane(pt_world, camera_dir);
+ Vector3 inters;
- // overlap zones
- for (int z = 0; z < room->_gizmo_overlap_zones.size(); z++) {
- const Geometry::MeshData &md_overlap = room->_gizmo_overlap_zones[z];
- Vector pts;
+ if (plane.intersects_ray(ray_from, ray_dir, &inters)) {
+ if (SpatialEditor::get_singleton()->is_snap_enabled()) {
+ float snap = SpatialEditor::get_singleton()->get_translate_snap();
+ inters.snap(Vector3(snap, snap, snap));
+ }
- for (int f = 0; f < md_overlap.faces.size(); f++) {
- const Geometry::MeshData::Face &face = md_overlap.faces[f];
-
- for (int c = 0; c < face.indices.size() - 2; c++) {
- pts.push_back(tr.xform(md_overlap.vertices[face.indices[0]]));
- pts.push_back(tr.xform(md_overlap.vertices[face.indices[c + 1]]));
- pts.push_back(tr.xform(md_overlap.vertices[face.indices[c + 2]]));
+ for (int n = 0; n < 3; n++) {
+ if (n != biggest_axis) {
+ pt_world.set_axis(n, inters.get_axis(n));
}
}
- Ref mesh = memnew(ArrayMesh);
- Array array;
- array.resize(Mesh::ARRAY_MAX);
- array[Mesh::ARRAY_VERTEX] = pts;
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
- p_gizmo->add_mesh(mesh, false, Ref(), material_overlap);
+ Vector3 pt_local = tr_inv.xform(pt_world);
+ _room->set_point(p_idx, pt_local);
}
+
+ return;
}
}
+void RoomSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+ if (!_room || (p_idx >= _room->_bound_pts.size())) {
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+
+ ur->create_action(TTR("Set Room Point Position"));
+ ur->add_do_method(_room, "set_point", p_idx, _room->_bound_pts[p_idx]);
+ ur->add_undo_method(_room, "set_point", p_idx, p_restore);
+ ur->commit_action();
+
+ _room->property_list_changed_notify();
+}
+
+void RoomSpatialGizmo::redraw() {
+ clear();
+
+ if (!_room) {
+ return;
+ }
+
+ const Geometry::MeshData &md = _room->_bound_mesh_data;
+ if (!md.edges.size())
+ return;
+
+ Vector lines;
+ Transform tr = _room->get_global_transform();
+ Transform tr_inv = tr.affine_inverse();
+
+ Ref material = gizmo_plugin->get_material("room", this);
+ Ref material_overlap = gizmo_plugin->get_material("room_overlap", this);
+ Color color(1, 1, 1, 1);
+
+ for (int n = 0; n < md.edges.size(); n++) {
+ Vector3 a = md.vertices[md.edges[n].a];
+ Vector3 b = md.vertices[md.edges[n].b];
+
+ // xform
+ a = tr_inv.xform(a);
+ b = tr_inv.xform(b);
+
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ if (lines.size()) {
+ add_lines(lines, material, false, color);
+ }
+
+ // overlap zones
+ for (int z = 0; z < _room->_gizmo_overlap_zones.size(); z++) {
+ const Geometry::MeshData &md_overlap = _room->_gizmo_overlap_zones[z];
+ Vector pts;
+
+ for (int f = 0; f < md_overlap.faces.size(); f++) {
+ const Geometry::MeshData::Face &face = md_overlap.faces[f];
+
+ for (int c = 0; c < face.indices.size() - 2; c++) {
+ pts.push_back(tr_inv.xform(md_overlap.vertices[face.indices[0]]));
+ pts.push_back(tr_inv.xform(md_overlap.vertices[face.indices[c + 1]]));
+ pts.push_back(tr_inv.xform(md_overlap.vertices[face.indices[c + 2]]));
+ }
+ }
+
+ Ref mesh = memnew(ArrayMesh);
+ Array array;
+ array.resize(Mesh::ARRAY_MAX);
+ array[Mesh::ARRAY_VERTEX] = pts;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
+ add_mesh(mesh, false, Ref(), material_overlap);
+ }
+
+ Vector handles;
+ // draw the handles separately because these must correspond to the raw points
+ // for editing
+ for (int n = 0; n < _room->_bound_pts.size(); n++) {
+ handles.push_back(_room->_bound_pts[n]);
+ }
+
+ // handles
+ if (handles.size()) {
+ Ref material_handle = gizmo_plugin->get_material("room_handle", this);
+ add_handles(handles, material_handle);
+ }
+}
+
+RoomSpatialGizmo::RoomSpatialGizmo(Room *p_room) {
+ _room = p_room;
+ set_spatial_node(p_room);
+}
+
////
PortalGizmoPlugin::PortalGizmoPlugin() {
- _color_portal_front = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_front", Color(0.05, 0.05, 1.0, 0.3));
- _color_portal_back = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_back", Color(1.0, 1.0, 0.0, 0.15));
Color color_portal_margin = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_margin", Color(1.0, 0.1, 0.1, 0.3));
Color color_portal_edge = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_edge", Color(0.0, 0.0, 0.0, 0.3));
Color color_portal_arrow = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_arrow", Color(1.0, 1.0, 1.0, 1.0));
@@ -4530,6 +4648,19 @@ PortalGizmoPlugin::PortalGizmoPlugin() {
create_material("portal_margin", color_portal_margin, false, false, false);
create_material("portal_edge", color_portal_edge, false, false, false);
create_material("portal_arrow", color_portal_arrow, false, false, false);
+
+ create_handle_material("portal_handle");
+}
+
+Ref PortalGizmoPlugin::create_gizmo(Spatial *p_spatial) {
+ Ref ref;
+
+ Portal *portal = Object::cast_to(p_spatial);
+ if (portal) {
+ ref = Ref(memnew(PortalSpatialGizmo(portal)));
+ }
+
+ return ref;
}
bool PortalGizmoPlugin::has_gizmo(Spatial *p_spatial) {
@@ -4548,168 +4679,262 @@ int PortalGizmoPlugin::get_priority() const {
return -1;
}
-void PortalGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
- p_gizmo->clear();
+//////////////////////
- Portal *portal = Object::cast_to(p_gizmo->get_spatial_node());
-
- if (portal) {
- // warnings
- if (portal->_warning_outside_room_aabb || portal->_warning_facing_wrong_way || portal->_warning_autolink_failed) {
- Ref icon = get_material("portal_icon", p_gizmo);
- p_gizmo->add_unscaled_billboard(icon, 0.05);
- }
-
- Transform tr = portal->get_global_transform();
- tr.affine_invert();
-
- Ref material_portal = get_material("portal", p_gizmo);
- Ref material_margin = get_material("portal_margin", p_gizmo);
- Ref material_edge = get_material("portal_edge", p_gizmo);
- Ref material_arrow = get_material("portal_arrow", p_gizmo);
- Color color(1, 1, 1, 1);
-
- // make sure world points are up to date
- portal->portal_update();
-
- int num_points = portal->_pts_world.size();
-
- // prevent compiler warnings later on
- if (num_points < 3) {
- return;
- }
-
- // margins
- real_t margin = portal->get_active_portal_margin();
- bool show_margins = Portal::_settings_gizmo_show_margins;
-
- if (margin < 0.05f) {
- show_margins = false;
- }
-
- PoolVector pts_portal;
- PoolVector cols_portal;
- PoolVector pts_margin;
- Vector edge_pts;
-
- Vector3 portal_normal_world_space = portal->_plane.normal;
- portal_normal_world_space *= margin;
-
- // this may not be necessary, dealing with non uniform scales,
- // possible the affine_invert dealt with this earlier .. but it's just for
- // the editor so not performance critical
- Basis normal_basis = tr.basis;
-
- Vector3 portal_normal = normal_basis.xform(portal_normal_world_space);
- Vector3 pt_portal_first = tr.xform(portal->_pts_world[0]);
-
- for (int n = 0; n < num_points; n++) {
- Vector3 pt = portal->_pts_world[n];
- pt = tr.xform(pt);
-
- // CI for visual studio can't seem to get around the possibility
- // that this could cause a divide by zero, so using a local to preclude the
- // possibility of aliasing from another thread
- int m = (n + 1) % num_points;
- Vector3 pt_next = portal->_pts_world[m];
- pt_next = tr.xform(pt_next);
-
- // don't need the first and last triangles
- if ((n != 0) && (n != (num_points - 1))) {
- pts_portal.push_back(pt_portal_first);
- pts_portal.push_back(pt);
- pts_portal.push_back(pt_next);
- cols_portal.push_back(_color_portal_front);
- cols_portal.push_back(_color_portal_front);
- cols_portal.push_back(_color_portal_front);
-
- pts_portal.push_back(pt_next);
- pts_portal.push_back(pt);
- pts_portal.push_back(pt_portal_first);
- cols_portal.push_back(_color_portal_back);
- cols_portal.push_back(_color_portal_back);
- cols_portal.push_back(_color_portal_back);
- }
-
- if (show_margins) {
- Vector3 pt0 = pt - portal_normal;
- Vector3 pt1 = pt + portal_normal;
- Vector3 pt2 = pt_next - portal_normal;
- Vector3 pt3 = pt_next + portal_normal;
-
- pts_margin.push_back(pt0);
- pts_margin.push_back(pt2);
- pts_margin.push_back(pt1);
-
- pts_margin.push_back(pt2);
- pts_margin.push_back(pt3);
- pts_margin.push_back(pt1);
-
- edge_pts.push_back(pt0);
- edge_pts.push_back(pt2);
- edge_pts.push_back(pt1);
- edge_pts.push_back(pt3);
- }
- }
-
- // portal itself
- {
- Ref mesh = memnew(ArrayMesh);
- Array array;
- array.resize(Mesh::ARRAY_MAX);
- array[Mesh::ARRAY_VERTEX] = pts_portal;
- array[Mesh::ARRAY_COLOR] = cols_portal;
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
- p_gizmo->add_mesh(mesh, false, Ref(), material_portal);
- }
-
- if (show_margins) {
- Ref mesh = memnew(ArrayMesh);
- Array array;
- array.resize(Mesh::ARRAY_MAX);
- array[Mesh::ARRAY_VERTEX] = pts_margin;
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
- p_gizmo->add_mesh(mesh, false, Ref(), material_margin);
-
- // lines around the outside of mesh
- p_gizmo->add_lines(edge_pts, material_edge, false, color);
- } // only if the margin is sufficient to be worth drawing
-
- // arrow
- if (show_margins) {
- const int arrow_points = 7;
- const float arrow_length = 0.5; // 1.5
- const float arrow_width = 0.1; // 0.3
- const float arrow_barb = 0.27; // 0.8
-
- Vector3 arrow[arrow_points] = {
- Vector3(0, 0, -1),
- Vector3(0, arrow_barb, 0),
- Vector3(0, arrow_width, 0),
- Vector3(0, arrow_width, arrow_length),
- Vector3(0, -arrow_width, arrow_length),
- Vector3(0, -arrow_width, 0),
- Vector3(0, -arrow_barb, 0)
- };
-
- int arrow_sides = 2;
-
- Vector lines;
-
- for (int i = 0; i < arrow_sides; i++) {
- for (int j = 0; j < arrow_points; j++) {
- Basis ma(Vector3(0, 0, 1), Math_PI * i / arrow_sides);
-
- Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length);
- Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length);
-
- lines.push_back(ma.xform(v1));
- lines.push_back(ma.xform(v2));
- }
- }
-
- p_gizmo->add_lines(lines, material_arrow, false, color);
- }
-
- } // was portal
+String PortalSpatialGizmo::get_handle_name(int p_idx) const {
+ return "Point " + itos(p_idx);
+}
+
+Variant PortalSpatialGizmo::get_handle_value(int p_idx) {
+ if (!_portal) {
+ return Vector2(0, 0);
+ }
+
+ int num_points = _portal->_pts_local_raw.size();
+ if (p_idx >= num_points) {
+ return Vector2(0, 0);
+ }
+
+ return _portal->_pts_local_raw[p_idx];
+}
+
+void PortalSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+ if (!_portal || (p_idx >= _portal->_pts_local_raw.size())) {
+ return;
+ }
+
+ Transform tr = _portal->get_global_transform();
+ Transform tr_inv = tr.affine_inverse();
+
+ Vector3 pt_local = Portal::_vec2to3(_portal->_pts_local_raw[p_idx]);
+ Vector3 pt_world = tr.xform(pt_local);
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ // get a normal from the global transform
+ Plane plane(Vector3(0, 0, 0), Vector3(0, 0, 1));
+ plane = tr.xform(plane);
+
+ // construct the plane that the 2d portal is defined in
+ plane = Plane(pt_world, plane.normal);
+
+ Vector3 inters;
+
+ if (plane.intersects_ray(ray_from, ray_dir, &inters)) {
+ // back calculate from the 3d intersection to the 2d portal plane
+ inters = tr_inv.xform(inters);
+
+ // snapping will be in 2d for portals, and the scale may make less sense,
+ // but better to offer at least some functionality
+ if (SpatialEditor::get_singleton()->is_snap_enabled()) {
+ float snap = SpatialEditor::get_singleton()->get_translate_snap();
+ inters.snap(Vector3(snap, snap, snap));
+ }
+
+ _portal->set_point(p_idx, Vector2(inters.x, inters.y));
+
+ return;
+ }
+}
+
+void PortalSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+ if (!_portal || (p_idx >= _portal->_pts_local_raw.size())) {
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+
+ ur->create_action(TTR("Set Portal Point Position"));
+ ur->add_do_method(_portal, "set_point", p_idx, _portal->_pts_local_raw[p_idx]);
+ ur->add_undo_method(_portal, "set_point", p_idx, p_restore);
+ ur->commit_action();
+
+ _portal->property_list_changed_notify();
+}
+
+void PortalSpatialGizmo::redraw() {
+ clear();
+
+ if (!_portal) {
+ return;
+ }
+
+ // warnings
+ if (_portal->_warning_outside_room_aabb || _portal->_warning_facing_wrong_way || _portal->_warning_autolink_failed) {
+ Ref icon = gizmo_plugin->get_material("portal_icon", this);
+ add_unscaled_billboard(icon, 0.05);
+ }
+
+ Transform tr = _portal->get_global_transform();
+ Transform tr_inv = tr.affine_inverse();
+
+ Ref material_portal = gizmo_plugin->get_material("portal", this);
+ Ref material_margin = gizmo_plugin->get_material("portal_margin", this);
+ Ref material_edge = gizmo_plugin->get_material("portal_edge", this);
+ Ref material_arrow = gizmo_plugin->get_material("portal_arrow", this);
+ Color color(1, 1, 1, 1);
+
+ // make sure world points are up to date
+ _portal->portal_update();
+
+ int num_points = _portal->_pts_world.size();
+
+ // prevent compiler warnings later on
+ if (num_points < 3) {
+ return;
+ }
+
+ // margins
+ real_t margin = _portal->get_active_portal_margin();
+ bool show_margins = Portal::_settings_gizmo_show_margins;
+
+ if (margin < 0.05f) {
+ show_margins = false;
+ }
+
+ PoolVector pts_portal;
+ PoolVector cols_portal;
+ PoolVector pts_margin;
+ Vector edge_pts;
+
+ Vector handles;
+
+ Vector3 portal_normal_world_space = _portal->_plane.normal;
+ portal_normal_world_space *= margin;
+
+ // this may not be necessary, dealing with non uniform scales,
+ // possible the affine_invert dealt with this earlier .. but it's just for
+ // the editor so not performance critical
+ Basis normal_basis = tr_inv.basis;
+
+ Vector3 portal_normal = normal_basis.xform(portal_normal_world_space);
+ Vector3 pt_portal_first = tr_inv.xform(_portal->_pts_world[0]);
+
+ for (int n = 0; n < num_points; n++) {
+ Vector3 pt = _portal->_pts_world[n];
+ pt = tr_inv.xform(pt);
+
+ // CI for visual studio can't seem to get around the possibility
+ // that this could cause a divide by zero, so using a local to preclude the
+ // possibility of aliasing from another thread
+ int m = (n + 1) % num_points;
+ Vector3 pt_next = _portal->_pts_world[m];
+ pt_next = tr_inv.xform(pt_next);
+
+ // don't need the first and last triangles
+ if ((n != 0) && (n != (num_points - 1))) {
+ pts_portal.push_back(pt_portal_first);
+ pts_portal.push_back(pt);
+ pts_portal.push_back(pt_next);
+ cols_portal.push_back(_color_portal_front);
+ cols_portal.push_back(_color_portal_front);
+ cols_portal.push_back(_color_portal_front);
+
+ pts_portal.push_back(pt_next);
+ pts_portal.push_back(pt);
+ pts_portal.push_back(pt_portal_first);
+ cols_portal.push_back(_color_portal_back);
+ cols_portal.push_back(_color_portal_back);
+ cols_portal.push_back(_color_portal_back);
+ }
+
+ if (show_margins) {
+ Vector3 pt0 = pt - portal_normal;
+ Vector3 pt1 = pt + portal_normal;
+ Vector3 pt2 = pt_next - portal_normal;
+ Vector3 pt3 = pt_next + portal_normal;
+
+ pts_margin.push_back(pt0);
+ pts_margin.push_back(pt2);
+ pts_margin.push_back(pt1);
+
+ pts_margin.push_back(pt2);
+ pts_margin.push_back(pt3);
+ pts_margin.push_back(pt1);
+
+ edge_pts.push_back(pt0);
+ edge_pts.push_back(pt2);
+ edge_pts.push_back(pt1);
+ edge_pts.push_back(pt3);
+ }
+ }
+
+ // draw the handles separately because these must correspond to the raw points
+ // for editing
+ for (int n = 0; n < _portal->_pts_local_raw.size(); n++) {
+ Vector3 pt = Portal::_vec2to3(_portal->_pts_local_raw[n]);
+ handles.push_back(pt);
+ }
+
+ // portal itself
+ {
+ Ref mesh = memnew(ArrayMesh);
+ Array array;
+ array.resize(Mesh::ARRAY_MAX);
+ array[Mesh::ARRAY_VERTEX] = pts_portal;
+ array[Mesh::ARRAY_COLOR] = cols_portal;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
+ add_mesh(mesh, false, Ref(), material_portal);
+
+ // handles
+ Ref material_handle = gizmo_plugin->get_material("portal_handle", this);
+ add_handles(handles, material_handle);
+ }
+
+ if (show_margins) {
+ Ref mesh = memnew(ArrayMesh);
+ Array array;
+ array.resize(Mesh::ARRAY_MAX);
+ array[Mesh::ARRAY_VERTEX] = pts_margin;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
+ add_mesh(mesh, false, Ref(), material_margin);
+
+ // lines around the outside of mesh
+ add_lines(edge_pts, material_edge, false, color);
+ } // only if the margin is sufficient to be worth drawing
+
+ // arrow
+ if (show_margins) {
+ const int arrow_points = 7;
+ const float arrow_length = 0.5; // 1.5
+ const float arrow_width = 0.1; // 0.3
+ const float arrow_barb = 0.27; // 0.8
+
+ Vector3 arrow[arrow_points] = {
+ Vector3(0, 0, -1),
+ Vector3(0, arrow_barb, 0),
+ Vector3(0, arrow_width, 0),
+ Vector3(0, arrow_width, arrow_length),
+ Vector3(0, -arrow_width, arrow_length),
+ Vector3(0, -arrow_width, 0),
+ Vector3(0, -arrow_barb, 0)
+ };
+
+ int arrow_sides = 2;
+
+ Vector lines;
+
+ for (int i = 0; i < arrow_sides; i++) {
+ for (int j = 0; j < arrow_points; j++) {
+ Basis ma(Vector3(0, 0, 1), Math_PI * i / arrow_sides);
+
+ Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length);
+ Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length);
+
+ lines.push_back(ma.xform(v1));
+ lines.push_back(ma.xform(v2));
+ }
+ }
+
+ add_lines(lines, material_arrow, false, color);
+ }
+}
+
+PortalSpatialGizmo::PortalSpatialGizmo(Portal *p_portal) {
+ _portal = p_portal;
+ set_spatial_node(p_portal);
+
+ _color_portal_front = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_front", Color(0.05, 0.05, 1.0, 0.3));
+ _color_portal_back = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/portal_back", Color(1.0, 1.0, 0.0, 0.15));
}
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index 7c5197b846d..4f6f9eae1cb 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -429,6 +429,23 @@ public:
JointSpatialGizmoPlugin();
};
+class Room;
+
+class RoomSpatialGizmo : public EditorSpatialGizmo {
+ GDCLASS(RoomSpatialGizmo, EditorSpatialGizmo);
+
+ Room *_room = nullptr;
+
+public:
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx);
+ virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+ virtual void redraw();
+
+ RoomSpatialGizmo(Room *p_room = nullptr);
+};
+
class RoomGizmoPlugin : public EditorSpatialGizmoPlugin {
GDCLASS(RoomGizmoPlugin, EditorSpatialGizmoPlugin);
@@ -436,12 +453,31 @@ protected:
virtual bool has_gizmo(Spatial *p_spatial);
String get_name() const;
int get_priority() const;
- void redraw(EditorSpatialGizmo *p_gizmo);
+ Ref create_gizmo(Spatial *p_spatial);
public:
RoomGizmoPlugin();
};
+class Portal;
+
+class PortalSpatialGizmo : public EditorSpatialGizmo {
+ GDCLASS(PortalSpatialGizmo, EditorSpatialGizmo);
+
+ Portal *_portal = nullptr;
+ Color _color_portal_front;
+ Color _color_portal_back;
+
+public:
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx);
+ virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+ virtual void redraw();
+
+ PortalSpatialGizmo(Portal *p_portal = nullptr);
+};
+
class PortalGizmoPlugin : public EditorSpatialGizmoPlugin {
GDCLASS(PortalGizmoPlugin, EditorSpatialGizmoPlugin);
@@ -449,11 +485,7 @@ protected:
virtual bool has_gizmo(Spatial *p_spatial);
String get_name() const;
int get_priority() const;
- void redraw(EditorSpatialGizmo *p_gizmo);
-
-private:
- Color _color_portal_front;
- Color _color_portal_back;
+ Ref create_gizmo(Spatial *p_spatial);
public:
PortalGizmoPlugin();
diff --git a/scene/3d/portal.cpp b/scene/3d/portal.cpp
index 353ff316994..e51ce0bb6d9 100644
--- a/scene/3d/portal.cpp
+++ b/scene/3d/portal.cpp
@@ -114,6 +114,16 @@ String Portal::get_configuration_warning() const {
return warning;
}
+void Portal::set_point(int p_idx, const Vector2 &p_point) {
+ if (p_idx >= _pts_local_raw.size()) {
+ return;
+ }
+
+ _pts_local_raw.set(p_idx, p_point);
+ _sanitize_points();
+ update_gizmo();
+}
+
void Portal::set_points(const PoolVector &p_points) {
_pts_local_raw = p_points;
_sanitize_points();
@@ -670,6 +680,8 @@ void Portal::_bind_methods() {
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");
diff --git a/scene/3d/portal.h b/scene/3d/portal.h
index b2e0e311e31..72d67fd1caf 100644
--- a/scene/3d/portal.h
+++ b/scene/3d/portal.h
@@ -47,6 +47,7 @@ class Portal : public Spatial {
friend class RoomManager;
friend class PortalGizmoPlugin;
friend class PortalEditorPlugin;
+ friend class PortalSpatialGizmo;
public:
// ui interface .. will have no effect after room conversion
@@ -83,6 +84,9 @@ public:
void set_points(const PoolVector &p_points);
PoolVector get_points() const;
+ // primarily for the gizmo
+ void set_point(int p_idx, const Vector2 &p_point);
+
String get_configuration_warning() const;
Portal();
@@ -105,7 +109,7 @@ private:
void flip();
void _sanitize_points();
void _update_aabb();
- Vector3 _vec2to3(const Vector2 &p_pt) const { 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 &r_verts);
Plane _plane_from_points_newell(const Vector &p_pts);
void resolve_links(const LocalVector &p_rooms, const RID &p_from_room_rid);
diff --git a/scene/3d/room.cpp b/scene/3d/room.cpp
index 41272302ccb..e1959822ec8 100644
--- a/scene/3d/room.cpp
+++ b/scene/3d/room.cpp
@@ -130,6 +130,18 @@ 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) {
+ if (p_idx >= _bound_pts.size()) {
+ return;
+ }
+
+ _bound_pts.set(p_idx, p_point);
+
+#ifdef TOOLS_ENABLED
+ _changed(true);
+#endif
+}
+
void Room::set_points(const PoolVector &p_points) {
_bound_pts = p_points;
@@ -273,6 +285,8 @@ void Room::_bind_methods() {
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");
diff --git a/scene/3d/room.h b/scene/3d/room.h
index 896758a544d..e743bc0bd4b 100644
--- a/scene/3d/room.h
+++ b/scene/3d/room.h
@@ -45,6 +45,7 @@ class Room : public Spatial {
friend class Portal;
friend class RoomGizmoPlugin;
friend class RoomEditorPlugin;
+ friend class RoomSpatialGizmo;
RID _room_rid;
@@ -71,6 +72,9 @@ public:
void set_points(const PoolVector &p_points);
PoolVector get_points() const;
+ // primarily for the gizmo
+ void set_point(int p_idx, const Vector3 &p_point);
+
// editor only
PoolVector generate_points();