mirror of
https://github.com/godotengine/godot.git
synced 2024-11-12 23:24:26 +00:00
Use collision detection ray to reposition an object already in the scene
This commit is contained in:
parent
db66bd35af
commit
fb768a7118
@ -673,6 +673,7 @@ void Node3DEditorViewport::cancel_transform() {
|
||||
sp->set_global_transform(se->original);
|
||||
}
|
||||
|
||||
collision_reposition = false;
|
||||
finish_transform();
|
||||
set_message(TTR("Transform Aborted."), 3);
|
||||
}
|
||||
@ -1802,7 +1803,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
if (b->is_pressed()) {
|
||||
clicked_wants_append = b->is_shift_pressed();
|
||||
|
||||
if (_edit.mode != TRANSFORM_NONE && _edit.instant) {
|
||||
if (_edit.mode != TRANSFORM_NONE && (_edit.instant || collision_reposition)) {
|
||||
commit_transform();
|
||||
break; // just commit the edit, stop processing the event so we don't deselect the object
|
||||
}
|
||||
@ -2398,10 +2399,10 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
cancel_transform();
|
||||
}
|
||||
if (!is_freelook_active() && !k->is_echo()) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event) && _edit.mode != TRANSFORM_TRANSLATE) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event) && (_edit.mode != TRANSFORM_TRANSLATE || collision_reposition)) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
begin_transform(TRANSFORM_TRANSLATE, true);
|
||||
} else if (_edit.instant) {
|
||||
} else if (_edit.instant || collision_reposition) {
|
||||
commit_transform();
|
||||
begin_transform(TRANSFORM_TRANSLATE, true);
|
||||
}
|
||||
@ -2409,7 +2410,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_rotate", p_event) && _edit.mode != TRANSFORM_ROTATE) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
begin_transform(TRANSFORM_ROTATE, true);
|
||||
} else if (_edit.instant) {
|
||||
} else if (_edit.instant || collision_reposition) {
|
||||
commit_transform();
|
||||
begin_transform(TRANSFORM_ROTATE, true);
|
||||
}
|
||||
@ -2417,11 +2418,23 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
||||
if (ED_IS_SHORTCUT("spatial_editor/instant_scale", p_event) && _edit.mode != TRANSFORM_SCALE) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
begin_transform(TRANSFORM_SCALE, true);
|
||||
} else if (_edit.instant) {
|
||||
} else if (_edit.instant || collision_reposition) {
|
||||
commit_transform();
|
||||
begin_transform(TRANSFORM_SCALE, true);
|
||||
}
|
||||
}
|
||||
if (ED_IS_SHORTCUT("spatial_editor/collision_reposition", p_event) && editor_selection->get_selected_node_list().size() == 1 && !collision_reposition) {
|
||||
if (_edit.mode == TRANSFORM_NONE || _edit.instant) {
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
_compute_edit(_edit.mouse_pos);
|
||||
} else {
|
||||
commit_transform();
|
||||
_compute_edit(_edit.mouse_pos);
|
||||
}
|
||||
_edit.mode = TRANSFORM_TRANSLATE;
|
||||
collision_reposition = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Freelook doesn't work in orthogonal mode.
|
||||
@ -3072,11 +3085,24 @@ void Node3DEditorViewport::_notification(int p_what) {
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PHYSICS_PROCESS: {
|
||||
if (collision_reposition) {
|
||||
List<Node *> &selection = editor_selection->get_selected_node_list();
|
||||
|
||||
if (selection.size() == 1) {
|
||||
Node3D *first_selected_node = Object::cast_to<Node3D>(selection.front()->get());
|
||||
double snap = EDITOR_GET("interface/inspector/default_float_step");
|
||||
int snap_step_decimals = Math::range_step_decimals(snap);
|
||||
set_message(TTR("Translating:") + " (" + String::num(first_selected_node->get_global_position().x, snap_step_decimals) + ", " +
|
||||
String::num(first_selected_node->get_global_position().y, snap_step_decimals) + ", " + String::num(first_selected_node->get_global_position().z, snap_step_decimals) + ")");
|
||||
first_selected_node->set_global_position(spatial_editor->snap_point(_get_instance_position(_edit.mouse_pos, first_selected_node)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!update_preview_node) {
|
||||
return;
|
||||
}
|
||||
if (preview_node->is_inside_tree()) {
|
||||
preview_node_pos = spatial_editor->snap_point(_get_instance_position(preview_node_viewport_pos));
|
||||
preview_node_pos = spatial_editor->snap_point(_get_instance_position(preview_node_viewport_pos, preview_node));
|
||||
double snap = EDITOR_GET("interface/inspector/default_float_step");
|
||||
int snap_step_decimals = Math::range_step_decimals(snap);
|
||||
set_message(TTR("Instantiating:") + " (" + String::num(preview_node_pos.x, snap_step_decimals) + ", " +
|
||||
@ -3966,7 +3992,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool show_gizmo = spatial_editor->is_gizmo_visible() && !_edit.instant && transform_gizmo_visible;
|
||||
bool show_gizmo = spatial_editor->is_gizmo_visible() && !_edit.instant && transform_gizmo_visible && !collision_reposition;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Transform3D axis_angle;
|
||||
if (xform.basis.get_column(i).normalized().dot(xform.basis.get_column((i + 1) % 3).normalized()) < 1.0) {
|
||||
@ -3997,7 +4023,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
|
||||
xform.orthonormalize();
|
||||
xform.basis.scale(scale);
|
||||
RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[3], xform);
|
||||
RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], spatial_editor->is_gizmo_visible() && !_edit.instant && transform_gizmo_visible && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
|
||||
RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], spatial_editor->is_gizmo_visible() && !_edit.instant && transform_gizmo_visible && !collision_reposition && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
|
||||
}
|
||||
|
||||
void Node3DEditorViewport::set_state(const Dictionary &p_state) {
|
||||
@ -4241,7 +4267,19 @@ void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node,
|
||||
accept = p_accept;
|
||||
}
|
||||
|
||||
Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const {
|
||||
void _insert_rid_recursive(Node *node, HashSet<RID> &rids) {
|
||||
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(node);
|
||||
if (co) {
|
||||
rids.insert(co->get_rid());
|
||||
}
|
||||
|
||||
for (int i = 0; i < node->get_child_count(); i++) {
|
||||
Node *child = node->get_child(i);
|
||||
_insert_rid_recursive(child, rids);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos, Node3D *p_node) const {
|
||||
const float MAX_DISTANCE = 50.0;
|
||||
const float FALLBACK_DISTANCE = 5.0;
|
||||
|
||||
@ -4250,13 +4288,28 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
|
||||
|
||||
PhysicsDirectSpaceState3D *ss = get_tree()->get_root()->get_world_3d()->get_direct_space_state();
|
||||
|
||||
HashSet<RID> rids;
|
||||
|
||||
if (!preview_node->is_inside_tree()) {
|
||||
List<Node *> &selection = editor_selection->get_selected_node_list();
|
||||
|
||||
Node3D *first_selected_node = Object::cast_to<Node3D>(selection.front()->get());
|
||||
|
||||
Array children = first_selected_node->get_children();
|
||||
|
||||
if (first_selected_node) {
|
||||
_insert_rid_recursive(first_selected_node, rids);
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsDirectSpaceState3D::RayParameters ray_params;
|
||||
ray_params.exclude = rids;
|
||||
ray_params.from = world_pos;
|
||||
ray_params.to = world_pos + world_ray * camera->get_far();
|
||||
|
||||
PhysicsDirectSpaceState3D::RayResult result;
|
||||
if (ss->intersect_ray(ray_params, result) && preview_node->get_child_count() > 0) {
|
||||
// Calculate an offset for the `preview_node` such that the its bounding box is on top of and touching the contact surface's plane.
|
||||
if (ss->intersect_ray(ray_params, result) && (preview_node->get_child_count() > 0 || !preview_node->is_inside_tree())) {
|
||||
// Calculate an offset for the `p_node` such that the its bounding box is on top of and touching the contact surface's plane.
|
||||
|
||||
// Use the Gram-Schmidt process to get an orthonormal Basis aligned with the surface normal.
|
||||
const Vector3 bb_basis_x = result.normal;
|
||||
@ -4271,10 +4324,10 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
|
||||
const Basis bb_basis = Basis(bb_basis_x, bb_basis_y, bb_basis_z);
|
||||
|
||||
// This normal-aligned Basis allows us to create an AABB that can fit on the surface plane as snugly as possible.
|
||||
const Transform3D bb_transform = Transform3D(bb_basis, preview_node->get_transform().origin);
|
||||
const AABB preview_node_bb = _calculate_spatial_bounds(preview_node, true, &bb_transform);
|
||||
// The x-axis's alignment with the surface normal also makes it trivial to get the distance from `preview_node`'s origin at (0, 0, 0) to the correct AABB face.
|
||||
const float offset_distance = -preview_node_bb.position.x;
|
||||
const Transform3D bb_transform = Transform3D(bb_basis, p_node->get_transform().origin);
|
||||
const AABB p_node_bb = _calculate_spatial_bounds(p_node, true, &bb_transform);
|
||||
// The x-axis's alignment with the surface normal also makes it trivial to get the distance from `p_node`'s origin at (0, 0, 0) to the correct AABB face.
|
||||
const float offset_distance = -p_node_bb.position.x;
|
||||
|
||||
// `result_offset` is in global space.
|
||||
const Vector3 result_offset = result.position + result.normal * offset_distance;
|
||||
@ -4912,6 +4965,7 @@ void Node3DEditorViewport::commit_transform() {
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
|
||||
collision_reposition = false;
|
||||
finish_transform();
|
||||
set_message("");
|
||||
}
|
||||
@ -5509,6 +5563,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
|
||||
ED_SHORTCUT("spatial_editor/instant_translate", TTR("Begin Translate Transformation"));
|
||||
ED_SHORTCUT("spatial_editor/instant_rotate", TTR("Begin Rotate Transformation"));
|
||||
ED_SHORTCUT("spatial_editor/instant_scale", TTR("Begin Scale Transformation"));
|
||||
ED_SHORTCUT("spatial_editor/collision_reposition", TTR("Reposition Using Collisions"), KeyModifierMask::SHIFT | Key::G);
|
||||
|
||||
preview_camera = memnew(CheckBox);
|
||||
preview_camera->set_text(TTR("Preview"));
|
||||
|
@ -245,6 +245,7 @@ private:
|
||||
bool auto_orthogonal;
|
||||
bool lock_rotation;
|
||||
bool transform_gizmo_visible = true;
|
||||
bool collision_reposition = false;
|
||||
real_t gizmo_scale;
|
||||
|
||||
bool freelook_active;
|
||||
@ -338,7 +339,6 @@ private:
|
||||
TRANSFORM_ROTATE,
|
||||
TRANSFORM_TRANSLATE,
|
||||
TRANSFORM_SCALE
|
||||
|
||||
};
|
||||
enum TransformPlane {
|
||||
TRANSFORM_VIEW,
|
||||
@ -471,7 +471,7 @@ private:
|
||||
void _list_select(Ref<InputEventMouseButton> b);
|
||||
Point2 _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const;
|
||||
|
||||
Vector3 _get_instance_position(const Point2 &p_pos) const;
|
||||
Vector3 _get_instance_position(const Point2 &p_pos, Node3D *p_node) const;
|
||||
static AABB _calculate_spatial_bounds(const Node3D *p_parent, bool p_omit_top_level = false, const Transform3D *p_bounds_orientation = nullptr);
|
||||
|
||||
Node *_sanitize_preview_node(Node *p_node) const;
|
||||
|
Loading…
Reference in New Issue
Block a user