summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/create_dialog.cpp119
-rw-r--r--editor/editor_data.cpp35
-rw-r--r--editor/editor_data.h3
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp199
-rw-r--r--editor/plugins/spatial_editor_plugin.h14
-rw-r--r--editor/scene_tree_dock.cpp53
6 files changed, 334 insertions, 89 deletions
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 6b2a072e20..3e0c1f2d53 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -40,6 +40,11 @@
void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) {
+ type_list.clear();
+ ClassDB::get_class_list(&type_list);
+ ScriptServer::get_global_class_list(&type_list);
+ type_list.sort_custom<StringName::AlphCompare>();
+
recent->clear();
FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::READ);
@@ -173,10 +178,28 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
if (p_types.has(p_type))
return;
- if (!ClassDB::is_parent_class(p_type, base_type) || p_type == base_type)
+
+ bool cpp_type = ClassDB::class_exists(p_type);
+ EditorData &ed = EditorNode::get_singleton()->get_editor_data();
+
+ if (p_type == base_type)
return;
- String inherits = ClassDB::get_parent_class(p_type);
+ if (cpp_type) {
+ if (!ClassDB::is_parent_class(p_type, base_type))
+ return;
+ } else {
+ if (!ScriptServer::is_global_class(p_type) || !ed.script_class_is_parent(p_type, base_type))
+ return;
+
+ String script_path = ScriptServer::get_global_class_path(p_type);
+ if (script_path.find("res://addons/", 0) != -1) {
+ if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3)))
+ return;
+ }
+ }
+
+ String inherits = cpp_type ? ClassDB::get_parent_class(p_type) : ed.script_class_get_base(p_type);
TreeItem *parent = p_root;
@@ -189,17 +212,32 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
if (p_types.has(inherits))
parent = p_types[inherits];
+ else if (ScriptServer::is_global_class(inherits))
+ return;
}
+ bool can_instance = (cpp_type && ClassDB::can_instance(p_type)) || ScriptServer::is_global_class(p_type);
+
TreeItem *item = search_options->create_item(parent);
- item->set_text(0, p_type);
- if (!ClassDB::can_instance(p_type)) {
+ if (cpp_type) {
+ item->set_text(0, p_type);
+ } else {
+ item->set_metadata(0, p_type);
+ item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")");
+ }
+ if (!can_instance) {
item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
item->set_selectable(0, false);
} else {
bool is_search_subsequence = search_box->get_text().is_subsequence_ofi(p_type);
String to_select_type = *to_select ? (*to_select)->get_text(0) : "";
- bool current_item_is_preffered = ClassDB::is_parent_class(p_type, preferred_search_result_type) && !ClassDB::is_parent_class(to_select_type, preferred_search_result_type);
+ to_select_type = to_select_type.split(" ")[0];
+ bool current_item_is_preffered;
+ if (cpp_type) {
+ current_item_is_preffered = ClassDB::is_parent_class(p_type, preferred_search_result_type) && !ClassDB::is_parent_class(to_select_type, preferred_search_result_type);
+ } else {
+ current_item_is_preffered = ed.script_class_is_parent(p_type, preferred_search_result_type) && !ed.script_class_is_parent(to_select_type, preferred_search_result_type);
+ }
if (*to_select && p_type.length() < (*to_select)->get_text(0).length()) {
current_item_is_preffered = true;
}
@@ -217,16 +255,19 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
// don't collapse the root node
collapse &= (item != p_root);
// don't collapse abstract nodes on the first tree level
- collapse &= ((parent != p_root) || (ClassDB::can_instance(p_type)));
+ collapse &= ((parent != p_root) || (can_instance));
item->set_collapsed(collapse);
}
const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description;
item->set_tooltip(0, description);
- if (has_icon(p_type, "EditorIcons")) {
+ if (cpp_type && has_icon(p_type, "EditorIcons")) {
item->set_icon(0, get_icon(p_type, "EditorIcons"));
+ } else if (!cpp_type && has_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons")) {
+
+ item->set_icon(0, get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons"));
}
p_types[p_type] = item;
@@ -243,47 +284,38 @@ void CreateDialog::_update_search() {
_parse_fs(EditorFileSystem::get_singleton()->get_filesystem());
*/
- List<StringName> global_classes;
- ScriptServer::get_global_class_list(&global_classes);
-
- Map<String, List<String> > global_class_map;
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- String base = ScriptServer::get_global_class_base(E->get());
- if (!global_class_map.has(base)) {
- global_class_map[base] = List<String>();
- }
- global_class_map[base].push_back(E->get());
- }
-
HashMap<String, TreeItem *> types;
TreeItem *root = search_options->create_item();
+ EditorData &ed = EditorNode::get_singleton()->get_editor_data();
root->set_text(0, base_type);
if (has_icon(base_type, "EditorIcons")) {
root->set_icon(0, get_icon(base_type, "EditorIcons"));
}
- List<StringName>::Element *I = type_list.front();
TreeItem *to_select = search_box->get_text() == base_type ? root : NULL;
- for (; I; I = I->next()) {
+ for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) {
String type = I->get();
+ bool cpp_type = ClassDB::class_exists(type);
if (base_type == "Node" && type.begins_with("Editor"))
continue; // do not show editor nodes
- if (!ClassDB::can_instance(type))
+ if (cpp_type && !ClassDB::can_instance(type))
continue; // can't create what can't be instanced
bool skip = false;
- for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) {
- if (ClassDB::is_parent_class(type, E->get()))
- skip = true;
+ if (cpp_type) {
+ for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) {
+ if (ClassDB::is_parent_class(type, E->get()))
+ skip = true;
+ }
+ if (skip)
+ continue;
}
- if (skip)
- continue;
if (search_box->get_text() == "") {
add_type(type, types, root, &to_select);
@@ -291,7 +323,7 @@ void CreateDialog::_update_search() {
bool found = false;
String type = I->get();
- while (type != "" && ClassDB::is_parent_class(type, base_type) && type != base_type) {
+ while (type != "" && (cpp_type ? ClassDB::is_parent_class(type, base_type) : ed.script_class_is_parent(type, base_type)) && type != base_type) {
if (search_box->get_text().is_subsequence_ofi(type)) {
found = true;
@@ -305,32 +337,6 @@ void CreateDialog::_update_search() {
add_type(I->get(), types, root, &to_select);
}
- if (global_class_map.has(type) && ClassDB::is_parent_class(type, base_type)) {
- for (List<String>::Element *J = global_class_map[type].front(); J; J = J->next()) {
- bool show = search_box->get_text().is_subsequence_ofi(J->get());
-
- if (!show)
- continue;
-
- if (!types.has(type))
- add_type(type, types, root, &to_select);
-
- TreeItem *ti;
- if (types.has(type))
- ti = types[type];
- else
- ti = search_options->get_root();
-
- TreeItem *item = search_options->create_item(ti);
- item->set_metadata(0, J->get());
- item->set_text(0, J->get() + " (" + ScriptServer::get_global_class_path(J->get()).get_file() + ")");
- item->set_icon(0, _get_editor_icon(type));
- if (!to_select || J->get() == search_box->get_text()) {
- to_select = item;
- }
- }
- }
-
if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) {
//there are custom types based on this... cool.
@@ -694,9 +700,6 @@ CreateDialog::CreateDialog() {
is_replace_mode = false;
- ClassDB::get_class_list(&type_list);
- type_list.sort_custom<StringName::AlphCompare>();
-
set_resizable(true);
HSplitContainer *hsc = memnew(HSplitContainer);
@@ -762,4 +765,6 @@ CreateDialog::CreateDialog() {
type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here
type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix.
+
+ EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
}
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 729f6f50ef..f4ef11eb36 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -853,6 +853,41 @@ void EditorData::get_plugin_window_layout(Ref<ConfigFile> p_layout) {
}
}
+bool EditorData::script_class_is_parent(const String &p_class, const String &p_inherits) {
+ if (!ScriptServer::is_global_class(p_class))
+ return false;
+ String base = script_class_get_base(p_class);
+ while (p_inherits != base) {
+ if (ClassDB::class_exists(base)) {
+ return ClassDB::is_parent_class(base, p_inherits);
+ } else if (ScriptServer::is_global_class(base)) {
+ base = script_class_get_base(base);
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+StringName EditorData::script_class_get_base(const String &p_class) {
+
+ if (!ScriptServer::is_global_class(p_class))
+ return StringName();
+
+ String path = ScriptServer::get_global_class_path(p_class);
+
+ Ref<Script> script = ResourceLoader::load(path, "Script");
+ if (script.is_null())
+ return StringName();
+
+ Ref<Script> base_script = script->get_base_script();
+ if (base_script.is_null()) {
+ return ScriptServer::get_global_class_base(p_class);
+ }
+
+ return script->get_language()->get_global_class_name(base_script->get_path());
+}
+
EditorData::EditorData() {
current_edited_scene = -1;
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 0ecef8ae31..fac6635cd2 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -211,6 +211,9 @@ public:
void notify_edited_scene_changed();
void notify_resource_saved(const Ref<Resource> &p_resource);
+ bool script_class_is_parent(const String &p_class, const String &p_inherits);
+ StringName script_class_get_base(const String &p_class);
+
EditorData();
};
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 4b410b5ece..eab1588a55 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -2156,6 +2156,21 @@ void SpatialEditorViewport::_notification(int p_what) {
_update_freelook(delta);
+ Node *scene_root = editor->get_scene_tree_dock()->get_editor_data()->get_edited_scene_root();
+ if (previewing_cinema == true && scene_root != NULL) {
+ Camera *cam = scene_root->get_viewport()->get_camera();
+ if (cam != NULL && cam != previewing) {
+ //then switch the viewport's camera to the scene's viewport camera
+ if (previewing != NULL) {
+ previewing->disconnect("tree_exited", this, "_preview_exited_scene");
+ }
+ previewing = cam;
+ previewing->connect("tree_exited", this, "_preview_exited_scene");
+ VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), cam->get_camera());
+ surface->update();
+ }
+ }
+
_update_camera(delta);
Map<Node *, Object *> &selection = editor_selection->get_selection();
@@ -2261,6 +2276,13 @@ void SpatialEditorViewport::_notification(int p_what) {
text += TTR("FPS") + ": " + itos(temp_fps) + " (" + String::num(1000.0f / temp_fps, 2) + " ms)";
fps_label->set_text(text);
}
+
+ bool show_cinema = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW));
+ cinema_label->set_visible(show_cinema);
+ if (show_cinema) {
+ float cinema_half_width = cinema_label->get_size().width / 2.0f;
+ cinema_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -cinema_half_width);
+ }
}
if (p_what == NOTIFICATION_ENTER_TREE) {
@@ -2273,6 +2295,7 @@ void SpatialEditorViewport::_notification(int p_what) {
surface->connect("focus_exited", this, "_surface_focus_exit");
info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
fps_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
+ cinema_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
preview_camera->set_icon(get_icon("Camera", "EditorIcons"));
_init_gizmo_instance(index);
}
@@ -2599,6 +2622,22 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked(idx, current);
} break;
+ case VIEW_CINEMATIC_PREVIEW: {
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW);
+ bool current = view_menu->get_popup()->is_item_checked(idx);
+ current = !current;
+ view_menu->get_popup()->set_item_checked(idx, current);
+ previewing_cinema = true;
+ _toggle_cinema_preview(current);
+
+ if (current) {
+ preview_camera->hide();
+ } else {
+ if (previewing != NULL)
+ preview_camera->show();
+ }
+ } break;
case VIEW_GIZMOS: {
int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
@@ -2762,6 +2801,25 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
}
}
+void SpatialEditorViewport::_toggle_cinema_preview(bool p_activate) {
+ previewing_cinema = p_activate;
+ if (!previewing_cinema) {
+ if (previewing != NULL)
+ previewing->disconnect("tree_exited", this, "_preview_exited_scene");
+
+ previewing = NULL;
+ VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore
+ preview_camera->set_pressed(false);
+ if (!preview) {
+ preview_camera->hide();
+ } else {
+ preview_camera->show();
+ }
+ view_menu->show();
+ surface->update();
+ }
+}
+
void SpatialEditorViewport::_selection_result_pressed(int p_result) {
if (selection_results.size() <= p_result)
@@ -2786,7 +2844,7 @@ void SpatialEditorViewport::set_can_preview(Camera *p_preview) {
preview = p_preview;
- if (!preview_camera->is_pressed())
+ if (!preview_camera->is_pressed() && !previewing_cinema)
preview_camera->set_visible(p_preview);
}
@@ -2907,6 +2965,12 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) {
int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION);
view_menu->get_popup()->set_item_checked(idx, half_res);
}
+ if (p_state.has("cinematic_preview")) {
+ previewing_cinema = p_state["cinematic_preview"];
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW);
+ view_menu->get_popup()->set_item_checked(idx, previewing_cinema);
+ }
if (p_state.has("previewing")) {
Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]);
@@ -2945,6 +3009,7 @@ Dictionary SpatialEditorViewport::get_state() const {
d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
d["fps"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS));
d["half_res"] = viewport_container->get_stretch_shrink() > 1;
+ d["cinematic_preview"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW));
if (previewing)
d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing);
if (lock_rotation)
@@ -3425,6 +3490,9 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true);
view_menu->get_popup()->add_separator();
+ view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_cinematic_preview", TTR("Cinematic Preview")), VIEW_CINEMATIC_PREVIEW);
+
+ view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_selection_with_view"), VIEW_ALIGN_SELECTION_WITH_VIEW);
@@ -3474,6 +3542,15 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
surface->add_child(fps_label);
fps_label->hide();
+ cinema_label = memnew(Label);
+ cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
+ cinema_label->set_h_grow_direction(GROW_DIRECTION_END);
+ cinema_label->set_align(Label::ALIGN_CENTER);
+ surface->add_child(cinema_label);
+ cinema_label->set_text(TTR("Cinematic Preview"));
+ cinema_label->hide();
+ previewing_cinema = false;
+
accept = NULL;
freelook_active = false;
@@ -4270,6 +4347,9 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50));
} break;
+ case MENU_SNAP_TO_FLOOR: {
+ snap_selected_nodes_to_floor();
+ } break;
case MENU_LOCK_SELECTED: {
List<Node *> &selection = editor_selection->get_selected_node_list();
@@ -4745,6 +4825,119 @@ void SpatialEditor::_refresh_menu_icons() {
tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked);
}
+template <typename T>
+Set<T *> _get_child_nodes(Node *parent_node) {
+ Set<T *> nodes = Set<T *>();
+ T *node = Node::cast_to<T>(parent_node);
+ if (node) {
+ nodes.insert(node);
+ }
+
+ for (int i = 0; i < parent_node->get_child_count(); i++) {
+ Node *child_node = parent_node->get_child(i);
+ Set<T *> child_nodes = _get_child_nodes<T>(child_node);
+ for (typename Set<T *>::Element *I = child_nodes.front(); I; I = I->next()) {
+ nodes.insert(I->get());
+ }
+ }
+
+ return nodes;
+}
+
+Set<RID> _get_physics_bodies_rid(Node *node) {
+ Set<RID> rids = Set<RID>();
+ PhysicsBody *pb = Node::cast_to<PhysicsBody>(node);
+ if (pb) {
+ rids.insert(pb->get_rid());
+ }
+ Set<PhysicsBody *> child_nodes = _get_child_nodes<PhysicsBody>(node);
+ for (Set<PhysicsBody *>::Element *I = child_nodes.front(); I; I = I->next()) {
+ rids.insert(I->get()->get_rid());
+ }
+
+ return rids;
+}
+
+void SpatialEditor::snap_selected_nodes_to_floor() {
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+ Dictionary snap_data;
+
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ Spatial *sp = Object::cast_to<Spatial>(E->get());
+ if (sp) {
+ Vector3 from = Vector3();
+ Vector3 position_offset = Vector3();
+
+ // Priorities for snapping to floor are CollisionShapes, VisualInstances and then origin
+ Set<VisualInstance *> vi = _get_child_nodes<VisualInstance>(sp);
+ Set<CollisionShape *> cs = _get_child_nodes<CollisionShape>(sp);
+
+ if (cs.size()) {
+ AABB aabb = sp->get_global_transform().xform(cs.front()->get()->get_shape()->get_debug_mesh()->get_aabb());
+ for (Set<CollisionShape *>::Element *I = cs.front(); I; I = I->next()) {
+ aabb.merge_with(sp->get_global_transform().xform(I->get()->get_shape()->get_debug_mesh()->get_aabb()));
+ }
+ Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5);
+ from = aabb.position + size;
+ position_offset.y = from.y - sp->get_global_transform().origin.y;
+ } else if (vi.size()) {
+ AABB aabb = vi.front()->get()->get_transformed_aabb();
+ for (Set<VisualInstance *>::Element *I = vi.front(); I; I = I->next()) {
+ aabb.merge_with(I->get()->get_transformed_aabb());
+ }
+ Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5);
+ from = aabb.position + size;
+ position_offset.y = from.y - sp->get_global_transform().origin.y;
+ } else {
+ from = sp->get_global_transform().origin;
+ }
+
+ // We add a bit of margin to the from position to avoid it from snapping
+ // when the spatial is already on a floor and there's another floor under
+ // it
+ from = from + Vector3(0.0, 0.1, 0.0);
+
+ Dictionary d;
+
+ d["from"] = from;
+ d["position_offset"] = position_offset;
+ snap_data[sp] = d;
+ }
+ }
+
+ PhysicsDirectSpaceState *ss = get_tree()->get_root()->get_world()->get_direct_space_state();
+ PhysicsDirectSpaceState::RayResult result;
+
+ Array keys = snap_data.keys();
+
+ if (keys.size()) {
+ undo_redo->create_action("Snap Nodes To Floor");
+
+ for (int i = 0; i < keys.size(); i++) {
+ Node *node = keys[i];
+ Spatial *sp = Object::cast_to<Spatial>(node);
+
+ Dictionary d = snap_data[node];
+ Vector3 from = d["from"];
+ Vector3 position_offset = d["position_offset"];
+
+ Vector3 to = from - Vector3(0.0, 10.0, 0.0);
+ Set<RID> excluded = _get_physics_bodies_rid(sp);
+
+ if (ss->intersect_ray(from, to, result, excluded)) {
+ Transform new_transform = sp->get_global_transform();
+ new_transform.origin.y = result.position.y;
+ new_transform.origin = new_transform.origin - position_offset;
+
+ undo_redo->add_do_method(sp, "set_global_transform", new_transform);
+ undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+ }
+ }
+
+ undo_redo->commit_action();
+ }
+}
+
void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack())
@@ -4773,6 +4966,8 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", p_event))
_menu_item_pressed(MENU_TOOL_SCALE);
+ else if (ED_IS_SHORTCUT("spatial_editor/snap_to_floor", p_event))
+ snap_selected_nodes_to_floor();
else if (ED_IS_SHORTCUT("spatial_editor/local_coords", p_event))
if (are_local_coords_enabled()) {
@@ -5132,6 +5327,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/tool_move", TTR("Tool Move"), KEY_W);
ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Tool Rotate"), KEY_E);
ED_SHORTCUT("spatial_editor/tool_scale", TTR("Tool Scale"), KEY_R);
+ ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap To Floor"), KEY_PAGEDOWN);
ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F);
@@ -5142,6 +5338,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
hbc_menu->add_child(transform_menu);
p = transform_menu->get_popup();
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap object to floor")), MENU_SNAP_TO_FLOOR);
p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index c89db1867b..bd449a28df 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -95,7 +95,8 @@ class SpatialEditorViewport : public Control {
VIEW_DISPLAY_WIREFRAME,
VIEW_DISPLAY_OVERDRAW,
VIEW_DISPLAY_SHADELESS,
- VIEW_LOCK_ROTATION
+ VIEW_LOCK_ROTATION,
+ VIEW_CINEMATIC_PREVIEW
};
public:
@@ -109,7 +110,6 @@ private:
int index;
String name;
void _menu_option(int p_option);
-
Spatial *preview_node;
AABB *preview_bounds;
Vector<String> selected_files;
@@ -141,6 +141,7 @@ private:
Label *info_label;
Label *fps_label;
+ Label *cinema_label;
struct _RayResult {
@@ -289,8 +290,11 @@ private:
Camera *previewing;
Camera *preview;
+ bool previewing_cinema;
+
void _preview_exited_scene();
void _toggle_camera_preview(bool);
+ void _toggle_cinema_preview(bool);
void _init_gizmo_instance(int p_idx);
void _finish_gizmo_instances();
void _selection_result_pressed(int);
@@ -405,7 +409,6 @@ public:
TOOL_LOCK_SELECTED,
TOOL_UNLOCK_SELECTED,
TOOL_MAX
-
};
enum ToolOptions {
@@ -489,7 +492,8 @@ private:
MENU_VIEW_CAMERA_SETTINGS,
MENU_LOCK_SELECTED,
MENU_UNLOCK_SELECTED,
- MENU_VISIBILITY_SKELETON
+ MENU_VISIBILITY_SKELETON,
+ MENU_SNAP_TO_FLOOR
};
Button *tool_button[TOOL_MAX];
@@ -598,7 +602,7 @@ public:
void update_transform_gizmo();
void update_all_gizmos();
-
+ void snap_selected_nodes_to_floor();
void select_gizmo_highlight_axis(int p_axis);
void set_custom_camera(Node *p_camera) { custom_camera = p_camera; }
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 8d38bf39b5..d8982c751c 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -349,21 +349,33 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
Ref<Script> existing = selected->get_script();
- if (existing.is_valid())
- editor->push_item(existing.ptr());
- else {
- String path = selected->get_filename();
- if (path == "") {
- String root_path = editor_data->get_edited_scene_root()->get_filename();
- if (root_path == "") {
- path = "res://" + selected->get_name();
- } else {
- path = root_path.get_base_dir() + "/" + selected->get_name();
+
+ String path = selected->get_filename();
+ if (path == "") {
+ String root_path = editor_data->get_edited_scene_root()->get_filename();
+ if (root_path == "") {
+ path = "res://" + selected->get_name();
+ } else {
+ path = root_path.get_base_dir() + "/" + selected->get_name();
+ }
+ }
+
+ String inherits = selected->get_class();
+ if (existing.is_valid()) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptLanguage *l = ScriptServer::get_language(i);
+ if (l->get_type() == existing->get_class()) {
+ if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
+ String name = l->get_global_class_name(existing->get_path(), NULL);
+ inherits = editor->get_editor_data().script_class_get_base(name);
+ } else if (l->can_inherit_from_file()) {
+ inherits = "\"" + existing->get_path() + "\"";
+ }
}
}
- script_create_dialog->config(selected->get_class(), path);
- script_create_dialog->popup_centered();
}
+ script_create_dialog->config(inherits, path);
+ script_create_dialog->popup_centered();
} break;
case TOOL_CLEAR_SCRIPT: {
@@ -1426,13 +1438,9 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing);
}
- editor_data->get_undo_redo().add_do_method(editor, "push_item", p_script.operator->());
- editor_data->get_undo_redo().add_undo_method(editor, "push_item", (Script *)NULL);
-
- editor_data->get_undo_redo().add_do_method(this, "_update_script_button");
- editor_data->get_undo_redo().add_undo_method(this, "_update_script_button");
-
editor_data->get_undo_redo().commit_action();
+
+ editor->push_item(p_script.operator->());
}
void SceneTreeDock::_delete_confirm() {
@@ -1521,16 +1529,9 @@ void SceneTreeDock::_delete_confirm() {
void SceneTreeDock::_update_script_button() {
if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) {
- if (EditorNode::get_singleton()->get_editor_selection()->get_selection().front()->key()->get_script().is_null()) {
- button_create_script->show();
- button_clear_script->hide();
- } else {
- button_create_script->hide();
- button_clear_script->show();
- }
+ button_create_script->show();
} else {
button_create_script->hide();
- button_clear_script->hide();
}
}