Merge pull request #119408 from smix8/canvas_mesh_dragndrop

Add CanvasItemEditor drag and drop for Mesh resources
This commit is contained in:
Thaddeus Crews
2026-06-22 08:29:48 -05:00
2 changed files with 70 additions and 0 deletions
@@ -58,6 +58,7 @@
#include "editor/translations/editor_translation_preview_button.h"
#include "editor/translations/editor_translation_preview_menu.h"
#include "scene/2d/audio_stream_player_2d.h"
#include "scene/2d/mesh_instance_2d.h"
#include "scene/2d/physics/touch_screen_button.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/skeleton_2d.h"
@@ -6243,6 +6244,14 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
add_preview = true;
}
Ref<Mesh> mesh = res;
if (mesh.is_valid()) {
MeshInstance2D *mesh_instance = memnew(MeshInstance2D);
mesh_instance->set_mesh(mesh);
preview_node->add_child(mesh_instance);
add_preview = true;
}
Ref<AudioStream> audio = res;
if (audio.is_valid()) {
Sprite2D *sprite = memnew(Sprite2D);
@@ -6377,6 +6386,56 @@ void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &
undo_redo->add_do_property(child, "stream", ResourceCache::get_ref(p_path));
}
void CanvasItemEditorViewport::_create_mesh_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Ref<Mesh> mesh = ResourceCache::get_ref(p_path);
MeshInstance2D *child = memnew(MeshInstance2D);
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
if (!node_name.is_empty()) {
child->set_name(node_name);
}
if (p_parent) {
undo_redo->add_do_method(p_parent, "add_child", child, true);
undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(child);
undo_redo->add_undo_method(p_parent, "remove_child", child);
} else { // If no parent is selected, set as root node of the scene.
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
undo_redo->add_do_reference(child);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
}
if (p_parent) {
String new_name = p_parent->validate_child_name(child);
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), child->get_class(), new_name);
undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
}
undo_redo->add_do_property(child, "mesh", mesh);
// Compute the global position
Transform2D xform = canvas_item_editor->get_canvas_transform();
Point2 target_position = xform.affine_inverse().xform(p_point);
// There's nothing to be used as source position, so snapping will work as absolute if enabled.
target_position = canvas_item_editor->snap_point(target_position);
CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent);
Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
undo_redo->add_do_method(child, "set_position", local_target_pos);
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
undo_redo->add_do_method(editor_selection, "add_node", child);
}
bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
Ref<PackedScene> sdata = ResourceLoader::load(p_path);
if (sdata.is_null()) { // invalid scene
@@ -6490,6 +6549,11 @@ void CanvasItemEditorViewport::_perform_drop_data() {
if (audio.is_valid()) {
_create_audio_node(target_node, path, drop_pos);
}
Ref<Mesh> mesh = res;
if (mesh.is_valid()) {
_create_mesh_node(target_node, path, drop_pos);
}
}
undo_redo->commit_action();
@@ -6533,6 +6597,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
SCENE = 1 << 0,
TEXTURE = 1 << 1,
AUDIO = 1 << 2,
MESH = 1 << 3,
};
int instantiate_type = 0;
@@ -6558,6 +6623,8 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
instantiate_type |= TEXTURE;
} else if (ClassDB::is_parent_class(res_type, SNAME("AudioStream"))) {
instantiate_type |= AUDIO;
} else if (ClassDB::is_parent_class(res_type, "Mesh")) {
instantiate_type |= MESH;
}
}
@@ -6616,6 +6683,8 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
} else if (instantiate_type & TEXTURE) {
// TRANSLATORS: The placeholders are the types of nodes being instantiated.
title = vformat(TTR("Dropping a Texture file as a %s node..."), default_texture_node_type);
} else if (instantiate_type & MESH) {
title = TTR("Dropping a Mesh file...");
}
if (instantiate_type & TEXTURE) {
desc += "\n" + TTR("[b]Hold Alt + Shift:[/b] Add Texture as a different node type.");
+1
View File
@@ -689,6 +689,7 @@ class CanvasItemEditorViewport : public Control {
void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point);
void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
void _create_mesh_node(Node *p_parent, const String &p_path, const Point2 &p_point);
void _perform_drop_data();
void _show_texture_node_type_selector();
void _update_theme();