/*************************************************************************/ /* mdi_gizmo.cpp */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* 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 "mdi_gizmo.h" #include "../mesh_data_resource.h" #include "../nodes/mesh_data_instance.h" #include "./utilities/mdr_ed_mesh_decompose.h" #include "./utilities/mdr_ed_mesh_outline.h" #include "./utilities/mdr_ed_mesh_utils.h" #include "core/math/geometry.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "modules/mesh_utils/mesh_utils.h" #include "scene/3d/camera.h" void MDIGizmo::set_visible(const bool visible) { _visible = visible; redraw(); } void MDIGizmo::setup() { MeshDataInstance *mdi = Object::cast_to(get_spatial_node()); ERR_FAIL_COND(!mdi); mdi->connect("mesh_data_resource_changed", this, "on_mesh_data_resource_changed"); on_mesh_data_resource_changed(mdi->get_mesh_data()); } void MDIGizmo::set_editor_plugin(EditorPlugin *editor_plugin) { _editor_plugin = editor_plugin; _undo_redo = EditorNode::get_undo_redo(); } void MDIGizmo::set_handle(int index, bool secondary, Camera *camera, const Point2 &point) { Vector2 relative = point - previous_point; if (!_handle_drag_op) { relative = Vector2(); _handle_drag_op = true; if (edit_mode == EditMode::EDIT_MODE_SCALE) { _drag_op_accumulator = Vector3(1, 1, 1); } else { _drag_op_accumulator = Vector3(); } _drag_op_accumulator_quat = Quaternion(); _drag_op_orig_verices = copy_mdr_verts_array(); setup_op_drag_indices(); _drag_op_pivot = get_drag_op_pivot(); } if (edit_mode == EditMode::EDIT_MODE_NONE) { return; } else if (edit_mode == EditMode::EDIT_MODE_TRANSLATE) { Vector3 ofs; ofs = camera->get_global_transform().basis.get_axis(0); if ((axis_constraint & AXIS_CONSTRAINT_X) != 0) { ofs.x *= relative.x * 0.01; } else { ofs.x = 0; } if ((axis_constraint & AXIS_CONSTRAINT_Y) != 0) { ofs.y = relative.y * -0.01; } else { ofs.y = 0; } if ((axis_constraint & AXIS_CONSTRAINT_Z) != 0) { ofs.z *= relative.x * 0.01; } else { ofs.z = 0; } _drag_op_accumulator += ofs; add_to_all_selected(_drag_op_accumulator); apply(); redraw(); } else if (edit_mode == EditMode::EDIT_MODE_SCALE) { float r = ((relative.x + relative.y) * 0.05); Vector3 vs; if ((axis_constraint & AXIS_CONSTRAINT_X) != 0) { vs.x = r; } if ((axis_constraint & AXIS_CONSTRAINT_Y) != 0) { vs.y = r; } if ((axis_constraint & AXIS_CONSTRAINT_Z) != 0) { vs.z = r; } _drag_op_accumulator += vs; Basis b = Basis().scaled(_drag_op_accumulator); Transform t = Transform(Basis(), _drag_op_pivot); t *= Transform(b, Vector3()); t *= Transform(Basis(), _drag_op_pivot).inverse(); mul_all_selected_with_transform(t); apply(); redraw(); } else if (edit_mode == EditMode::EDIT_MODE_ROTATE) { Quaternion yrot = Quaternion(Vector3(0, 1, 0), relative.x * 0.01); Quaternion xrot = Quaternion(camera->get_global_transform().basis.get_axis(0), relative.y * 0.01); _drag_op_accumulator_quat *= yrot; _drag_op_accumulator_quat *= xrot; _drag_op_accumulator_quat = _drag_op_accumulator_quat.normalized(); Basis b = Basis(_drag_op_accumulator_quat); Transform t = Transform(Basis(), _drag_op_pivot); t *= Transform(b, Vector3()); t *= Transform(Basis(), _drag_op_pivot).inverse(); mul_all_selected_with_transform(t); apply(); redraw(); } previous_point = point; } void MDIGizmo::redraw() { clear(); if (!_visible) { return; } if (!_mdr.is_valid()) { return; } Array array = _mdr->get_array(); if (array.size() != ArrayMesh::ARRAY_MAX) { return; } if (!get_plugin().is_valid()) { return; } Ref handles_material = get_plugin()->get_material("handles", Ref(this)); Ref material = get_plugin()->get_material("main", Ref(this)); Ref seam_material = get_plugin()->get_material("seam", Ref(this)); _mesh_outline_generator->setup(_mdr); if (selection_mode == SELECTION_MODE_EDGE) { _mesh_outline_generator->generate_mark_edges(visual_indicator_outline, visual_indicator_handle); } else if (selection_mode == SELECTION_MODE_FACE) { _mesh_outline_generator->generate_mark_faces(visual_indicator_outline, visual_indicator_handle); } else { _mesh_outline_generator->generate(visual_indicator_outline, visual_indicator_handle); } if (visual_indicator_outline || visual_indicator_handle) { add_lines(_mesh_outline_generator->lines, material, false); } if (visual_indicator_seam) { add_lines(_mesh_outline_generator->seam_lines, seam_material, false); } if (_selected_points.size() > 0) { Vector vs; for (int i = 0; i < _selected_points.size(); ++i) { vs.push_back(_handle_points[_selected_points[i]]); } add_handles(vs, handles_material); } } void MDIGizmo::apply() { if (!_mdr.is_valid()) { return; } disable_change_event(); Array arrs = _mdr->get_array(); arrs[ArrayMesh::ARRAY_VERTEX] = _vertices; arrs[ArrayMesh::ARRAY_INDEX] = _indices; _mdr->set_array(arrs); enable_change_event(); } void MDIGizmo::select_all() { if (_selected_points.size() == _handle_points.size()) { return; } _selected_points.resize(_handle_points.size()); PoolIntArray::Write w = _selected_points.write(); for (int i = 0; i < _selected_points.size(); ++i) { w[i] = i; } redraw(); } bool MDIGizmo::selection_click(Camera *camera, const Ref &event) { if (handle_selection_type == HANDLE_SELECTION_TYPE_FRONT) { return selection_click_select_front_or_back(camera, event); } else if (handle_selection_type == HANDLE_SELECTION_TYPE_BACK) { return selection_click_select_front_or_back(camera, event); } else { return selection_click_select_through(camera, event); } return false; } bool MDIGizmo::is_point_visible(const Vector3 &point_orig, const Vector3 &camera_pos, const Transform >) { Vector3 point = gt.xform(point_orig); // go from the given point to the origin (camera_pos -> camera) Vector3 dir = camera_pos - point; dir = dir.normalized(); // Might need to reduce z fighting //point += dir * 0.5 for (int i = 0; i < _indices.size(); i += 3) { int i0 = _indices[i]; int i1 = _indices[i + 1]; int i2 = _indices[i + 2]; Vector3 v0 = _vertices[i0]; Vector3 v1 = _vertices[i1]; Vector3 v2 = _vertices[i2]; v0 = gt.xform(v0); v1 = gt.xform(v1); v2 = gt.xform(v2); bool intersects = Geometry::ray_intersects_triangle(point, dir, v0, v1, v2); if (intersects) { return false; } } return true; } bool MDIGizmo::selection_click_select_front_or_back(Camera *camera, const Ref &event) { Transform gt = get_spatial_node()->get_global_transform(); Vector3 ray_from = camera->get_global_transform().origin; Vector2 gpoint = event->get_position(); float grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); // select vertex int closest_idx = -1; float closest_dist = 1e10; for (int i = 0; i < _handle_points.size(); ++i) { Vector3 vert_pos_3d = gt.xform(_handle_points[i]); Vector2 vert_pos_2d = camera->unproject_position(vert_pos_3d); float dist_3d = ray_from.distance_to(vert_pos_3d); float dist_2d = gpoint.distance_to(vert_pos_2d); if (dist_2d < grab_threshold && dist_3d < closest_dist) { bool point_visible = is_point_visible(_handle_points[i], ray_from, gt); if (handle_selection_type == HANDLE_SELECTION_TYPE_FRONT) { if (!point_visible) { continue; } } else if (handle_selection_type == HANDLE_SELECTION_TYPE_BACK) { if (point_visible) { continue; } } closest_dist = dist_3d; closest_idx = i; } } if (closest_idx >= 0) { for (int si = 0; si < _selected_points.size(); ++si) { if (_selected_points[si] == closest_idx) { if (event->get_alt() || event->get_control()) { _selected_points.remove(si); redraw(); return true; } return false; } } if (event->get_alt() || event->get_control()) { return false; } if (event->get_shift()) { _selected_points.append(closest_idx); } else { // Select new point only _selected_points.resize(0); _selected_points.append(closest_idx); } redraw(); } else { // Don't unselect all if either control or shift is held down if (event->get_shift() || event->get_control() || event->get_alt()) { return false; } if (_selected_points.size() == 0) { return false; } //Unselect all _selected_points.resize(0); redraw(); } return false; } bool MDIGizmo::selection_click_select_through(Camera *camera, const Ref &event) { Transform gt = get_spatial_node()->get_global_transform(); Vector3 ray_from = camera->get_global_transform().origin; Vector2 gpoint = event->get_position(); float grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); // select vertex int closest_idx = -1; float closest_dist = 1e10; for (int i = 0; i < _handle_points.size(); ++i) { Vector3 vert_pos_3d = gt.xform(_handle_points[i]); Vector2 vert_pos_2d = camera->unproject_position(vert_pos_3d); float dist_3d = ray_from.distance_to(vert_pos_3d); float dist_2d = gpoint.distance_to(vert_pos_2d); if (dist_2d < grab_threshold && dist_3d < closest_dist) { closest_dist = dist_3d; closest_idx = i; } } if (closest_idx >= 0) { for (int si = 0; si < _selected_points.size(); ++si) { if (_selected_points[si] == closest_idx) { if (event->get_alt() || event->get_control()) { _selected_points.remove(si); redraw(); return true; } return false; } } if (event->get_alt() || event->get_control()) { return false; } if (event->get_shift()) { _selected_points.append(closest_idx); } else { // Select new point only _selected_points.resize(0); _selected_points.append(closest_idx); } redraw(); } else { // Don't unselect all if either control or shift is held down if (event->get_shift() || event->get_control() || event->get_alt()) { return false; } if (_selected_points.size() == 0) { return false; } //Unselect all _selected_points.resize(0); redraw(); } return false; } void MDIGizmo::selection_drag(Camera *camera, const Ref &event) { if (handle_selection_type == HANDLE_SELECTION_TYPE_FRONT) { selection_drag_rect_select_front_back(camera, event); } else if (handle_selection_type == HANDLE_SELECTION_TYPE_BACK) { selection_drag_rect_select_front_back(camera, event); } else { selection_drag_rect_select_through(camera, event); } } void MDIGizmo::selection_drag_rect_select_front_back(Camera *camera, const Ref &event) { Transform gt = get_spatial_node()->get_global_transform(); Vector3 ray_from = camera->get_global_transform().origin; Vector2 mouse_pos = event->get_position(); Vector2 rect_size = _rect_drag_start_point - mouse_pos; rect_size.x = ABS(rect_size.x); rect_size.y = ABS(rect_size.y); Rect2 rect = Rect2(_rect_drag_start_point, rect_size); // This is needed so selection works even when you drag from bottom to top, and from right to left Vector2 rect_ofs = _rect_drag_start_point - mouse_pos; if (rect_ofs.x > 0) { rect.position.x -= rect_ofs.x; } if (rect_ofs.y > 0) { rect.position.y -= rect_ofs.y; } PoolIntArray selected; for (int i = 0; i < _handle_points.size(); ++i) { Vector3 vert_pos_3d = gt.xform(_handle_points[i]); Vector2 vert_pos_2d = camera->unproject_position(vert_pos_3d); if (rect.has_point(vert_pos_2d)) { bool point_visible = is_point_visible(_handle_points[i], ray_from, gt); if (handle_selection_type == HANDLE_SELECTION_TYPE_FRONT) { if (!point_visible) { continue; } } else if (handle_selection_type == HANDLE_SELECTION_TYPE_BACK) { if (point_visible) { continue; } } selected.push_back(i); } } if (event->get_alt() || event->get_control()) { PoolIntArray::Read r = selected.read(); for (int is = 0; is < selected.size(); ++is) { int isel = r[is]; for (int i = 0; i < _selected_points.size(); ++i) { if (_selected_points[i] == isel) { _selected_points.remove(i); break; } } } r.release(); redraw(); return; } if (event->get_shift()) { PoolIntArray::Read r = selected.read(); for (int is = 0; is < selected.size(); ++is) { int isel = r[is]; if (!pool_int_arr_contains(_selected_points, isel)) { _selected_points.push_back(isel); } } r.release(); redraw(); return; } _selected_points.resize(0); _selected_points.append_array(selected); redraw(); } void MDIGizmo::selection_drag_rect_select_through(Camera *camera, const Ref &event) { Transform gt = get_spatial_node()->get_global_transform(); Vector2 mouse_pos = event->get_position(); Vector2 rect_size = _rect_drag_start_point - mouse_pos; rect_size.x = ABS(rect_size.x); rect_size.y = ABS(rect_size.y); Rect2 rect = Rect2(_rect_drag_start_point, rect_size); // This is needed so selection works even when you drag from bottom to top, and from right to left Vector2 rect_ofs = _rect_drag_start_point - mouse_pos; if (rect_ofs.x > 0) { rect.position.x -= rect_ofs.x; } if (rect_ofs.y > 0) { rect.position.y -= rect_ofs.y; } PoolIntArray selected; for (int i = 0; i < _handle_points.size(); ++i) { Vector3 vert_pos_3d = gt.xform(_handle_points[i]); Vector2 vert_pos_2d = camera->unproject_position(vert_pos_3d); if (rect.has_point(vert_pos_2d)) { selected.push_back(i); } } if (event->get_alt() || event->get_control()) { for (int ii = 0; ii < selected.size(); ++ii) { int isel = selected[ii]; for (int i = 0; i < _selected_points.size(); ++i) { if (_selected_points[i] == isel) { _selected_points.remove(i); break; } } } redraw(); return; } if (event->get_shift()) { for (int ii = 0; ii < selected.size(); ++ii) { int isel = selected[ii]; if (!pool_int_arr_contains(_selected_points, isel)) { _selected_points.push_back(isel); } } redraw(); return; } _selected_points.resize(0); _selected_points.append_array(selected); redraw(); } EditorPlugin::AfterGUIInput MDIGizmo::forward_spatial_gui_input(Camera *camera, const Ref &event) { _last_known_camera_facing = camera->get_transform().basis.xform(Vector3(0, 0, -1)); Ref event_button = event; if (event_button.is_valid()) { if (event_button->get_button_index() == BUTTON_LEFT) { if (_handle_drag_op) { if (!event_button->is_pressed()) { _handle_drag_op = false; // If a handle was being dragged only run these if (_mdr.is_valid() && _mdr->get_array().size() == ArrayMesh::ARRAY_MAX && !_mdr->get_array()[ArrayMesh::ARRAY_VERTEX].is_null()) { Array arrs = _mdr->get_array(); PoolVector3Array vertices = arrs[ArrayMesh::ARRAY_VERTEX]; if (vertices.size() == _drag_op_orig_verices.size()) { Array arr_new = _mdr->get_array().duplicate(true); Array arr_orig = arr_new.duplicate(true); arr_orig[ArrayMesh::ARRAY_VERTEX] = _drag_op_orig_verices; disable_change_event(); _undo_redo->create_action("Drag"); _undo_redo->add_do_method(_mdr.ptr(), "set_array", arr_new); _undo_redo->add_undo_method(_mdr.ptr(), "set_array", arr_orig); _undo_redo->commit_action(); enable_change_event(); } } } // Dont consume the event here, because the handles will get stuck // to the mouse pointer if we return true return EditorPlugin::AFTER_GUI_INPUT_NO_DESELECT; } if (!event_button->is_pressed()) { // See whether we should check for a click or a selection box Vector2 mouse_pos = event_button->get_position(); Vector2 rect_size = _rect_drag_start_point - mouse_pos; rect_size.x = ABS(rect_size.x); rect_size.y = ABS(rect_size.y); bool had_rect_drag = false; if (rect_size.x > _rect_drag_min_ofset || rect_size.y > _rect_drag_min_ofset) { had_rect_drag = true; } if (!had_rect_drag) { if (selection_click(camera, event)) { return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { return EditorPlugin::AFTER_GUI_INPUT_NO_DESELECT; } } else { selection_drag(camera, event_button); // Always return false here, so the drag rect thing disappears in the editor return EditorPlugin::AFTER_GUI_INPUT_NO_DESELECT; } } else { // event is pressed _rect_drag = true; _rect_drag_start_point = event_button->get_position(); } } } return EditorPlugin::AFTER_GUI_INPUT_NO_DESELECT; } void MDIGizmo::add_to_all_selected(const Vector3 &ofs) { for (int i = 0; i < _selected_points.size(); ++i) { int indx = _selected_points[i]; Vector3 v = _handle_points[indx]; v += ofs; _handle_points.set(indx, v); } for (int i = 0; i < _drag_op_indices.size(); ++i) { int indx = _drag_op_indices[i]; Vector3 v = _drag_op_orig_verices[indx]; v += ofs; _vertices.set(indx, v); } } void MDIGizmo::mul_all_selected_with_basis(const Basis &b) { for (int i = 0; i < _selected_points.size(); ++i) { int indx = _selected_points[i]; Vector3 v = _handle_points[indx]; v = b.xform(v); _handle_points.set(indx, v); } for (int i = 0; i < _drag_op_indices.size(); ++i) { int indx = _drag_op_indices[i]; Vector3 v = _drag_op_orig_verices[indx]; v = b.xform(v); _vertices.set(indx, v); } } void MDIGizmo::mul_all_selected_with_transform(const Transform &t) { for (int i = 0; i < _selected_points.size(); ++i) { int indx = _selected_points[i]; Vector3 v = _handle_points[indx]; v = t.xform(v); _handle_points.set(indx, v); } for (int i = 0; i < _drag_op_indices.size(); ++i) { int indx = _drag_op_indices[i]; Vector3 v = _drag_op_orig_verices[indx]; v = t.xform(v); _vertices.set(indx, v); } } void MDIGizmo::mul_all_selected_with_transform_acc(const Transform &t) { for (int i = 0; i < _selected_points.size(); ++i) { int indx = _selected_points[i]; Vector3 v = _handle_points[indx]; v = t.xform(v); _handle_points.set(indx, v); } for (int i = 0; i < _drag_op_indices.size(); ++i) { int indx = _drag_op_indices[i]; Vector3 v = _vertices[indx]; v = t.xform(v); _vertices.set(indx, v); } } void MDIGizmo::set_translate() { edit_mode = EDIT_MODE_TRANSLATE; } void MDIGizmo::set_scale() { edit_mode = EDIT_MODE_SCALE; } void MDIGizmo::set_rotate() { edit_mode = EDIT_MODE_ROTATE; } void MDIGizmo::set_edit_mode(int em) { edit_mode = em; } void MDIGizmo::set_axis_x(bool on) { if (on) { axis_constraint |= AXIS_CONSTRAINT_X; } else { if ((axis_constraint & AXIS_CONSTRAINT_X) != 0) { axis_constraint ^= AXIS_CONSTRAINT_X; } } } void MDIGizmo::set_axis_y(bool on) { if (on) { axis_constraint |= AXIS_CONSTRAINT_Y; } else { if ((axis_constraint & AXIS_CONSTRAINT_Y) != 0) { axis_constraint ^= AXIS_CONSTRAINT_Y; } } } void MDIGizmo::set_axis_z(bool on) { if (on) { axis_constraint |= AXIS_CONSTRAINT_Z; } else { if ((axis_constraint & AXIS_CONSTRAINT_Z) != 0) { axis_constraint ^= AXIS_CONSTRAINT_Z; } } } void MDIGizmo::set_selection_mode_vertex() { if (selection_mode == SELECTION_MODE_VERTEX) { return; } selection_mode = SELECTION_MODE_VERTEX; _selected_points.resize(0); recalculate_handle_points(); redraw(); } void MDIGizmo::set_selection_mode_edge() { if (selection_mode == SELECTION_MODE_EDGE) { return; } selection_mode = SELECTION_MODE_EDGE; _selected_points.resize(0); recalculate_handle_points(); redraw(); } void MDIGizmo::set_selection_mode_face() { if (selection_mode == SELECTION_MODE_FACE) { return; } selection_mode = SELECTION_MODE_FACE; _selected_points.resize(0); recalculate_handle_points(); redraw(); } void MDIGizmo::recalculate_handle_points() { if (!_mdr.is_valid()) { _handle_points.resize(0); _handle_to_vertex_map.resize(0); _selected_points.resize(0); return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { _handle_points.resize(0); _handle_to_vertex_map.resize(0); _selected_points.resize(0); return; } PoolVector3Array vertices = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (vertices.size() == 0) { _handle_points.resize(0); _handle_to_vertex_map.resize(0); _selected_points.resize(0); return; } Array arr; arr.resize(ArrayMesh::ARRAY_MAX); arr[ArrayMesh::ARRAY_VERTEX] = mdr_arr[ArrayMesh::ARRAY_VERTEX]; arr[ArrayMesh::ARRAY_INDEX] = mdr_arr[ArrayMesh::ARRAY_INDEX]; if (selection_mode == SELECTION_MODE_VERTEX) { Array merged_arrays = MeshUtils::get_singleton()->merge_mesh_array(arr); _handle_points = merged_arrays[ArrayMesh::ARRAY_VERTEX]; _handle_to_vertex_map = MDREDMeshDecompose::get_handle_vertex_to_vertex_map(mdr_arr, _handle_points); } else if (selection_mode == SELECTION_MODE_EDGE) { MDREDMeshDecompose::HandleVertexMapResult result = MDREDMeshDecompose::get_handle_edge_to_vertex_map(arr); _handle_points = result.handle_points; _handle_to_vertex_map = result.handle_to_vertex_map; } else if (selection_mode == SELECTION_MODE_FACE) { MDREDMeshDecompose::HandleVertexMapResult result = MDREDMeshDecompose::get_handle_face_to_vertex_map(arr); _handle_points = result.handle_points; _handle_to_vertex_map = result.handle_to_vertex_map; } for (int i = 0; i < _selected_points.size(); ++i) { if (_selected_points[i] >= _handle_points.size()) { _selected_points.remove(i); --i; } } } void MDIGizmo::on_mesh_data_resource_changed(Ref mdr) { if (_mdr.is_valid()) { _mdr->disconnect("changed", this, "on_mdr_changed"); } _mdr = mdr; if (_mdr.is_valid()) { _mdr->connect("changed", this, "on_mdr_changed"); } else { _vertices.resize(0); _indices.resize(0); recalculate_handle_points(); redraw(); return; } Array arrs = _mdr->get_array(); if (arrs.size() == ArrayMesh::ARRAY_MAX && !arrs[ArrayMesh::ARRAY_VERTEX].is_null()) { _vertices = arrs[ArrayMesh::ARRAY_VERTEX]; _indices = arrs[ArrayMesh::ARRAY_INDEX]; } else { _vertices.resize(0); _indices.resize(0); } recalculate_handle_points(); redraw(); } void MDIGizmo::on_mdr_changed() { if (!_mdr.is_valid()) { _vertices.resize(0); _indices.resize(0); recalculate_handle_points(); redraw(); } Array arr = _mdr->get_array(); if (arr.size() == ArrayMesh::ARRAY_MAX && !arr[ArrayMesh::ARRAY_VERTEX].is_null()) { _vertices = arr[ArrayMesh::ARRAY_VERTEX]; _indices = arr[ArrayMesh::ARRAY_INDEX]; } else { _vertices.resize(0); _indices.resize(0); } recalculate_handle_points(); redraw(); } void MDIGizmo::disable_change_event() { _mdr->disconnect("changed", this, "on_mdr_changed"); } void MDIGizmo::enable_change_event(bool update) { _mdr->connect("changed", this, "on_mdr_changed"); if (update) { on_mdr_changed(); } } void MDIGizmo::add_triangle() { if (_mdr.is_valid()) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); MDREDMeshUtils::add_triangle(_mdr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Add Triangle"); enable_change_event(); } } void MDIGizmo::add_quad() { if (_mdr.is_valid()) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); MDREDMeshUtils::add_quad(_mdr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Add Quad"); enable_change_event(); } } bool MDIGizmo::is_verts_equal(const Vector3 &v0, const Vector3 &v1) { return Math::is_equal_approx(v0.x, v1.x) && Math::is_equal_approx(v0.y, v1.y) && Math::is_equal_approx(v0.z, v1.z); } Vector3 MDIGizmo::find_other_vertex_for_edge(const int edge, const Vector3 &v0) { PoolIntArray ps = _handle_to_vertex_map[edge]; Vector3 vert; for (int i = 0; i < ps.size(); ++i) { vert = _vertices[ps[i]]; if (!is_verts_equal(v0, vert)) { return vert; } } return v0; } Vector MDIGizmo::split_edge_indices(const int edge) { PoolIntArray ps = _handle_to_vertex_map[edge]; if (ps.size() == 0) { return Vector(); } Vector3 v0 = _vertices[ps[0]]; PoolIntArray v0ei; v0ei.append(ps[0]); PoolIntArray v1ei; for (int i = 1; i < ps.size(); ++i) { Vector3 vert = _vertices[ps[i]]; if (is_verts_equal(v0, vert)) { v0ei.append(ps[i]); } else { v1ei.append(ps[i]); } } Vector arr; arr.push_back(v0ei); arr.push_back(v1ei); return arr; } bool MDIGizmo::pool_int_arr_contains(const PoolIntArray &arr, const int val) { PoolIntArray::Read r = arr.read(); for (int i = 0; i < arr.size(); ++i) { if (r[i] == val) { return true; } } return false; } PoolIntArray MDIGizmo::find_triangles_for_edge(int edge) { Vector eisarr = split_edge_indices(edge); if (eisarr.size() == 0) { return PoolIntArray(); } // these should have the same size PoolIntArray v0ei = eisarr[0]; PoolIntArray v1ei = eisarr[1]; PoolIntArray res; for (int i = 0; i < _indices.size(); i += 3) { int i0 = _indices[i]; int i1 = _indices[i + 1]; int i2 = _indices[i + 2]; if (pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2)) { if (pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2)) { res.append(i / 3); } } } return res; } int MDIGizmo::find_first_triangle_for_edge(int edge) { Vector eisarr = split_edge_indices(edge); if (eisarr.size() == 0) { return -1; } // these should have the same size PoolIntArray v0ei = eisarr[0]; PoolIntArray v1ei = eisarr[1]; for (int i = 0; i < _indices.size(); i += 3) { int i0 = _indices[i]; int i1 = _indices[i + 1]; int i2 = _indices[i + 2]; if (pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2)) { if (pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2)) { return i / 3; } } } return -1; } void MDIGizmo::add_triangle_to_edge(int edge) { int triangle_index = find_first_triangle_for_edge(edge); int inds = triangle_index * 3; int ti0 = _indices[inds]; int ti1 = _indices[inds + 1]; int ti2 = _indices[inds + 2]; PoolIntArray ps = _handle_to_vertex_map[edge]; if (ps.size() == 0) { return; } int ei0 = 0; int ei1 = 0; int erefind = 0; if (!pool_int_arr_contains(ps, ti0)) { ei0 = ti1; ei1 = ti2; erefind = ti0; } else if (!pool_int_arr_contains(ps, ti1)) { ei0 = ti0; ei1 = ti2; erefind = ti1; } else if (!pool_int_arr_contains(ps, ti2)) { ei0 = ti0; ei1 = ti1; erefind = ti2; } Vector3 fo = MDREDMeshUtils::get_face_normal(_vertices[ti0], _vertices[ti1], _vertices[ti2]); Vector3 fn = MDREDMeshUtils::get_face_normal(_vertices[ei0], _vertices[ei1], _vertices[erefind]); if (fo.dot(fn) < 0) { int t = ei0; ei0 = ei1; ei1 = t; } MDREDMeshUtils::append_triangle_to_tri_edge(_mdr, _vertices[ei0], _vertices[ei1], _vertices[erefind]); } void MDIGizmo::add_quad_to_edge(int edge) { int triangle_index = find_first_triangle_for_edge(edge); int inds = triangle_index * 3; int ti0 = _indices[inds]; int ti1 = _indices[inds + 1]; int ti2 = _indices[inds + 2]; PoolIntArray ps = _handle_to_vertex_map[edge]; if (ps.size() == 0) { return; } int ei0 = 0; int ei1 = 0; int erefind = 0; if (!pool_int_arr_contains(ps, ti0)) { ei0 = ti1; ei1 = ti2; erefind = ti0; } else if (!pool_int_arr_contains(ps, ti1)) { ei0 = ti0; ei1 = ti2; erefind = ti1; } else if (!pool_int_arr_contains(ps, ti2)) { ei0 = ti0; ei1 = ti1; erefind = ti2; } Vector3 fo = MDREDMeshUtils::get_face_normal(_vertices[ti0], _vertices[ti1], _vertices[ti2]); Vector3 fn = MDREDMeshUtils::get_face_normal(_vertices[ei0], _vertices[ei1], _vertices[erefind]); if (fo.dot(fn) < 0) { int t = ei0; ei0 = ei1; ei1 = t; } MDREDMeshUtils::append_quad_to_tri_edge(_mdr, _vertices[ei0], _vertices[ei1], _vertices[erefind]); } void MDIGizmo::add_triangle_at() { if (!_mdr.is_valid()) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray::Read r = _selected_points.read(); for (int i = 0; i < _selected_points.size(); ++i) { add_triangle_to_edge(r[i]); } r.release(); _selected_points.resize(0); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Add Triangle At"); enable_change_event(); } else { add_triangle(); } } void MDIGizmo::add_quad_at() { if (!_mdr.is_valid()) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray::Read r = _selected_points.read(); for (int i = 0; i < _selected_points.size(); ++i) { add_quad_to_edge(r[i]); } r.release(); _selected_points.resize(0); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Add Triangle At"); enable_change_event(); } else { add_quad(); } } void MDIGizmo::extrude() { if (!_mdr.is_valid()) { return; } Array arr = _mdr->get_array(); if (arr.size() != ArrayMesh::ARRAY_MAX || arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { disable_change_event(); Array orig_arr = copy_arrays(arr); PoolVector3Array voa = orig_arr[ArrayMesh::ARRAY_VERTEX]; int original_size = voa.size(); PoolIntArray::Read r = _selected_points.read(); for (int i = 0; i < _selected_points.size(); ++i) { add_quad_to_edge(r[i]); } r.release(); arr = _mdr->get_array(); // Note: This algorithm depends heavily depends on the inner workings of add_quad_to_edge! PoolVector3Array new_verts = arr[ArrayMesh::ARRAY_VERTEX]; // every 4 vertex is a quad // 1 ---- 2 // | | // | | // 0 ---- 3 // vertex 1, and 2 are the created new ones, 0, and 3 are duplicated from the original edge // Don't reallocate it every time PoolIntArray found_verts; // Go through every new created 0th vertex for (int i = original_size; i < new_verts.size(); i += 4) { Vector3 v0 = new_verts[i]; found_verts.resize(0); // Find a pair for it (has to be the 3th). for (int j = original_size; j < new_verts.size(); j += 4) { if (i == j) { continue; } // +3 offset to 3rd vert Vector3 v3 = new_verts[j + 3]; if (is_verts_equal(v0, v3)) { // +2 offset to 2nd vert found_verts.append(j + 2); } } if (found_verts.size() == 0) { continue; } // Also append the first vertex index to simplify logic found_verts.append(i + 1); // Calculate avg Vector3 vavg; for (int ic = 0; ic < found_verts.size(); ++ic) { vavg += new_verts[found_verts[ic]]; } vavg /= found_verts.size(); // set back for (int ic = 0; ic < found_verts.size(); ++ic) { new_verts.set(found_verts[ic], vavg); } } arr[ArrayMesh::ARRAY_VERTEX] = new_verts; _mdr->set_array(arr); _selected_points.resize(0); add_mesh_change_undo_redo(orig_arr, arr, "Extrude"); enable_change_event(); // The selection also will take care of the duplicates PoolVector3Array new_handle_points; for (int i = original_size; i < new_verts.size(); i += 4) { Vector3 vavg = new_verts[i + 1]; vavg += new_verts[i + 2]; vavg /= 2; new_handle_points.append(vavg); } select_handle_points(new_handle_points); } else { add_quad(); } } void MDIGizmo::add_box() { if (_mdr.is_valid()) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); MDREDMeshUtils::add_box(_mdr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Add Box"); enable_change_event(); } } void MDIGizmo::split() { } void MDIGizmo::disconnect_action() { } int MDIGizmo::get_first_triangle_index_for_vertex(int indx) { PoolIntArray::Read r = _indices.read(); for (int i = 0; i < _indices.size(); ++i) { if (r[i] == indx) { return i / 3; } } return -1; } void MDIGizmo::create_face() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() <= 2) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolVector3Array points; for (int i = 0; i < _selected_points.size(); ++i) { points.push_back(_handle_points[_selected_points[i]]); } if (points.size() == 3) { int i0 = _handle_to_vertex_map[_selected_points[0]][0]; int i1 = _handle_to_vertex_map[_selected_points[1]][0]; int i2 = _handle_to_vertex_map[_selected_points[2]][0]; Vector3 v0 = points[0]; Vector3 v1 = points[1]; Vector3 v2 = points[2]; Vector3 tfn; PoolVector3Array orig_arr_normals = orig_arr[ArrayMesh::ARRAY_NORMAL]; PoolVector3Array orig_arr_verts = orig_arr[ArrayMesh::ARRAY_VERTEX]; if (!orig_arr[ArrayMesh::ARRAY_NORMAL].is_null() && orig_arr_normals.size() == orig_arr_verts.size()) { PoolVector3Array normals = orig_arr[ArrayMesh::ARRAY_NORMAL]; tfn += normals[i0]; tfn += normals[i1]; tfn += normals[i2]; tfn /= 3; tfn = tfn.normalized(); } else { tfn = MDREDMeshUtils::get_face_normal(_vertices[i0], _vertices[i1], _vertices[i2]); } bool flip = !MDREDMeshUtils::should_triangle_flip(v0, v1, v2, tfn); MDREDMeshUtils::add_triangle_at(_mdr, v0, v1, v2, flip); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Create Face"); enable_change_event(); return; } if (!MDREDMeshUtils::add_triangulated_mesh_from_points_delaunay(_mdr, points, _last_known_camera_facing)) { enable_change_event(); return; } add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Create Face"); //_selected_points.resize(0) enable_change_event(); } else if (selection_mode == SELECTION_MODE_EDGE) { } else if (selection_mode == SELECTION_MODE_FACE) { } } Vector MDIGizmo::split_face_indices(int face) { Vector ret; PoolIntArray ps = _handle_to_vertex_map[face]; if (ps.size() == 0) { return ret; } Vector3 v0 = _vertices[ps[0]]; Vector3 v1; bool v1found = false; PoolIntArray v0ei; v0ei.append(ps[0]); PoolIntArray v1ei; PoolIntArray v2ei; PoolIntArray::Read r = ps.read(); for (int i = 1; i < ps.size(); ++i) { Vector3 vert = _vertices[ps[i]]; if (is_verts_equal(v0, vert)) { v0ei.append(ps[i]); } else { if (v1found) { if (is_verts_equal(v1, vert)) { v1ei.append(ps[i]); } else { v2ei.append(ps[i]); } } else { v1found = true; v1 = _vertices[ps[i]]; v1ei.append(ps[i]); } } } ret.push_back(v0ei); ret.push_back(v1ei); ret.push_back(v2ei); return ret; } int MDIGizmo::find_first_triangle_index_for_face(int face) { Vector split_indices_arr = split_face_indices(face); if (split_indices_arr.size() == 0) { return -1; } PoolIntArray v0ei = split_indices_arr[0]; PoolIntArray v1ei = split_indices_arr[1]; PoolIntArray v2ei = split_indices_arr[2]; //int tri_index = -1; for (int i = 0; i < _indices.size(); i += 3) { int i0 = _indices[i]; int i1 = _indices[i + 1]; int i2 = _indices[i + 2]; if (pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2)) { if (pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2)) { if (pool_int_arr_contains(v2ei, i0) || pool_int_arr_contains(v2ei, i1) || pool_int_arr_contains(v2ei, i2)) { return i / 3; } } } } return -1; } void MDIGizmo::delete_selected() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() == 0) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { } else if (selection_mode == SELECTION_MODE_FACE) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); Array triangle_indexes; for (int i = 0; i < _selected_points.size(); ++i) { int triangle_index = find_first_triangle_index_for_face(_selected_points[i]); triangle_indexes.append(triangle_index); } //delete in reverse triangle index order triangle_indexes.sort(); for (int i = triangle_indexes.size() - 1; i >= 0; --i) { int triangle_index = triangle_indexes[i]; MDREDMeshUtils::remove_triangle(_mdr, triangle_index); } add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Delete"); _selected_points.resize(0); enable_change_event(); } } void MDIGizmo::generate_normals() { if (!_mdr.is_valid()) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array verts = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (verts.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray orig_seams = copy_pool_int_array(_mdr->get_seams()); PoolVector3Array seam_points = MDREDMeshUtils::seams_to_points(_mdr); MDREDMeshUtils::generate_normals_mdr(_mdr); MDREDMeshUtils::points_to_seams(_mdr, seam_points); add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr->get_array(), _mdr->get_seams(), "Generate Normals"); enable_change_event(); } void MDIGizmo::generate_tangents() { if (!_mdr.is_valid()) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array verts = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (verts.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray orig_seams = copy_pool_int_array(_mdr->get_seams()); PoolVector3Array seam_points = MDREDMeshUtils::seams_to_points(_mdr); MDREDMeshUtils::generate_tangents(_mdr); MDREDMeshUtils::points_to_seams(_mdr, seam_points); add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr->get_array(), _mdr->get_seams(), "Generate Tangents"); enable_change_event(); } void MDIGizmo::remove_doubles() { if (!_mdr.is_valid()) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array verts = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (verts.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray orig_seams = copy_pool_int_array(_mdr->get_seams()); PoolVector3Array seam_points = MDREDMeshUtils::seams_to_points(_mdr); Array merged_arrays = MeshUtils::get_singleton()->remove_doubles(mdr_arr); _mdr->set_array(merged_arrays); MDREDMeshUtils::points_to_seams(_mdr, seam_points); add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr->get_array(), _mdr->get_seams(), "Remove Doubles"); enable_change_event(); } void MDIGizmo::merge_optimize() { if (!_mdr.is_valid()) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array verts = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (verts.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray orig_seams = copy_pool_int_array(_mdr->get_seams()); PoolVector3Array seam_points = MDREDMeshUtils::seams_to_points(_mdr); Array merged_arrays = MeshUtils::get_singleton()->merge_mesh_array(mdr_arr); _mdr->set_array(merged_arrays); MDREDMeshUtils::points_to_seams(_mdr, seam_points); add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr->get_array(), _mdr->get_seams(), "Merge Optimize"); enable_change_event(); } void MDIGizmo::connect_to_first_selected() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() < 2) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array vertices = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (vertices.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); if (selection_mode == SELECTION_MODE_VERTEX) { Vector3 mpos = _handle_points[_selected_points[0]]; for (int i = 1; i < _selected_points.size(); ++i) { PoolIntArray ps = _handle_to_vertex_map[_selected_points[i]]; for (int j = 0; j < ps.size(); ++j) { vertices.set(ps[j], mpos); } } _selected_points.resize(0); mdr_arr[ArrayMesh::ARRAY_VERTEX] = vertices; _mdr->set_array(mdr_arr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Connect to first selected"); } else if (selection_mode == SELECTION_MODE_EDGE) { } else if (selection_mode == SELECTION_MODE_FACE) { } enable_change_event(); } void MDIGizmo::connect_to_avg() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() < 2) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array vertices = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (vertices.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); if (selection_mode == SELECTION_MODE_VERTEX) { Vector3 mpos; for (int i = 0; i < _selected_points.size(); ++i) { mpos += _handle_points[_selected_points[i]]; } mpos /= _selected_points.size(); for (int i = 0; i < _selected_points.size(); ++i) { PoolIntArray ps = _handle_to_vertex_map[_selected_points[i]]; for (int j = 0; j < ps.size(); ++j) { vertices.set(ps[j], mpos); } } _selected_points.resize(0); mdr_arr[ArrayMesh::ARRAY_VERTEX] = vertices; _mdr->set_array(mdr_arr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Connect to average"); } else if (selection_mode == SELECTION_MODE_EDGE) { } else if (selection_mode == SELECTION_MODE_FACE) { } enable_change_event(); } void MDIGizmo::connect_to_last_selected() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() < 2) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array vertices = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (vertices.size() == 0) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); if (selection_mode == SELECTION_MODE_VERTEX) { Vector3 mpos = _handle_points[_selected_points[_selected_points.size() - 1]]; for (int i = 0; i < _selected_points.size(); ++i) { PoolIntArray ps = _handle_to_vertex_map[_selected_points[i]]; for (int j = 0; j < ps.size(); ++j) { vertices.set(ps[j], mpos); } } _selected_points.resize(0); mdr_arr[ArrayMesh::ARRAY_VERTEX] = vertices; _mdr->set_array(mdr_arr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Connect to last selected"); } else if (selection_mode == SELECTION_MODE_EDGE) { } else if (selection_mode == SELECTION_MODE_FACE) { } enable_change_event(); } PoolIntArray MDIGizmo::get_first_index_pair_for_edge(int edge) { PoolIntArray ret; Vector eisarr = split_edge_indices(edge); if (eisarr.size() == 0) { return ret; } // these should have the same size PoolIntArray v0ei = eisarr[0]; PoolIntArray v1ei = eisarr[1]; for (int i = 0; i < _indices.size(); i += 3) { int i0 = _indices[i]; int i1 = _indices[i + 1]; int i2 = _indices[i + 2]; if (pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2)) { if (pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2)) { if (pool_int_arr_contains(v0ei, i0)) { ret.push_back(i0); } else if (pool_int_arr_contains(v0ei, i1)) { ret.push_back(i1); } else if (pool_int_arr_contains(v0ei, i2)) { ret.push_back(i2); } if (pool_int_arr_contains(v1ei, i0)) { ret.push_back(i0); } else if (pool_int_arr_contains(v1ei, i1)) { ret.push_back(i1); } else if (pool_int_arr_contains(v1ei, i2)) { ret.push_back(i2); } return ret; } } } return ret; } PoolIntArray MDIGizmo::get_all_index_pairs_for_edge(int edge) { PoolIntArray ret; Vector eisarr = split_edge_indices(edge); if (eisarr.size() == 0) { return ret; } // these should have the same size PoolIntArray v0ei = eisarr[0]; PoolIntArray v1ei = eisarr[1]; for (int i = 0; i < _indices.size(); i += 3) { int i0 = _indices[i]; int i1 = _indices[i + 1]; int i2 = _indices[i + 2]; if (pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2)) { if (pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2)) { if (pool_int_arr_contains(v0ei, i0)) { ret.push_back(i0); } else if (pool_int_arr_contains(v0ei, i1)) { ret.push_back(i1); } else if (pool_int_arr_contains(v0ei, i2)) { ret.push_back(i2); } if (pool_int_arr_contains(v1ei, i0)) { ret.push_back(i0); } else if (pool_int_arr_contains(v1ei, i1)) { ret.push_back(i1); } else if (pool_int_arr_contains(v1ei, i2)) { ret.push_back(i2); } } } } return ret; } void MDIGizmo::mark_seam() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() == 0) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { disable_change_event(); PoolIntArray prev_seams = copy_pool_int_array(_mdr->get_seams()); for (int i = 0; i < _selected_points.size(); ++i) { // se in _selected_points: int se = _selected_points[i]; PoolIntArray eis = MDREDMeshUtils::order_seam_indices(get_first_index_pair_for_edge(se)); if (eis.size() == 0) { continue; } MDREDMeshUtils::add_seam(_mdr, eis[0], eis[1]); } _undo_redo->create_action("mark_seam"); _undo_redo->add_do_method(_mdr.ptr(), "set_seams", copy_pool_int_array(_mdr->get_seams())); _undo_redo->add_undo_method(_mdr.ptr(), "set_seams", prev_seams); _undo_redo->commit_action(); enable_change_event(); } else if (selection_mode == SELECTION_MODE_FACE) { } } void MDIGizmo::unmark_seam() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() == 0) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { disable_change_event(); PoolIntArray prev_seams = copy_pool_int_array(_mdr->get_seams()); for (int i = 0; i < _selected_points.size(); ++i) { // se in _selected_points: int se = _selected_points[i]; PoolIntArray eis = MDREDMeshUtils::order_seam_indices(get_first_index_pair_for_edge(se)); if (eis.size() == 0) { continue; } MDREDMeshUtils::remove_seam(_mdr, eis[0], eis[1]); } _undo_redo->create_action("unmark_seam"); _undo_redo->add_do_method(_mdr.ptr(), "set_seams", copy_pool_int_array(_mdr->get_seams())); _undo_redo->add_undo_method(_mdr.ptr(), "set_seams", prev_seams); _undo_redo->commit_action(); enable_change_event(); } else if (selection_mode == SELECTION_MODE_FACE) { } } void MDIGizmo::set_seam(Ref mdr, PoolIntArray arr) { mdr->set_seams(arr); } void MDIGizmo::apply_seam() { if (!_mdr.is_valid()) { return; } disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); MDREDMeshUtils::apply_seam(_mdr); add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "apply_seam"); enable_change_event(); } void MDIGizmo::clean_mesh() { if (!_mdr.is_valid()) { return; } Array arrays = _mdr->get_array(); if (arrays.size() != ArrayMesh::ARRAY_MAX || arrays[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array vertices = arrays[ArrayMesh::ARRAY_VERTEX]; if (vertices.size() == 0) { return; } //int old_vert_size = vertices.size(); disable_change_event(); Array orig_arr = copy_arrays(arrays); arrays = MDREDMeshUtils::remove_used_vertices(arrays); //int new_vert_size = arrays[ArrayMesh::ARRAY_VERTEX].size(); add_mesh_change_undo_redo(orig_arr, arrays, "clean_mesh"); enable_change_event(); //var d : int = old_vert_size - new_vert_size; //print("MDRED: Removed " + str(d) + " unused vertices.") } void MDIGizmo::uv_unwrap() { if (!_mdr.is_valid()) { return; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return; } PoolVector3Array verts = mdr_arr[ArrayMesh::ARRAY_VERTEX]; if (verts.size() == 0) { return; } disable_change_event(); PoolVector2Array uvs = MeshUtils::get_singleton()->uv_unwrap(mdr_arr); if (uvs.size() != verts.size()) { ERR_PRINT("Error: Could not unwrap mesh!"); enable_change_event(false); return; } Array orig_arr = copy_arrays(mdr_arr); mdr_arr[ArrayMesh::ARRAY_TEX_UV] = uvs; add_mesh_change_undo_redo(orig_arr, mdr_arr, "uv_unwrap"); enable_change_event(); } void MDIGizmo::flip_selected_faces() { if (!_mdr.is_valid()) { return; } if (_selected_points.size() == 0) { return; } if (selection_mode == SELECTION_MODE_VERTEX) { } else if (selection_mode == SELECTION_MODE_EDGE) { } else if (selection_mode == SELECTION_MODE_FACE) { disable_change_event(); Array orig_arr = copy_arrays(_mdr->get_array()); PoolIntArray::Read r = _selected_points.read(); for (int i = 0; i < _selected_points.size(); ++i) { int sp = r[i]; int triangle_index = find_first_triangle_index_for_face(sp); MDREDMeshUtils::flip_triangle_ti(_mdr, triangle_index); } add_mesh_change_undo_redo(orig_arr, _mdr->get_array(), "Flip Faces"); enable_change_event(); } } void MDIGizmo::add_mesh_change_undo_redo(const Array &orig_arr, const Array &new_arr, const String &action_name) { _undo_redo->create_action(action_name); Array nac = copy_arrays(new_arr); _undo_redo->add_do_method(_mdr.ptr(), "set_array", nac); _undo_redo->add_undo_method(_mdr.ptr(), "set_array", orig_arr); _undo_redo->commit_action(); } void MDIGizmo::add_mesh_seam_change_undo_redo(const Array &orig_arr, const PoolIntArray &orig_seams, const Array &new_arr, const PoolIntArray &new_seams, const String &action_name) { _undo_redo->create_action(action_name); Array nac = copy_arrays(new_arr); _undo_redo->add_do_method(_mdr.ptr(), "set_array", nac); _undo_redo->add_undo_method(_mdr.ptr(), "set_array", orig_arr); _undo_redo->add_do_method(_mdr.ptr(), "set_seams", copy_pool_int_array(new_seams)); _undo_redo->add_undo_method(_mdr.ptr(), "set_seams", orig_seams); _undo_redo->commit_action(); } void MDIGizmo::apply_mesh_change(Ref mdr, const Array &arr) { if (!mdr.is_valid()) { return; } mdr->set_array(copy_arrays(arr)); } void MDIGizmo::apply_vertex_array(Ref mdr, const PoolVector3Array &verts) { if (!mdr.is_valid()) { return; } Array mdr_arr = mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX) { return; } mdr_arr[ArrayMesh::ARRAY_VERTEX] = verts; mdr->set_array(mdr_arr); } Array MDIGizmo::copy_arrays(const Array &arr) { return arr.duplicate(true); } PoolIntArray MDIGizmo::copy_pool_int_array(const PoolIntArray &pia) { PoolIntArray ret; ret.resize(pia.size()); PoolIntArray::Read r = pia.read(); PoolIntArray::Write w = ret.write(); for (int i = 0; i < pia.size(); ++i) { w[i] = r[i]; } r.release(); w.release(); return ret; } PoolVector3Array MDIGizmo::copy_mdr_verts_array() { PoolVector3Array ret; if (!_mdr.is_valid()) { return ret; } Array mdr_arr = _mdr->get_array(); if (mdr_arr.size() != ArrayMesh::ARRAY_MAX || mdr_arr[ArrayMesh::ARRAY_VERTEX].is_null()) { return ret; } PoolVector3Array vertices = mdr_arr[ArrayMesh::ARRAY_VERTEX]; ret.append_array(vertices); return ret; } void MDIGizmo::setup_op_drag_indices() { _drag_op_indices.resize(0); PoolIntArray::Read r = _selected_points.read(); for (int i = 0; i < _selected_points.size(); ++i) { int sp = r[i]; PoolIntArray pi = _handle_to_vertex_map[sp]; PoolIntArray::Read pir = pi.read(); for (int j = 0; j < pi.size(); ++j) { int indx = pir[j]; if (!pool_int_arr_contains(_drag_op_indices, indx)) { _drag_op_indices.append(indx); } } pir.release(); } } Vector3 MDIGizmo::get_drag_op_pivot() { if (pivot_type == PIVOT_TYPE_AVERAGED) { Vector3 avg = Vector3(); PoolIntArray::Read r = _drag_op_indices.read(); for (int i = 0; i < _drag_op_indices.size(); ++i) { avg += _vertices[r[i]]; } r.release(); avg /= _drag_op_indices.size(); return avg; } else if (pivot_type == PIVOT_TYPE_MDI_ORIGIN) { return Vector3(); } else if (pivot_type == PIVOT_TYPE_WORLD_ORIGIN) { return get_spatial_node()->to_local(Vector3()); } return Vector3(); } void MDIGizmo::select_handle_points(const PoolVector3Array &points) { _selected_points.resize(0); PoolVector3Array::Read r = points.read(); for (int ip = 0; ip < points.size(); ++ip) { Vector3 p = r[ip]; PoolVector3Array::Read hpr = _handle_points.read(); for (int i = 0; i < _handle_points.size(); ++i) { if (is_verts_equal(p, hpr[i])) { if (!pool_int_arr_contains(_selected_points, i)) { _selected_points.push_back(i); } } } hpr.release(); } redraw(); } void MDIGizmo::set_pivot_averaged() { pivot_type = PIVOT_TYPE_AVERAGED; } void MDIGizmo::set_pivot_mdi_origin() { pivot_type = PIVOT_TYPE_MDI_ORIGIN; } void MDIGizmo::set_pivot_world_origin() { pivot_type = PIVOT_TYPE_WORLD_ORIGIN; } void MDIGizmo::transfer_state_from(const Ref &other) { edit_mode = other->edit_mode; pivot_type = other->pivot_type; axis_constraint = other->axis_constraint; selection_mode = other->selection_mode; handle_selection_type = other->handle_selection_type; visual_indicator_outline = other->visual_indicator_outline; visual_indicator_seam = other->visual_indicator_seam; visual_indicator_handle = other->visual_indicator_handle; } void MDIGizmo::visual_indicator_outline_set(bool on) { visual_indicator_outline = on; redraw(); } void MDIGizmo::visual_indicator_seam_set(bool on) { visual_indicator_seam = on; redraw(); } void MDIGizmo::visual_indicator_handle_set(bool on) { visual_indicator_handle = on; redraw(); } void MDIGizmo::handle_selection_type_front() { handle_selection_type = HANDLE_SELECTION_TYPE_FRONT; } void MDIGizmo::handle_selection_type_back() { handle_selection_type = HANDLE_SELECTION_TYPE_BACK; } void MDIGizmo::handle_selection_type_all() { handle_selection_type = HANDLE_SELECTION_TYPE_ALL; } MDIGizmo::MDIGizmo() { gizmo_size = 3.0; edit_mode = EDIT_MODE_TRANSLATE; pivot_type = PIVOT_TYPE_AVERAGED; axis_constraint = AXIS_CONSTRAINT_X | AXIS_CONSTRAINT_Y | AXIS_CONSTRAINT_Z; selection_mode = SELECTION_MODE_VERTEX; handle_selection_type = HANDLE_SELECTION_TYPE_FRONT; visual_indicator_outline = true; visual_indicator_seam = true; visual_indicator_handle = true; _last_known_camera_facing = Vector3(0, 0, -1); _rect_drag = false; _rect_drag_min_ofset = 10; _mesh_outline_generator.instance(); _handle_drag_op = false; _editor_plugin = nullptr; _undo_redo = nullptr; _visible = false; } MDIGizmo::~MDIGizmo() { } void MDIGizmo::_bind_methods() { ClassDB::bind_method(D_METHOD("on_mdr_changed"), &MDIGizmo::on_mdr_changed); ClassDB::bind_method(D_METHOD("on_mesh_data_resource_changed"), &MDIGizmo::on_mesh_data_resource_changed); ClassDB::bind_method(D_METHOD("apply_mesh_change"), &MDIGizmo::apply_mesh_change); ClassDB::bind_method(D_METHOD("set_seam"), &MDIGizmo::set_seam); ClassDB::bind_method(D_METHOD("apply_vertex_array"), &MDIGizmo::apply_vertex_array); }