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