Преглед на файлове

Merge pull request #10907 from SaracenOne/spatial_edit_drag

Added support for drag and drop in spatial editor.
Rémi Verschelde преди 8 години
родител
ревизия
7f262e484a

+ 24 - 17
editor/plugins/canvas_item_editor_plugin.cpp

@@ -3608,29 +3608,31 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
 				Sprite *sprite = memnew(Sprite);
 				sprite->set_texture(texture);
 				sprite->set_modulate(Color(1, 1, 1, 0.7f));
-				preview->add_child(sprite);
+				preview_node->add_child(sprite);
 				label->show();
 				label_desc->show();
 			} else {
 				if (scene.is_valid()) {
 					Node *instance = scene->instance();
 					if (instance) {
-						preview->add_child(instance);
+						preview_node->add_child(instance);
 					}
 				}
 			}
-			editor->get_scene_root()->add_child(preview);
+			editor->get_scene_root()->add_child(preview_node);
 		}
 	}
 }
 
 void CanvasItemEditorViewport::_remove_preview() {
-	if (preview->get_parent()) {
-		editor->get_scene_root()->remove_child(preview);
-		for (int i = preview->get_child_count() - 1; i >= 0; i--) {
-			Node *node = preview->get_child(i);
-			memdelete(node);
+	if (preview_node->get_parent()) {
+		for (int i = preview_node->get_child_count() - 1; i >= 0; i--) {
+			Node *node = preview_node->get_child(i);
+			node->queue_delete();
+			preview_node->remove_child(node);
 		}
+		editor->get_scene_root()->remove_child(preview_node);
+
 		label->hide();
 		label_desc->hide();
 	}
@@ -3840,11 +3842,11 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
 				break;
 			}
 			if (can_instance) {
-				if (!preview->get_parent()) { // create preview only once
+				if (!preview_node->get_parent()) { // create preview only once
 					_create_preview(files);
 				}
 				Transform2D trans = canvas->get_canvas_transform();
-				preview->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
+				preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x);
 				label->set_text(vformat(TTR("Adding %s..."), default_type));
 			}
 			return can_instance;
@@ -3866,11 +3868,16 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
 
 	List<Node *> list = editor->get_editor_selection()->get_selected_node_list();
 	if (list.size() == 0) {
-		accept->get_ok()->set_text(TTR("OK :("));
-		accept->set_text(TTR("No parent to instance a child at."));
-		accept->popup_centered_minsize();
-		_remove_preview();
-		return;
+		Node *root_node = editor->get_edited_scene();
+		if (root_node) {
+			list.push_back(root_node);
+		} else {
+			accept->get_ok()->set_text(TTR("OK :("));
+			accept->set_text(TTR("No parent to instance a child at."));
+			accept->popup_centered_minsize();
+			_remove_preview();
+			return;
+		}
 	}
 	if (list.size() != 1) {
 		accept->get_ok()->set_text(TTR("I see.."));
@@ -3937,7 +3944,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
 	editor = p_node;
 	editor_data = editor->get_scene_tree_dock()->get_editor_data();
 	canvas = p_canvas;
-	preview = memnew(Node2D);
+	preview_node = memnew(Node2D);
 	accept = memnew(AcceptDialog);
 	editor->get_gui_base()->add_child(accept);
 
@@ -3991,5 +3998,5 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
 }
 
 CanvasItemEditorViewport::~CanvasItemEditorViewport() {
-	memdelete(preview);
+	memdelete(preview_node);
 }

+ 1 - 1
editor/plugins/canvas_item_editor_plugin.h

@@ -460,7 +460,7 @@ class CanvasItemEditorViewport : public Control {
 	EditorNode *editor;
 	EditorData *editor_data;
 	CanvasItemEditor *canvas;
-	Node2D *preview;
+	Node2D *preview_node;
 	AcceptDialog *accept;
 	WindowDialog *selector;
 	Label *selector_label;

+ 315 - 2
editor/plugins/spatial_editor_plugin.cpp

@@ -35,12 +35,15 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
+#include "editor/plugins/script_editor_plugin.h"
+#include "editor/script_editor_debugger.h"
 #include "editor/spatial_editor_gizmos.h"
 #include "os/keyboard.h"
 #include "print_string.h"
 #include "project_settings.h"
 #include "scene/3d/camera.h"
 #include "scene/3d/visual_instance.h"
+#include "scene/resources/packed_scene.h"
 #include "scene/resources/surface_tool.h"
 #include "sort.h"
 
@@ -228,7 +231,7 @@ Vector3 SpatialEditorViewport::_get_camera_normal() const {
 	return -_get_camera_transform().basis.get_axis(2);
 }
 
-Vector3 SpatialEditorViewport::_get_ray(const Vector2 &p_pos) {
+Vector3 SpatialEditorViewport::_get_ray(const Vector2 &p_pos) const {
 
 	return camera->project_ray_normal(p_pos);
 }
@@ -700,6 +703,11 @@ void SpatialEditorViewport::_smouseenter() {
 		surface->grab_focus();
 }
 
+void SpatialEditorViewport::_smouseexit() {
+
+	_remove_preview();
+}
+
 void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
 
 	_find_items_at_pos(b->get_position(), clicked_includes_current, selection_results, b->get_shift());
@@ -1895,6 +1903,7 @@ void SpatialEditorViewport::_notification(int p_what) {
 		surface->connect("draw", this, "_draw");
 		surface->connect("gui_input", this, "_sinput");
 		surface->connect("mouse_entered", this, "_smouseenter");
+		surface->connect("mouse_exited", this, "_smouseexit");
 		info->add_style_override("panel", get_stylebox("panel", "Panel"));
 		preview_camera->set_icon(get_icon("Camera", "EditorIcons"));
 		_init_gizmo_instance(index);
@@ -2425,6 +2434,7 @@ void SpatialEditorViewport::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("_draw"), &SpatialEditorViewport::_draw);
 	ClassDB::bind_method(D_METHOD("_smouseenter"), &SpatialEditorViewport::_smouseenter);
+	ClassDB::bind_method(D_METHOD("_smouseexit"), &SpatialEditorViewport::_smouseexit);
 	ClassDB::bind_method(D_METHOD("_sinput"), &SpatialEditorViewport::_sinput);
 	ClassDB::bind_method(D_METHOD("_menu_option"), &SpatialEditorViewport::_menu_option);
 	ClassDB::bind_method(D_METHOD("_toggle_camera_preview"), &SpatialEditorViewport::_toggle_camera_preview);
@@ -2432,6 +2442,8 @@ void SpatialEditorViewport::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("update_transform_gizmo_view"), &SpatialEditorViewport::update_transform_gizmo_view);
 	ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &SpatialEditorViewport::_selection_result_pressed);
 	ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &SpatialEditorViewport::_selection_menu_hide);
+	ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpatialEditorViewport::can_drop_data_fw);
+	ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpatialEditorViewport::drop_data_fw);
 
 	ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")));
 }
@@ -2481,6 +2493,293 @@ void SpatialEditorViewport::focus_selection() {
 	cursor.pos = center;
 }
 
+void SpatialEditorViewport::assign_pending_data_pointers(Spatial *p_preview_node, Rect3 *p_preview_bounds, AcceptDialog *p_accept) {
+	preview_node = p_preview_node;
+	preview_bounds = p_preview_bounds;
+	accept = p_accept;
+}
+
+Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const {
+	const float MAX_DISTANCE = 10;
+
+	Vector3 world_ray = _get_ray(p_pos);
+	Vector3 world_pos = _get_ray_pos(p_pos);
+
+	Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(world_pos, world_ray, get_tree()->get_root()->get_world()->get_scenario());
+	Set<Ref<SpatialEditorGizmo> > found_gizmos;
+
+	float closest_dist = MAX_DISTANCE;
+
+	Vector3 point = world_pos + world_ray * MAX_DISTANCE;
+	Vector3 normal = Vector3(0.0, 0.0, 0.0);
+
+	for (int i = 0; i < instances.size(); i++) {
+
+		MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(ObjectDB::get_instance(instances[i]));
+
+		if (!mesh_instance)
+			continue;
+
+		Ref<SpatialEditorGizmo> seg = mesh_instance->get_gizmo();
+
+		if ((!seg.is_valid()) || found_gizmos.has(seg)) {
+			continue;
+		}
+
+		found_gizmos.insert(seg);
+
+		int handle = -1;
+		Vector3 hit_point;
+		Vector3 hit_normal;
+		bool inters = seg->intersect_ray(camera, p_pos, hit_point, hit_normal, NULL, false);
+
+		if (!inters)
+			continue;
+
+		float dist = world_pos.distance_to(hit_point);
+
+		if (dist < 0)
+			continue;
+
+		if (dist < closest_dist) {
+			closest_dist = dist;
+			point = hit_point;
+			normal = hit_normal;
+		}
+	}
+	Vector3 center = preview_bounds->get_size() * 0.5;
+	return point + (center * normal);
+}
+
+Rect3 SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, const Rect3 p_bounds) {
+	Rect3 bounds = p_bounds;
+	for (int i = 0; i < p_parent->get_child_count(); i++) {
+		Spatial *child = Object::cast_to<Spatial>(p_parent->get_child(i));
+		if (child) {
+			MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(child);
+			if (mesh_instance) {
+				Rect3 mesh_instance_bounds = mesh_instance->get_aabb();
+				mesh_instance_bounds.position += mesh_instance->get_global_transform().origin - p_parent->get_global_transform().origin;
+				bounds.merge_with(mesh_instance_bounds);
+			}
+			bounds = _calculate_spatial_bounds(child, bounds);
+		}
+	}
+	return bounds;
+}
+
+void SpatialEditorViewport::_create_preview(const Vector<String> &files) const {
+	for (int i = 0; i < files.size(); i++) {
+		String path = files[i];
+		RES res = ResourceLoader::load(path);
+		Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
+		if (scene != NULL) {
+			if (scene.is_valid()) {
+				Node *instance = scene->instance();
+				if (instance) {
+					preview_node->add_child(instance);
+				}
+			}
+			editor->get_scene_root()->add_child(preview_node);
+		}
+	}
+	*preview_bounds = _calculate_spatial_bounds(preview_node, Rect3());
+}
+
+void SpatialEditorViewport::_remove_preview() {
+	if (preview_node->get_parent()) {
+		for (int i = preview_node->get_child_count() - 1; i >= 0; i--) {
+			Node *node = preview_node->get_child(i);
+			node->queue_delete();
+			preview_node->remove_child(node);
+		}
+		editor->get_scene_root()->remove_child(preview_node);
+	}
+}
+
+bool SpatialEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) {
+	if (p_desired_node->get_filename() == p_target_scene_path) {
+		return true;
+	}
+
+	int childCount = p_desired_node->get_child_count();
+	for (int i = 0; i < childCount; i++) {
+		Node *child = p_desired_node->get_child(i);
+		if (_cyclical_dependency_exists(p_target_scene_path, child)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
+	Ref<PackedScene> sdata = ResourceLoader::load(path);
+	if (!sdata.is_valid()) { // invalid scene
+		return false;
+	}
+
+	Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
+	if (!instanced_scene) { // error on instancing
+		return false;
+	}
+
+	if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing
+		if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) {
+			memdelete(instanced_scene);
+			return false;
+		}
+	}
+
+	instanced_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path));
+
+	editor_data->get_undo_redo().add_do_method(parent, "add_child", instanced_scene);
+	editor_data->get_undo_redo().add_do_method(instanced_scene, "set_owner", editor->get_edited_scene());
+	editor_data->get_undo_redo().add_do_reference(instanced_scene);
+	editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
+
+	String new_name = parent->validate_child_name(instanced_scene);
+	ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+	editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
+	editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+
+	Transform global_transform;
+	Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
+	if (parent_spatial)
+		global_transform = parent_spatial->get_global_transform();
+
+	global_transform.origin = _get_instance_position(p_point);
+
+	editor_data->get_undo_redo().add_do_method(instanced_scene, "set_global_transform", global_transform);
+
+	return true;
+}
+
+void SpatialEditorViewport::_perform_drop_data() {
+	_remove_preview();
+
+	Vector<String> error_files;
+
+	editor_data->get_undo_redo().create_action(TTR("Create Node"));
+
+	for (int i = 0; i < selected_files.size(); i++) {
+		String path = selected_files[i];
+		RES res = ResourceLoader::load(path);
+		if (res.is_null()) {
+			continue;
+		}
+		Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
+		if (scene != NULL) {
+			bool success = _create_instance(target_node, path, drop_pos);
+			if (!success) {
+				error_files.push_back(path);
+			}
+		}
+	}
+
+	editor_data->get_undo_redo().commit_action();
+
+	if (error_files.size() > 0) {
+		String files_str;
+		for (int i = 0; i < error_files.size(); i++) {
+			files_str += error_files[i].get_file().get_basename() + ",";
+		}
+		files_str = files_str.substr(0, files_str.length() - 1);
+		accept->get_ok()->set_text(TTR("Ugh"));
+		accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str()));
+		accept->popup_centered_minsize();
+	}
+}
+
+bool SpatialEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+
+	bool can_instance = false;
+
+	if (!preview_node->is_inside_tree()) {
+		Dictionary d = p_data;
+		if (d.has("type") && (String(d["type"]) == "files")) {
+			Vector<String> files = d["files"];
+
+			List<String> scene_extensions;
+			ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
+
+			for (int i = 0; i < files.size(); i++) {
+				if (scene_extensions.find(files[i].get_extension())) {
+					RES res = ResourceLoader::load(files[i]);
+					if (res.is_null()) {
+						continue;
+					}
+
+					String type = res->get_class();
+					if (type == "PackedScene") {
+						Ref<PackedScene> sdata = ResourceLoader::load(files[i]);
+						Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
+						if (!instanced_scene) {
+							continue;
+						}
+						memdelete(instanced_scene);
+					}
+					can_instance = true;
+					break;
+				}
+			}
+			if (can_instance) {
+				_create_preview(files);
+			}
+		}
+	} else {
+		can_instance = true;
+	}
+
+	if (can_instance) {
+		Transform global_transform = Transform(Basis(), _get_instance_position(p_point));
+		preview_node->set_global_transform(global_transform);
+	}
+
+	return can_instance;
+}
+
+void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+	if (!can_drop_data_fw(p_point, p_data, p_from))
+		return;
+
+	bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+
+	selected_files.clear();
+	Dictionary d = p_data;
+	if (d.has("type") && String(d["type"]) == "files") {
+		selected_files = d["files"];
+	}
+
+	List<Node *> list = editor->get_editor_selection()->get_selected_node_list();
+	if (list.size() == 0) {
+		Node *root_node = editor->get_edited_scene();
+		if (root_node) {
+			list.push_back(root_node);
+		} else {
+			accept->get_ok()->set_text(TTR("OK :("));
+			accept->set_text(TTR("No parent to instance a child at."));
+			accept->popup_centered_minsize();
+			_remove_preview();
+			return;
+		}
+	}
+	if (list.size() != 1) {
+		accept->get_ok()->set_text(TTR("I see.."));
+		accept->set_text(TTR("This operation requires a single selected node."));
+		accept->popup_centered_minsize();
+		_remove_preview();
+		return;
+	}
+
+	target_node = list[0];
+	if (is_shift && target_node != editor->get_edited_scene()) {
+		target_node = target_node->get_parent();
+	}
+	drop_pos = p_point;
+
+	_perform_drop_data();
+}
+
 SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) {
 
 	_edit.mode = TRANSFORM_NONE;
@@ -2491,6 +2790,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 
 	index = p_index;
 	editor = p_editor;
+	editor_data = editor->get_scene_tree_dock()->get_editor_data();
 	editor_selection = editor->get_editor_selection();
 	undo_redo = editor->get_undo_redo();
 	clicked = 0;
@@ -2509,6 +2809,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 
 	c->add_child(viewport);
 	surface = memnew(Control);
+	surface->set_drag_forwarding(this);
 	add_child(surface);
 	surface->set_area_as_parent_rect();
 	surface->set_clip_contents(true);
@@ -2573,9 +2874,10 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	preview_camera->hide();
 	preview_camera->connect("toggled", this, "_toggle_camera_preview");
 	previewing = NULL;
-	preview = NULL;
 	gizmo_scale = 1.0;
 
+	preview_node = NULL;
+
 	info = memnew(PanelContainer);
 	info->set_self_modulate(Color(1, 1, 1, 0.4));
 	surface->add_child(info);
@@ -2583,6 +2885,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	info->add_child(info_label);
 	info->hide();
 
+	accept = NULL;
+
 	freelook_active = false;
 
 	selection_menu = memnew(PopupMenu);
@@ -4000,6 +4304,10 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 	vs = memnew(VSeparator);
 	hbc_menu->add_child(vs);
 
+	// Drag and drop support;
+	preview_node = memnew(Spatial);
+	preview_bounds = Rect3();
+
 	ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT + KEY_KP_7);
 	ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7);
 	ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KEY_MASK_ALT + KEY_KP_1);
@@ -4044,6 +4352,9 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 
 	p = view_menu->get_popup();
 
+	accept = memnew(AcceptDialog);
+	editor->get_gui_base()->add_child(accept);
+
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
 	p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
@@ -4078,6 +4389,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 
 		viewports[i] = memnew(SpatialEditorViewport(this, editor, i));
 		viewports[i]->connect("toggle_maximize_view", this, "_toggle_maximize_view");
+		viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept);
 		viewport_base->add_child(viewports[i]);
 	}
 	//vbc->add_child(viewport_base);
@@ -4212,6 +4524,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
 }
 
 SpatialEditor::~SpatialEditor() {
+	memdelete(preview_node);
 }
 
 void SpatialEditorPlugin::make_visible(bool p_visible) {

+ 34 - 1
editor/plugins/spatial_editor_plugin.h

@@ -106,7 +106,16 @@ private:
 	void _menu_option(int p_option);
 	Size2 prev_size;
 
+	Spatial *preview_node;
+	Rect3 *preview_bounds;
+	Vector<String> selected_files;
+	AcceptDialog *accept;
+
+	Node *target_node;
+	Point2 drop_pos;
+
 	EditorNode *editor;
+	EditorData *editor_data;
 	EditorSelection *editor_selection;
 	UndoRedo *undo_redo;
 
@@ -143,7 +152,7 @@ private:
 	ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = NULL, bool p_alt_select = false);
 	void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false);
 	Vector3 _get_ray_pos(const Vector2 &p_pos) const;
-	Vector3 _get_ray(const Vector2 &p_pos);
+	Vector3 _get_ray(const Vector2 &p_pos) const;
 	Point2 _point_to_screen(const Vector3 &p_point);
 	Transform _get_camera_transform() const;
 	int get_selected_count() const;
@@ -250,6 +259,7 @@ private:
 	void _draw();
 
 	void _smouseenter();
+	void _smouseexit();
 	void _sinput(const Ref<InputEvent> &p_event);
 	void _update_freelook(real_t delta);
 	SpatialEditor *spatial_editor;
@@ -266,6 +276,17 @@ private:
 	void _list_select(Ref<InputEventMouseButton> b);
 	Point2i _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const;
 
+	Vector3 _get_instance_position(const Point2 &p_pos) const;
+	static Rect3 _calculate_spatial_bounds(const Spatial *p_parent, const Rect3 p_bounds);
+	void _create_preview(const Vector<String> &files) const;
+	void _remove_preview();
+	bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node);
+	bool _create_instance(Node *parent, String &path, const Point2 &p_point);
+	void _perform_drop_data();
+
+	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+
 protected:
 	void _notification(int p_what);
 	static void _bind_methods();
@@ -281,6 +302,11 @@ public:
 
 	void focus_selection();
 
+	void assign_pending_data_pointers(
+			Spatial *p_preview_node,
+			Rect3 *p_preview_bounds,
+			AcceptDialog *p_accept);
+
 	Viewport *get_viewport_node() { return viewport; }
 
 	SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index);
@@ -396,6 +422,10 @@ private:
 	Ref<SpatialMaterial> indicator_mat;
 	Ref<SpatialMaterial> cursor_material;
 
+	// Scene drag and drop support
+	Spatial *preview_node;
+	Rect3 preview_bounds;
+
 	/*
 	struct Selected {
 		AABB aabb;
@@ -442,6 +472,8 @@ private:
 	MenuButton *transform_menu;
 	MenuButton *view_menu;
 
+	AcceptDialog *accept;
+
 	ConfirmationDialog *snap_dialog;
 	ConfirmationDialog *xform_dialog;
 	ConfirmationDialog *settings_dialog;
@@ -559,6 +591,7 @@ public:
 	Camera *get_camera() { return NULL; }
 	void edit(Spatial *p_spatial);
 	void clear();
+
 	SpatialEditor(EditorNode *p_editor);
 	~SpatialEditor();
 };