Merge pull request #20022 from EIREXE/snap_to_floor

Add snap to floor functionality to the editor
This commit is contained in:
Max Hilbrunner 2018-07-26 22:31:03 +02:00 committed by GitHub
commit 6cf5eb8e37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 4 deletions

View File

@ -4270,6 +4270,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 +4748,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 +4889,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 +5250,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 +5261,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);

View File

@ -109,7 +109,6 @@ private:
int index;
String name;
void _menu_option(int p_option);
Spatial *preview_node;
AABB *preview_bounds;
Vector<String> selected_files;
@ -405,7 +404,6 @@ public:
TOOL_LOCK_SELECTED,
TOOL_UNLOCK_SELECTED,
TOOL_MAX
};
enum ToolOptions {
@ -489,7 +487,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 +597,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; }