From cec004adf08fc57cd1d27408ee5b67637355f2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilles=20Roudi=C3=A8re?= Date: Tue, 19 Oct 2021 11:40:46 +0200 Subject: Implement polygons editors in the tiles selection mode --- editor/editor_inspector.cpp | 12 +- editor/icons/MirrorX.svg | 1 + editor/icons/MirrorY.svg | 1 + editor/icons/RotateLeft.svg | 1 + editor/icons/RotateRight.svg | 1 + editor/plugins/tiles/tile_data_editors.cpp | 117 +++++++++++-- editor/plugins/tiles/tile_data_editors.h | 11 +- .../plugins/tiles/tile_set_atlas_source_editor.cpp | 187 ++++++++++++++++++++- .../plugins/tiles/tile_set_atlas_source_editor.h | 36 +++- scene/resources/tile_set.cpp | 19 ++- scene/resources/tile_set.h | 3 +- 11 files changed, 363 insertions(+), 26 deletions(-) create mode 100644 editor/icons/MirrorX.svg create mode 100644 editor/icons/MirrorY.svg create mode 100644 editor/icons/RotateLeft.svg create mode 100644 editor/icons/RotateRight.svg diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 9f049a0e58..0bfcd2383f 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3143,12 +3143,20 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } else { undo_redo->create_action(vformat(TTR("Set %s"), p_name), UndoRedo::MERGE_ENDS); undo_redo->add_do_property(object, p_name, p_value); - undo_redo->add_undo_property(object, p_name, object->get(p_name)); + bool valid = false; + Variant value = object->get(p_name, &valid); + if (valid) { + undo_redo->add_undo_property(object, p_name, value); + } PropertyInfo prop_info; if (ClassDB::get_property_info(object->get_class_name(), p_name, &prop_info)) { for (const String &linked_prop : prop_info.linked_properties) { - undo_redo->add_undo_property(object, linked_prop, object->get(linked_prop)); + valid = false; + value = object->get(linked_prop, &valid); + if (valid) { + undo_redo->add_undo_property(object, linked_prop, value); + } } } diff --git a/editor/icons/MirrorX.svg b/editor/icons/MirrorX.svg new file mode 100644 index 0000000000..fa668986ac --- /dev/null +++ b/editor/icons/MirrorX.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/MirrorY.svg b/editor/icons/MirrorY.svg new file mode 100644 index 0000000000..bb4e4d3543 --- /dev/null +++ b/editor/icons/MirrorY.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/RotateLeft.svg b/editor/icons/RotateLeft.svg new file mode 100644 index 0000000000..1200df1dde --- /dev/null +++ b/editor/icons/RotateLeft.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/RotateRight.svg b/editor/icons/RotateRight.svg new file mode 100644 index 0000000000..d69e6a7705 --- /dev/null +++ b/editor/icons/RotateRight.svg @@ -0,0 +1 @@ + diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index e9d80bb4b8..a215939de9 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -125,7 +125,14 @@ void GenericTilePolygonEditor::_base_control_draw() { Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); const Ref handle = get_theme_icon(SNAME("EditorPathSharpHandle"), SNAME("EditorIcons")); const Ref add_handle = get_theme_icon(SNAME("EditorHandleAdd"), SNAME("EditorIcons")); + const Ref focus_stylebox = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles")); + // Draw the focus rectangle. + if (base_control->has_focus()) { + base_control->draw_style_box(focus_stylebox, Rect2(Vector2(), base_control->get_size())); + } + + // Draw tile-related things. Size2 tile_size = tile_set->get_tile_size(); Transform2D xform; @@ -240,9 +247,10 @@ void GenericTilePolygonEditor::_zoom_changed() { } void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { + UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo); switch (p_item_pressed) { case RESET_TO_DEFAULT_TILE: { - undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->create_action(TTR("Reset Polygons")); undo_redo->add_do_method(this, "clear_polygons"); Vector polygon = tile_set->get_tile_shape_polygon(); for (int i = 0; i < polygon.size(); i++) { @@ -260,7 +268,7 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { undo_redo->commit_action(true); } break; case CLEAR_TILE: { - undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->create_action(TTR("Clear Polygons")); undo_redo->add_do_method(this, "clear_polygons"); undo_redo->add_do_method(base_control, "update"); undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); @@ -272,9 +280,50 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); undo_redo->commit_action(true); } break; + case ROTATE_RIGHT: + case ROTATE_LEFT: + case FLIP_HORIZONTALLY: + case FLIP_VERTICALLY: { + undo_redo->create_action(TTR("Rotate Polygons Left")); + for (unsigned int i = 0; i < polygons.size(); i++) { + Vector new_polygon; + for (int point_index = 0; point_index < polygons[i].size(); point_index++) { + Vector2 point = polygons[i][point_index]; + switch (p_item_pressed) { + case ROTATE_RIGHT: { + point = Vector2(-point.y, point.x); + } break; + case ROTATE_LEFT: { + point = Vector2(point.y, -point.x); + } break; + case FLIP_HORIZONTALLY: { + point = Vector2(-point.x, point.y); + } break; + case FLIP_VERTICALLY: { + point = Vector2(point.x, -point.y); + } break; + default: + break; + } + new_polygon.push_back(point); + } + undo_redo->add_do_method(this, "set_polygon", i, new_polygon); + } + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); + for (unsigned int i = 0; i < polygons.size(); i++) { + undo_redo->add_undo_method(this, "set_polygon", polygons[i]); + } + undo_redo->add_undo_method(base_control, "update"); + undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); + undo_redo->commit_action(true); + } break; default: break; } + if (!use_undo_redo) { + memdelete(undo_redo); + } } void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) { @@ -359,6 +408,7 @@ void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) { } void GenericTilePolygonEditor::_base_control_gui_input(Ref p_event) { + UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo); real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius"); hovered_polygon_index = -1; @@ -549,21 +599,47 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref p_event) } base_control->update(); + + if (!use_undo_redo) { + memdelete(undo_redo); + } +} + +void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) { + use_undo_redo = p_use_undo_redo; } void GenericTilePolygonEditor::set_tile_set(Ref p_tile_set) { - if (tile_set != p_tile_set) { - // Set the default tile shape - clear_polygons(); - if (p_tile_set.is_valid()) { - Vector polygon = p_tile_set->get_tile_shape_polygon(); - for (int i = 0; i < polygon.size(); i++) { - polygon.write[i] = polygon[i] * p_tile_set->get_tile_size(); - } - add_polygon(polygon); + ERR_FAIL_COND(!p_tile_set.is_valid()); + if (tile_set == p_tile_set) { + return; + } + + // Set the default tile shape + clear_polygons(); + if (p_tile_set.is_valid()) { + Vector polygon = p_tile_set->get_tile_shape_polygon(); + for (int i = 0; i < polygon.size(); i++) { + polygon.write[i] = polygon[i] * p_tile_set->get_tile_size(); } + add_polygon(polygon); } + tile_set = p_tile_set; + + // Set the default zoom value. + int default_control_y_size = 200 * EDSCALE; + Vector2 zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); + while (zoomed_tile.y < default_control_y_size) { + editor_zoom_widget->set_zoom_by_increments(6, false); + zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); + } + while (zoomed_tile.y > default_control_y_size) { + editor_zoom_widget->set_zoom_by_increments(-6, false); + zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); + } + editor_zoom_widget->set_zoom_by_increments(-6, false); + _zoom_changed(); } void GenericTilePolygonEditor::set_background(Ref p_texture, Rect2 p_region, Vector2 p_offset, bool p_flip_h, bool p_flip_v, bool p_transpose, Color p_modulate) { @@ -644,6 +720,12 @@ void GenericTilePolygonEditor::_notification(int p_what) { button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons"))); button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + + PopupMenu *p = button_advanced_menu->get_popup(); + p->set_item_icon(p->get_item_index(ROTATE_RIGHT), get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons"))); + p->set_item_icon(p->get_item_index(ROTATE_LEFT), get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons"))); + p->set_item_icon(p->get_item_index(FLIP_HORIZONTALLY), get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons"))); + p->set_item_icon(p->get_item_index(FLIP_VERTICALLY), get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons"))); break; } } @@ -670,18 +752,21 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { button_create->set_toggle_mode(true); button_create->set_button_group(tools_button_group); button_create->set_pressed(true); + button_create->set_tooltip(TTR("Add polygon tool")); toolbar->add_child(button_create); button_edit = memnew(Button); button_edit->set_flat(true); button_edit->set_toggle_mode(true); button_edit->set_button_group(tools_button_group); + button_edit->set_tooltip(TTR("Edit points tool")); toolbar->add_child(button_edit); button_delete = memnew(Button); button_delete->set_flat(true); button_delete->set_toggle_mode(true); button_delete->set_button_group(tools_button_group); + button_delete->set_tooltip(TTR("Delete points tool")); toolbar->add_child(button_delete); button_advanced_menu = memnew(MenuButton); @@ -689,7 +774,13 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { button_advanced_menu->set_toggle_mode(true); button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE); button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE); + button_advanced_menu->get_popup()->add_separator(); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")), TTR("Rotate Right"), ROTATE_RIGHT); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")), TTR("Rotate Left"), ROTATE_LEFT); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")), TTR("Flip Horizontally"), FLIP_HORIZONTALLY); + button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")), TTR("Flip Vertically"), FLIP_VERTICALLY); button_advanced_menu->get_popup()->connect("id_pressed", callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed)); + button_advanced_menu->set_focus_mode(FOCUS_ALL); toolbar->add_child(button_advanced_menu); toolbar->add_child(memnew(VSeparator)); @@ -698,6 +789,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { button_pixel_snap->set_flat(true); button_pixel_snap->set_toggle_mode(true); button_pixel_snap->set_pressed(true); + button_pixel_snap->set_tooltip(TTR("Snap to half-pixel")); toolbar->add_child(button_pixel_snap); Control *root = memnew(Control); @@ -717,6 +809,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { base_control->connect("draw", callable_mp(this, &GenericTilePolygonEditor::_base_control_draw)); base_control->connect("gui_input", callable_mp(this, &GenericTilePolygonEditor::_base_control_gui_input)); base_control->set_clip_contents(true); + base_control->set_focus_mode(Control::FOCUS_CLICK); root->add_child(base_control); editor_zoom_widget = memnew(EditorZoomWidget); @@ -1432,7 +1525,7 @@ TileDataCollisionEditor::TileDataCollisionEditor() { angular_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); angular_velocity_editor->update_property(); add_child(angular_velocity_editor); - property_editors["angular_velocity"] = linear_velocity_editor; + property_editors["angular_velocity"] = angular_velocity_editor; _polygons_changed(); } diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index acb4c5882d..3fc5e738bb 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -94,7 +94,8 @@ private: LocalVector> polygons; bool multiple_polygon_mode = false; - UndoRedo *undo_redo = EditorNode::get_undo_redo(); + bool use_undo_redo = true; + UndoRedo *editor_undo_redo = EditorNode::get_undo_redo(); // UI int hovered_polygon_index = -1; @@ -108,7 +109,7 @@ private: DRAG_TYPE_CREATE_POINT, DRAG_TYPE_PAN, }; - DragType drag_type; + DragType drag_type = DRAG_TYPE_NONE; int drag_polygon_index; int drag_point_index; Vector2 drag_last_pos; @@ -143,6 +144,10 @@ private: enum AdvancedMenuOption { RESET_TO_DEFAULT_TILE, CLEAR_TILE, + ROTATE_RIGHT, + ROTATE_LEFT, + FLIP_HORIZONTALLY, + FLIP_VERTICALLY, }; void _base_control_draw(); @@ -161,6 +166,8 @@ protected: static void _bind_methods(); public: + void set_use_undo_redo(bool p_use_undo_redo); + void set_tile_set(Ref p_tile_set); void set_background(Ref p_texture, Rect2 p_region = Rect2(), Vector2 p_offset = Vector2(), bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, Color p_modulate = Color(1.0, 1.0, 1.0, 0.0)); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index c8892bceaa..31b2f96b62 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2057,7 +2057,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo int new_polygons_count = p_new_value; int old_polygons_count = tile_data_proxy->get(vformat("physics_layer_%d/polygons_count", layer_index)); if (new_polygons_count < old_polygons_count) { - for (int i = new_polygons_count - 1; i < old_polygons_count; i++) { + for (int i = new_polygons_count; i < old_polygons_count; i++) { ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/points", layer_index, i)); ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i)); ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i)); @@ -2530,9 +2530,194 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { right_panel->add_child(tile_atlas_view_missing_source_label); EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback)); + + // Inspector plugin. + Ref tile_data_inspector_plugin; + tile_data_inspector_plugin.instantiate(); + EditorInspector::add_inspector_plugin(tile_data_inspector_plugin); } TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() { memdelete(tile_proxy_object); memdelete(atlas_source_proxy_object); } + +////// EditorPropertyTilePolygon ////// + +void EditorPropertyTilePolygon::_add_focusable_children(Node *p_node) { + Control *control = Object::cast_to(p_node); + if (control && control->get_focus_mode() != Control::FOCUS_NONE) { + add_focusable(control); + } + for (int i = 0; i < p_node->get_child_count(); i++) { + _add_focusable_children(p_node->get_child(i)); + } +} + +void EditorPropertyTilePolygon::_polygons_changed() { + if (String(count_property).is_empty()) { + if (base_type == "OccluderPolygon2D") { + // Single OccluderPolygon2D. + Ref occluder; + if (generic_tile_polygon_editor->get_polygon_count() >= 1) { + occluder.instantiate(); + occluder->set_polygon(generic_tile_polygon_editor->get_polygon(0)); + } + emit_changed(get_edited_property(), occluder); + } else if (base_type == "NavigationPolygon") { + Ref navigation_polygon; + if (generic_tile_polygon_editor->get_polygon_count() >= 1) { + navigation_polygon.instantiate(); + for (int i = 0; i < generic_tile_polygon_editor->get_polygon_count(); i++) { + Vector polygon = generic_tile_polygon_editor->get_polygon(i); + navigation_polygon->add_outline(polygon); + } + navigation_polygon->make_polygons_from_outlines(); + } + emit_changed(get_edited_property(), navigation_polygon); + } + } else { + if (base_type.is_empty()) { + // Multiple array of vertices. + Vector changed_properties; + Array values; + int count = generic_tile_polygon_editor->get_polygon_count(); + changed_properties.push_back(count_property); + values.push_back(count); + for (int i = 0; i < count; i++) { + changed_properties.push_back(vformat(element_pattern, i)); + values.push_back(generic_tile_polygon_editor->get_polygon(i)); + } + emit_signal("multiple_properties_changed", changed_properties, values, false); + } + } +} + +void EditorPropertyTilePolygon::update_property() { + TileSetAtlasSourceEditor::AtlasTileProxyObject *atlas_tile_proxy_object = Object::cast_to(get_edited_object()); + ERR_FAIL_COND(!atlas_tile_proxy_object); + ERR_FAIL_COND(atlas_tile_proxy_object->get_edited_tiles().is_empty()); + + TileSetAtlasSource *tile_set_atlas_source = atlas_tile_proxy_object->get_edited_tile_set_atlas_source(); + generic_tile_polygon_editor->set_tile_set(Ref(tile_set_atlas_source->get_tile_set())); + + // Set the background + Vector2i coords = atlas_tile_proxy_object->get_edited_tiles().front()->get().tile; + int alternative = atlas_tile_proxy_object->get_edited_tiles().front()->get().alternative; + TileData *tile_data = Object::cast_to(tile_set_atlas_source->get_tile_data(coords, alternative)); + generic_tile_polygon_editor->set_background(tile_set_atlas_source->get_texture(), tile_set_atlas_source->get_tile_texture_region(coords), tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); + + // Reset the polygons. + generic_tile_polygon_editor->clear_polygons(); + + if (String(count_property).is_empty()) { + if (base_type == "OccluderPolygon2D") { + // Single OccluderPolygon2D. + Ref occluder = get_edited_object()->get(get_edited_property()); + generic_tile_polygon_editor->clear_polygons(); + if (occluder.is_valid()) { + generic_tile_polygon_editor->add_polygon(occluder->get_polygon()); + } + } else if (base_type == "NavigationPolygon") { + // Single OccluderPolygon2D. + Ref navigation_polygon = get_edited_object()->get(get_edited_property()); + generic_tile_polygon_editor->clear_polygons(); + if (navigation_polygon.is_valid()) { + for (int i = 0; i < navigation_polygon->get_outline_count(); i++) { + generic_tile_polygon_editor->add_polygon(navigation_polygon->get_outline(i)); + } + } + } + } else { + int count = get_edited_object()->get(count_property); + if (base_type.is_empty()) { + // Multiple array of vertices. + generic_tile_polygon_editor->clear_polygons(); + for (int i = 0; i < count; i++) { + generic_tile_polygon_editor->add_polygon(get_edited_object()->get(vformat(element_pattern, i))); + } + } + } +} + +void EditorPropertyTilePolygon::setup_single_mode(const StringName &p_property, const String &p_base_type) { + set_object_and_property(nullptr, p_property); + base_type = p_base_type; + + generic_tile_polygon_editor->set_multiple_polygon_mode(false); +} + +void EditorPropertyTilePolygon::setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type) { + set_object_and_property(nullptr, p_property); + count_property = p_count_property; + element_pattern = p_element_pattern; + base_type = p_base_type; + + generic_tile_polygon_editor->set_multiple_polygon_mode(true); +} + +EditorPropertyTilePolygon::EditorPropertyTilePolygon() { + // Setup the polygon editor. + generic_tile_polygon_editor = memnew(GenericTilePolygonEditor); + generic_tile_polygon_editor->set_use_undo_redo(false); + generic_tile_polygon_editor->clear_polygons(); + add_child(generic_tile_polygon_editor); + generic_tile_polygon_editor->connect("polygons_changed", callable_mp(this, &EditorPropertyTilePolygon::_polygons_changed)); + + // Add all focussable children of generic_tile_polygon_editor as focussable. + _add_focusable_children(generic_tile_polygon_editor); +} + +////// EditorInspectorPluginTileData ////// + +bool EditorInspectorPluginTileData::can_handle(Object *p_object) { + return Object::cast_to(p_object) != nullptr; +} + +bool EditorInspectorPluginTileData::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { + Vector components = String(p_path).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygon") { + EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon); + ep->setup_single_mode(p_path, "OccluderPolygon2D"); + add_property_editor(p_path, ep); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { + // Physics layers. + int layer_index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygons_count") { + EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon); + ep->setup_multiple_mode(vformat("physics_layer_%d/polygons", layer_index), vformat("physics_layer_%d/polygons_count", layer_index), vformat("physics_layer_%d/polygon_%%d/points", layer_index), ""); + Vector properties; + properties.push_back(p_path); + int count = p_object->get(vformat("physics_layer_%d/polygons_count", layer_index)); + for (int i = 0; i < count; i++) { + properties.push_back(vformat(vformat("physics_layer_%d/polygon_%d/points", layer_index, i))); + } + add_property_editor_for_multiple_properties("Polygons", properties, ep); + return true; + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (components[2] == "points") { + return true; + } + } + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { + // Navigation layers. + int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygon") { + EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon); + ep->setup_single_mode(p_path, "NavigationPolygon"); + add_property_editor(p_path, ep); + return true; + } + } + return false; +} diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index ea6f2847ae..bd1fd2e7d0 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -43,7 +43,7 @@ class TileSet; class TileSetAtlasSourceEditor : public HBoxContainer { GDCLASS(TileSetAtlasSourceEditor, HBoxContainer); -private: +public: // A class to store which tiles are selected. struct TileSelection { Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS; @@ -99,6 +99,9 @@ private: static void _bind_methods(); public: + TileSetAtlasSource *get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; }; + Set get_edited_tiles() const { return tiles; }; + // Update the proxyed object. void edit(TileSetAtlasSource *p_tile_set_atlas_source, Set p_tiles = Set()); @@ -107,6 +110,7 @@ private: } }; +private: Ref tile_set; TileSetAtlasSource *tile_set_atlas_source = nullptr; int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; @@ -281,4 +285,34 @@ public: ~TileSetAtlasSourceEditor(); }; +class EditorPropertyTilePolygon : public EditorProperty { + GDCLASS(EditorPropertyTilePolygon, EditorProperty); + + StringName count_property; + String element_pattern; + String base_type; + + void _add_focusable_children(Node *p_node); + + GenericTilePolygonEditor *generic_tile_polygon_editor; + void _polygons_changed(); + +public: + virtual void update_property() override; + void setup_single_mode(const StringName &p_property, const String &p_base_type); + void setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type); + EditorPropertyTilePolygon(); +}; + +class EditorInspectorPluginTileData : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginTileData, EditorInspectorPlugin); + + void _occlusion_polygon_set_callback(); + void _polygons_changed(Object *p_generic_tile_polygon_editor, Object *p_object, const String &p_path); + +public: + virtual bool can_handle(Object *p_object) override; + virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false) override; +}; + #endif // TILE_SET_ATLAS_SOURCE_EDITOR_H diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 6411a6d233..928f320db4 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -3277,6 +3277,10 @@ void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { } } +const TileSet *TileSetAtlasSource::get_tile_set() const { + return tile_set; +} + void TileSetAtlasSource::notify_tile_data_properties_should_change() { // Set the TileSet on all TileData. for (KeyValue &E_tile : tiles) { @@ -3850,13 +3854,17 @@ void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); ERR_FAIL_COND(p_frames_count < 1); + int old_size = tiles[p_atlas_coords].animation_frames_durations.size(); + if (p_frames_count == old_size) { + return; + } + TileAlternativesData &tad = tiles[p_atlas_coords]; bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords); ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); _clear_coords_mapping_cache(p_atlas_coords); - int old_size = tiles[p_atlas_coords].animation_frames_durations.size(); tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count); for (int i = old_size; i < p_frames_count; i++) { tiles[p_atlas_coords].animation_frames_durations[i] = 1.0; @@ -4745,6 +4753,9 @@ real_t TileData::get_constant_angular_velocity(int p_layer_id) const { void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { ERR_FAIL_INDEX(p_layer_id, physics.size()); ERR_FAIL_COND(p_polygons_count < 0); + if (p_polygons_count == physics.write[p_layer_id].polygons.size()) { + return; + } physics.write[p_layer_id].polygons.resize(p_polygons_count); notify_property_list_changed(); emit_signal(SNAME("changed")); @@ -4950,9 +4961,6 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(layer_index < 0, false); if (components[1] == "polygon") { Ref polygon = p_value; - if (!polygon.is_valid()) { - return false; - } if (layer_index >= occluders.size()) { if (tile_set) { @@ -5024,9 +5032,6 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(layer_index < 0, false); if (components[1] == "polygon") { Ref polygon = p_value; - if (!polygon.is_valid()) { - return false; - } if (layer_index >= navigation.size()) { if (tile_set) { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 52446fd680..9a993fd19e 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -590,6 +590,7 @@ protected: public: // Not exposed. virtual void set_tile_set(const TileSet *p_tile_set) override; + const TileSet *get_tile_set() const; virtual void notify_tile_data_properties_should_change() override; virtual void add_occlusion_layer(int p_index) override; virtual void move_occlusion_layer(int p_from_index, int p_to_pos) override; @@ -743,7 +744,7 @@ private: }; Vector2 linear_velocity; - float angular_velocity = 0.0; + double angular_velocity = 0.0; Vector polygons; }; Vector physics; -- cgit v1.2.3 From 80b563672b075fb4b0eff691177f84d44e36bc39 Mon Sep 17 00:00:00 2001 From: Yuri Roubinsky Date: Fri, 5 Nov 2021 22:57:24 +0300 Subject: Added `MeshEmitter` node for particles in visual shader --- .../VisualShaderNodeParticleMeshEmitter.xml | 17 ++ editor/plugins/visual_shader_editor_plugin.cpp | 1 + scene/register_scene_types.cpp | 1 + scene/resources/visual_shader.cpp | 6 + scene/resources/visual_shader.h | 2 + scene/resources/visual_shader_nodes.cpp | 5 - scene/resources/visual_shader_particle_nodes.cpp | 280 ++++++++++++++++++++- scene/resources/visual_shader_particle_nodes.h | 47 +++- 8 files changed, 352 insertions(+), 7 deletions(-) create mode 100644 doc/classes/VisualShaderNodeParticleMeshEmitter.xml diff --git a/doc/classes/VisualShaderNodeParticleMeshEmitter.xml b/doc/classes/VisualShaderNodeParticleMeshEmitter.xml new file mode 100644 index 0000000000..7abb330fd3 --- /dev/null +++ b/doc/classes/VisualShaderNodeParticleMeshEmitter.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 10a9b2bb10..c7c6b9b0ee 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -4527,6 +4527,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", "A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters.", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("MeshEmitter", "Particles", "Emitters", "VisualShaderNodeParticleMeshEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index d44cd7ca63..d96126aae0 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -628,6 +628,7 @@ void register_scene_types() { GDREGISTER_CLASS(VisualShaderNodeParticleSphereEmitter); GDREGISTER_CLASS(VisualShaderNodeParticleBoxEmitter); GDREGISTER_CLASS(VisualShaderNodeParticleRingEmitter); + GDREGISTER_CLASS(VisualShaderNodeParticleMeshEmitter); GDREGISTER_CLASS(VisualShaderNodeParticleMultiplyByAxisAngle); GDREGISTER_CLASS(VisualShaderNodeParticleConeVelocity); GDREGISTER_CLASS(VisualShaderNodeParticleRandomness); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 0aa79b8ceb..4496feeb14 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -36,6 +36,11 @@ #include "visual_shader_particle_nodes.h" #include "visual_shader_sdf_nodes.h" +String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) { + static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; + return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id); +} + bool VisualShaderNode::is_simple_decl() const { return simple_decl; } @@ -1829,6 +1834,7 @@ void VisualShader::_update_shader() const { code += " vec3 __vec3_buff2;\n"; code += " float __scalar_buff1;\n"; code += " float __scalar_buff2;\n"; + code += " int __scalar_ibuff;\n"; code += " vec3 __ndiff = normalize(__diff);\n\n"; } if (has_start) { diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 7743affba7..09f91e54c8 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -697,4 +697,6 @@ public: VisualShaderNodeGlobalExpression(); }; +extern String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name); + #endif // VISUAL_SHADER_H diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index c3d9ef7b04..5a61df4a03 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -494,11 +494,6 @@ String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const { return ""; } -static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) { - static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" }; - return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id); -} - Vector VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { VisualShader::DefaultTextureParam dtp; dtp.name = make_unique_id(p_type, p_id, "tex"); diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index ada91cec1e..5bdaa8b272 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -30,6 +30,8 @@ #include "visual_shader_particle_nodes.h" +#include "core/core_string_names.h" + // VisualShaderNodeParticleEmitter int VisualShaderNodeParticleEmitter::get_output_port_count() const { @@ -255,6 +257,283 @@ VisualShaderNodeParticleRingEmitter::VisualShaderNodeParticleRingEmitter() { set_input_port_default_value(2, 0.0); } +// VisualShaderNodeParticleMeshEmitter + +String VisualShaderNodeParticleMeshEmitter::get_caption() const { + return "MeshEmitter"; +} + +int VisualShaderNodeParticleMeshEmitter::get_output_port_count() const { + return 2; +} + +VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_output_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR; // position + case 1: + return PORT_TYPE_VECTOR; // normal + } + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleMeshEmitter::get_output_port_name(int p_port) const { + switch (p_port) { + case 0: + return "position"; + case 1: + return "normal"; + } + return String(); +} + +int VisualShaderNodeParticleMeshEmitter::get_input_port_count() const { + return 0; +} + +VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeParticleMeshEmitter::get_input_port_name(int p_port) const { + return String(); +} + +String VisualShaderNodeParticleMeshEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code; + + if (mesh.is_valid()) { + code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n"; + code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n"; + } + + return code; +} + +String VisualShaderNodeParticleMeshEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + String code; + + code += " __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n"; + + if (position_texture->get_width() == 0) { + code += " " + p_output_vars[0] + " = vec3(0.0);\n"; + } else { + if (mode_2d) { + code += " " + p_output_vars[0] + " = vec3("; + code += "texelFetch("; + code += make_unique_id(p_type, p_id, "mesh_vx") + ", "; + code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n"; + } else { + code += " " + p_output_vars[0] + " = texelFetch("; + code += make_unique_id(p_type, p_id, "mesh_vx") + ", "; + code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n"; + } + } + + if (normal_texture->get_width() == 0) { + code += " " + p_output_vars[1] + " = vec3(0.0);\n"; + } else { + if (mode_2d) { + code += " " + p_output_vars[1] + " = vec3("; + code += "texelFetch("; + code += make_unique_id(p_type, p_id, "mesh_nm") + ", "; + code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n"; + } else { + code += " " + p_output_vars[1] + " = texelFetch("; + code += make_unique_id(p_type, p_id, "mesh_nm") + ", "; + code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n"; + } + } + + return code; +} + +Vector VisualShaderNodeParticleMeshEmitter::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp_vx; + dtp_vx.name = make_unique_id(p_type, p_id, "mesh_vx"); + dtp_vx.param = position_texture; + + VisualShader::DefaultTextureParam dtp_nm; + dtp_nm.name = make_unique_id(p_type, p_id, "mesh_nm"); + dtp_nm.param = normal_texture; + + Vector ret; + ret.push_back(dtp_vx); + ret.push_back(dtp_nm); + return ret; +} + +void VisualShaderNodeParticleMeshEmitter::update_texture() { + if (!mesh.is_valid()) { + return; + } + + Vector vertices; + Vector normals; + + if (use_all_surfaces) { + for (int i = 0; i < max_surface_index; i++) { + Array vertex_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_VERTEX]; + for (int j = 0; j < vertex_array.size(); j++) { + vertices.push_back((Vector3)vertex_array[j]); + } + + Array normal_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_NORMAL]; + for (int j = 0; j < vertex_array.size(); j++) { + normals.push_back((Vector3)vertex_array[j]); + } + } + } else { + Array vertex_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_VERTEX]; + for (int i = 0; i < vertex_array.size(); i++) { + vertices.push_back((Vector3)vertex_array[i]); + } + + Array normal_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_NORMAL]; + for (int i = 0; i < normal_array.size(); i++) { + normals.push_back((Vector3)normal_array[i]); + } + } + + // vertices + { + Ref image; + image.instantiate(); + image->create(vertices.size(), 1, false, Image::Format::FORMAT_RGBF); + + for (int i = 0; i < vertices.size(); i++) { + Vector3 v = vertices[i]; + image->set_pixel(i, 0, Color(v.x, v.y, v.z)); + } + if (position_texture->get_width() != vertices.size()) { + position_texture->create_from_image(image); + } else { + position_texture->update(image); + } + } + + // normals + { + Ref image; + image.instantiate(); + image->create(normals.size(), 1, false, Image::Format::FORMAT_RGBF); + + for (int i = 0; i < normals.size(); i++) { + Vector3 v = normals[i]; + image->set_pixel(i, 0, Color(v.x, v.y, v.z)); + } + if (normal_texture->get_width() != normals.size()) { + normal_texture->create_from_image(image); + } else { + normal_texture->update(image); + } + } +} + +void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref p_mesh) { + if (mesh == p_mesh) { + return; + } + + if (p_mesh.is_valid()) { + max_surface_index = p_mesh->get_surface_count(); + } else { + max_surface_index = 0; + } + + if (mesh.is_valid()) { + Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture); + + if (mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) { + mesh->disconnect(CoreStringNames::get_singleton()->changed, callable); + } + } + + mesh = p_mesh; + + if (mesh.is_valid()) { + Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture); + + if (!mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) { + mesh->connect(CoreStringNames::get_singleton()->changed, callable); + } + } + + emit_changed(); +} + +Ref VisualShaderNodeParticleMeshEmitter::get_mesh() const { + return mesh; +} + +void VisualShaderNodeParticleMeshEmitter::set_use_all_surfaces(bool p_enabled) { + if (use_all_surfaces == p_enabled) { + return; + } + use_all_surfaces = p_enabled; + emit_changed(); +} + +bool VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces() const { + return use_all_surfaces; +} + +void VisualShaderNodeParticleMeshEmitter::set_surface_index(int p_surface_index) { + if (p_surface_index == surface_index || p_surface_index < 0 || p_surface_index >= max_surface_index) { + return; + } + surface_index = p_surface_index; + emit_changed(); +} + +int VisualShaderNodeParticleMeshEmitter::get_surface_index() const { + return surface_index; +} + +Vector VisualShaderNodeParticleMeshEmitter::get_editable_properties() const { + Vector props = VisualShaderNodeParticleEmitter::get_editable_properties(); + + props.push_back("mesh"); + props.push_back("use_all_surfaces"); + if (!use_all_surfaces) { + props.push_back("surface_index"); + } + + return props; +} + +Map VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const { + Map names = VisualShaderNodeParticleEmitter::get_editable_properties_names(); + + names.insert("mesh", TTR("Mesh")); + names.insert("use_all_surfaces", TTR("Use All Surfaces")); + if (!use_all_surfaces) { + names.insert("surface_index", TTR("Surface Index")); + } + + return names; +} + +void VisualShaderNodeParticleMeshEmitter::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &VisualShaderNodeParticleMeshEmitter::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &VisualShaderNodeParticleMeshEmitter::get_mesh); + ClassDB::bind_method(D_METHOD("set_use_all_surfaces", "enabled"), &VisualShaderNodeParticleMeshEmitter::set_use_all_surfaces); + ClassDB::bind_method(D_METHOD("is_use_all_surfaces"), &VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces); + ClassDB::bind_method(D_METHOD("set_surface_index", "surface_index"), &VisualShaderNodeParticleMeshEmitter::set_surface_index); + ClassDB::bind_method(D_METHOD("get_surface_index"), &VisualShaderNodeParticleMeshEmitter::get_surface_index); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_all_surfaces"), "set_use_all_surfaces", "is_use_all_surfaces"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "surface_index"), "set_surface_index", "get_surface_index"); +} + +VisualShaderNodeParticleMeshEmitter::VisualShaderNodeParticleMeshEmitter() { + connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &VisualShaderNodeParticleMeshEmitter::update_texture)); + + position_texture.instantiate(); + normal_texture.instantiate(); +} + // VisualShaderNodeParticleMultiplyByAxisAngle void VisualShaderNodeParticleMultiplyByAxisAngle::_bind_methods() { @@ -334,7 +613,6 @@ bool VisualShaderNodeParticleMultiplyByAxisAngle::is_degrees_mode() const { Vector VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_properties() const { Vector props; props.push_back("degrees_mode"); - props.push_back("axis_amount"); return props; } diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index 0b1fa277de..0d0f21e4bf 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -52,7 +52,7 @@ public: bool is_mode_2d() const; Vector get_editable_properties() const override; - Map get_editable_properties_names() const override; + virtual Map get_editable_properties_names() const override; bool is_show_prop_names() const override; VisualShaderNodeParticleEmitter(); @@ -106,6 +106,51 @@ public: VisualShaderNodeParticleRingEmitter(); }; +class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitter { + GDCLASS(VisualShaderNodeParticleMeshEmitter, VisualShaderNodeParticleEmitter); + Ref mesh; + bool use_all_surfaces = true; + int surface_index = 0; + int max_surface_index = 0; + + Ref position_texture; + Ref normal_texture; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; + + void update_texture(); + + void set_mesh(Ref p_mesh); + Ref get_mesh() const; + + void set_use_all_surfaces(bool p_enabled); + bool is_use_all_surfaces() const; + + void set_surface_index(int p_surface_index); + int get_surface_index() const; + + Vector get_editable_properties() const override; + Map get_editable_properties_names() const override; + Vector get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override; + + VisualShaderNodeParticleMeshEmitter(); +}; + class VisualShaderNodeParticleMultiplyByAxisAngle : public VisualShaderNode { GDCLASS(VisualShaderNodeParticleMultiplyByAxisAngle, VisualShaderNode); bool degrees_mode = true; -- cgit v1.2.3 From 31644fe2809beb9ae98472c7e5da70a130894f39 Mon Sep 17 00:00:00 2001 From: kobewi Date: Mon, 8 Nov 2021 02:28:55 +0100 Subject: Properly handle scenes in Find in Files --- editor/plugins/script_editor_plugin.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index aad378ecec..5485fcf6bc 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3434,6 +3434,9 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb shader_editor->make_visible(true); shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end); return; + } else if (fpath.get_extension() == "tscn") { + editor->load_scene(fpath); + return; } else { Ref