bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { if (!node) return false; Transform gt = node->get_global_transform(); Transform gi = gt.affine_inverse(); float depth = _get_depth() * 0.5; Vector3 n = gt.basis.get_axis(2).normalized(); Plane p(gt.origin + n * depth, n); Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { Vector2 gpoint = mb->get_position(); Vector3 ray_from = p_camera->project_ray_origin(gpoint); Vector3 ray_dir = p_camera->project_ray_normal(gpoint); Vector3 spoint; if (!p.intersects_ray(ray_from, ray_dir, &spoint)) return false; spoint = gi.xform(spoint); Vector2 cpoint(spoint.x, spoint.y); //DO NOT snap here, it's confusing in 3D for adding points. //Let the snap happen when the point is being moved, instead. //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); Vector<Vector2> poly = node->call("get_polygon"); //first check if a point is to be added (segment split) real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); switch (mode) { case MODE_CREATE: { if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { if (!wip_active) { wip.clear(); wip.push_back(cpoint); wip_active = true; edited_point_pos = cpoint; snap_ignore = false; _polygon_draw(); edited_point = 1; return true; } else { if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) { //wip closed _wip_close(); return true; } else { wip.push_back(cpoint); edited_point = wip.size(); snap_ignore = false; _polygon_draw(); return true; } } } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) { _wip_close(); } } break; case MODE_EDIT: { if (mb->get_button_index() == BUTTON_LEFT) { if (mb->is_pressed()) { if (mb->get_control()) { if (poly.size() < 3) { undo_redo->create_action(TTR("Edit Poly")); undo_redo->add_undo_method(node, "set_polygon", poly); poly.push_back(cpoint); undo_redo->add_do_method(node, "set_polygon", poly); undo_redo->add_do_method(this, "_polygon_draw"); undo_redo->add_undo_method(this, "_polygon_draw"); undo_redo->commit_action(); return true; } //search edges int closest_idx = -1; Vector2 closest_pos; real_t closest_dist = 1e10; for (int i = 0; i < poly.size(); i++) { Vector2 points[2] = { p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))), p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth))) }; Vector2 cp = Geometry::get_closest_point_to_segment_2d(gpoint, points); if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) continue; //not valid to reuse point real_t d = cp.distance_to(gpoint); if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; closest_idx = i; } } if (closest_idx >= 0) { pre_move_edit = poly; poly.insert(closest_idx + 1, cpoint); edited_point = closest_idx + 1; edited_point_pos = cpoint; node->call("set_polygon", poly); _polygon_draw(); snap_ignore = true; return true; } } else { //look for points to move int closest_idx = -1; Vector2 closest_pos; real_t closest_dist = 1e10; for (int i = 0; i < poly.size(); i++) { Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); real_t d = cp.distance_to(gpoint); if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; closest_idx = i; } } if (closest_idx >= 0) { pre_move_edit = poly; edited_point = closest_idx; edited_point_pos = poly[closest_idx]; _polygon_draw(); snap_ignore = false; return true; } } } else { snap_ignore = false; if (edited_point != -1) { //apply ERR_FAIL_INDEX_V(edited_point, poly.size(), false); poly[edited_point] = edited_point_pos; undo_redo->create_action(TTR("Edit Poly")); undo_redo->add_do_method(node, "set_polygon", poly); undo_redo->add_undo_method(node, "set_polygon", pre_move_edit); undo_redo->add_do_method(this, "_polygon_draw"); undo_redo->add_undo_method(this, "_polygon_draw"); undo_redo->commit_action(); edited_point = -1; return true; } } } if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) { int closest_idx = -1; Vector2 closest_pos; real_t closest_dist = 1e10; for (int i = 0; i < poly.size(); i++) { Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); real_t d = cp.distance_to(gpoint); if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; closest_idx = i; } } if (closest_idx >= 0) { undo_redo->create_action(TTR("Edit Poly (Remove Point)")); undo_redo->add_undo_method(node, "set_polygon", poly); poly.remove(closest_idx); undo_redo->add_do_method(node, "set_polygon", poly); undo_redo->add_do_method(this, "_polygon_draw"); undo_redo->add_undo_method(this, "_polygon_draw"); undo_redo->commit_action(); return true; } } } break; } } Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) { Vector2 gpoint = mm->get_position(); Vector3 ray_from = p_camera->project_ray_origin(gpoint); Vector3 ray_dir = p_camera->project_ray_normal(gpoint); Vector3 spoint; if (!p.intersects_ray(ray_from, ray_dir, &spoint)) return false; spoint = gi.xform(spoint); Vector2 cpoint(spoint.x, spoint.y); if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { snap_ignore = false; } if (!snap_ignore) { cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); } edited_point_pos = cpoint; _polygon_draw(); } } return false; }
float AudioEffectChorus::get_voice_cutoff_hz(int p_voice) const { ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0); return voice[p_voice].cutoff; }
Vector<int> NavigationMesh::get_polygon(int p_idx) { ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); return polygons[p_idx].indices; }
int Camera2D::get_limit(Margin p_margin) const{ ERR_FAIL_INDEX_V(p_margin,4,0); return limit[p_margin]; }
float AudioEffectChorus::get_voice_depth_ms(int p_voice) const { ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0); return voice[p_voice].depth; }
NodePath EditorData::get_edited_scene_live_edit_root() { ERR_FAIL_INDEX_V(current_edited_scene, edited_scene.size(), String()); return edited_scene[current_edited_scene].live_edit_root; }
bool Environment::is_fx_enabled(Fx p_effect) const { ERR_FAIL_INDEX_V(p_effect, FX_MAX, false); return fx_enabled[p_effect]; }
String VisualScriptBuiltinFunc::get_func_name(BuiltinFunc p_func) { ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); return func_name[p_func]; }
bool VisibilityEnabler2D::is_enabler_enabled(Enabler p_enabler) const{ ERR_FAIL_INDEX_V(p_enabler,ENABLER_MAX,false); return enabler[p_enabler]; }
float Curve3D::get_point_tilt(int p_index) const { ERR_FAIL_INDEX_V(p_index,points.size(),0); return points[p_index].tilt; }
Vector3 Curve3D::get_point_out(int p_index) const { ERR_FAIL_INDEX_V(p_index,points.size(),Vector3()); return points[p_index].out; }
Vector2 Curve2D::get_point_in(int p_index) const { ERR_FAIL_INDEX_V(p_index,points.size(),Vector2()); return points[p_index].in; }
Ref<ResourceImportMetadata> EditorData::get_edited_scene_import_metadata() const{ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),Ref<ResourceImportMetadata>()); return edited_scene[current_edited_scene].medatata; }
Node* EditorData::get_edited_scene_root(){ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),NULL); return edited_scene[current_edited_scene].root; }
uint64_t EditorData::get_edited_scene_version() const { ERR_FAIL_INDEX_V(current_edited_scene, edited_scene.size(), 0); return edited_scene[current_edited_scene].version; }
bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const{ ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false); return flags[p_flag]; }
uint64_t EditorData::get_scene_version(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), false); return edited_scene[p_idx].version; }
float SoundRoomParams::get_param(Params p_param) const { ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0); return params[p_param]; }
Variant Environment::get_background_param(BGParam p_param) const { ERR_FAIL_INDEX_V(p_param, BG_PARAM_MAX, Variant()); return bg_param[p_param]; }
ScriptLanguage* ScriptServer::get_language(int p_idx) { ERR_FAIL_INDEX_V(p_idx,_language_count,NULL); return _languages[p_idx]; }
Variant Environment::fx_get_param(FxParam p_param) const { ERR_FAIL_INDEX_V(p_param, FX_PARAM_MAX, Variant()); return fx_param[p_param]; }
bool EditorHistory::is_history_obj_inspector_only(int p_obj) const { ERR_FAIL_INDEX_V(p_obj, history.size(), false); ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), false); return history[p_obj].path[history[p_obj].level].inspector_only; }
float Camera2D::get_drag_margin(Margin p_margin) const{ ERR_FAIL_INDEX_V(p_margin,4,0); return drag_margin[p_margin]; }
ObjectID EditorHistory::get_history_obj(int p_obj) const { ERR_FAIL_INDEX_V(p_obj, history.size(), 0); ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), 0); return history[p_obj].path[history[p_obj].level].object; }
float AudioEffectChorus::get_voice_level_db(int p_voice) const { ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0); return voice[p_voice].level; }
Dictionary EditorData::get_scene_editor_states(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), Dictionary()); EditedScene es = edited_scene[p_idx]; return es.editor_states; }
float AudioEffectChorus::get_voice_pan(int p_voice) const { ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0); return voice[p_voice].pan; }
EditorPlugin *EditorData::get_editor_plugin(int p_idx) { ERR_FAIL_INDEX_V(p_idx, editor_plugins.size(), NULL); return editor_plugins[p_idx]; }
Vector<String> EditorFileSystemDirectory::get_file_deps(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, files.size(), Vector<String>()); return files[p_idx]->deps; }
StringName NodePath::get_subname(int p_idx) const { ERR_FAIL_COND_V(!data, StringName()); ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName()); return data->subpath[p_idx]; }