Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions editor/scene/3d/node_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,10 @@ void Node3DEditorViewport::cancel_transform() {
sp->set_global_transform(se->original);
}

for (const KeyValue<Node3D *, Transform3D> &pair : _edit.children_original_globals) {
pair.key->set_global_transform(pair.value);
}

collision_reposition = false;
finish_transform();
set_message(TTRC("Transform Aborted."), 3);
Expand Down Expand Up @@ -1136,6 +1140,24 @@ void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
sel_item->original = sel_item->sp->get_global_gizmo_transform();
}
}

if (spatial_editor->is_preserve_children_transform_enabled() && _edit.children_original_globals.is_empty()) {
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
for (Node *E : selection) {
Node3D *sp = Object::cast_to<Node3D>(E);
if (!sp) {
continue;
}

int child_count = sp->get_child_count();
for (int i = 0; i < child_count; i++) {
Node3D *child = Object::cast_to<Node3D>(sp->get_child(i));
if (child) {
_edit.children_original_globals[child] = child->get_global_transform();
}
}
}
}
}

static Key _get_key_modifier_setting(const String &p_property) {
Expand Down Expand Up @@ -1437,11 +1459,33 @@ void Node3DEditorViewport::_transform_gizmo_apply(Node3D *p_node, const Transfor
return;
}

bool preserve_children = spatial_editor->is_preserve_children_transform_enabled();

Vector<Transform3D> children_global_transforms;
Vector<Node3D *> node3d_children;

if (preserve_children) {
int child_count = p_node->get_child_count();
for (int i = 0; i < child_count; i++) {
Node3D *child = Object::cast_to<Node3D>(p_node->get_child(i));
if (child) {
children_global_transforms.push_back(child->get_global_transform());
node3d_children.push_back(child);
}
}
}

if (p_local) {
p_node->set_transform(p_transform);
} else {
p_node->set_global_transform(p_transform);
}

if (preserve_children) {
for (int i = 0; i < node3d_children.size(); i++) {
node3d_children[i]->set_global_transform(children_global_transforms[i]);
}
}
}

Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local, bool p_orthogonal, bool p_view_axis) {
Expand Down Expand Up @@ -5160,6 +5204,8 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_

void Node3DEditorViewport::begin_transform(TransformMode p_mode, bool instant) {
if (get_selected_count() > 0) {
_edit.children_original_globals.clear();

_edit.mode = p_mode;
_compute_edit(_edit.mouse_pos);
_edit.instant = instant;
Expand Down Expand Up @@ -5218,6 +5264,18 @@ void Node3DEditorViewport::commit_transform() {
undo_redo->add_do_method(sp, "set_transform", sp->get_local_gizmo_transform());
undo_redo->add_undo_method(sp, "set_transform", se->original_local);
}

if (!_edit.children_original_globals.is_empty()) {
for (const KeyValue<Node3D *, Transform3D> &pair : _edit.children_original_globals) {
Node3D *child = pair.key;
Transform3D original_global = pair.value;
Transform3D current_global = child->get_global_transform();

undo_redo->add_do_method(child, "set_global_transform", current_global);
undo_redo->add_undo_method(child, "set_global_transform", original_global);
}
}

undo_redo->commit_action();

collision_reposition = false;
Expand Down Expand Up @@ -5646,6 +5704,7 @@ void Node3DEditorViewport::finish_transform() {
_edit.accumulated_rotation_angle = 0.0;
_edit.rotation_angle = 0.0;
_edit.gizmo_initiated = false;
_edit.children_original_globals.clear();
spatial_editor->set_local_coords_enabled(_edit.original_local);
spatial_editor->update_transform_gizmo();
surface->queue_redraw();
Expand Down Expand Up @@ -6692,6 +6751,7 @@ Dictionary Node3DEditor::get_state() const {
d["scale_snap"] = snap_scale_value;

d["local_coords"] = tool_option_button[TOOL_OPT_LOCAL_COORDS]->is_pressed();
d["preserve_children_transform"] = tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->is_pressed();

int vc = 0;
if (view_layout_menu->get_popup()->is_item_checked(view_layout_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT))) {
Expand Down Expand Up @@ -6786,6 +6846,10 @@ void Node3DEditor::set_state(const Dictionary &p_state) {

_snap_update();

if (d.has("preserve_children_transform")) {
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_pressed(d["preserve_children_transform"]);
}

if (d.has("local_coords")) {
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["local_coords"]);
update_transform_gizmo();
Expand Down Expand Up @@ -7028,6 +7092,47 @@ void Node3DEditor::_menu_item_toggled(bool pressed, int p_option) {
viewports[i]->update_transform_gizmo_highlight();
}
} break;

case MENU_TOOL_PRESERVE_CHILDREN_TRANSFORM: {
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_pressed(pressed);
if (pressed) {
EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &Node3DEditor::_undo_redo_inspector_callback));
} else {
EditorNode::get_editor_data().remove_undo_redo_inspector_hook_callback(callable_mp(this, &Node3DEditor::_undo_redo_inspector_callback));
}
} break;
}
}

void Node3DEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value) {
Node3D *node = Object::cast_to<Node3D>(p_edited);
if (!node) {
return;
}

static const char *transform_properties[] = { "position", "rotation", "scale", "quaternion", "basis", "transform", nullptr };
bool is_transform_prop = false;
for (int i = 0; transform_properties[i]; i++) {
if (p_property == transform_properties[i]) {
is_transform_prop = true;
break;
}
}
if (!is_transform_prop) {
return;
}

EditorUndoRedoManager *undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
ERR_FAIL_NULL(undo_redo);

int child_count = node->get_child_count();
for (int i = 0; i < child_count; i++) {
Node3D *child = Object::cast_to<Node3D>(node->get_child(i));
if (child) {
Transform3D child_global = child->get_global_transform();
undo_redo->add_do_method(child, "set_global_transform", child_global);
undo_redo->add_undo_method(child, "set_global_transform", child_global);
}
}
}

Expand Down Expand Up @@ -8619,6 +8724,7 @@ void Node3DEditor::_update_theme() {
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_button_icon(get_editor_theme_icon(SNAME("Object")));
tool_option_button[TOOL_OPT_USE_SNAP]->set_button_icon(get_editor_theme_icon(SNAME("Snap")));
tool_option_button[TOOL_OPT_USE_TRACKBALL]->set_button_icon(get_editor_theme_icon(SNAME("Trackball")));
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_button_icon(get_editor_theme_icon(SNAME("Pin")));

view_layout_menu->get_popup()->set_item_icon(view_layout_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_editor_theme_icon(SNAME("Panels1")));
view_layout_menu->get_popup()->set_item_icon(view_layout_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_editor_theme_icon(SNAME("Panels2")));
Expand Down Expand Up @@ -9596,6 +9702,16 @@ Node3DEditor::Node3DEditor() {
tool_option_button[TOOL_OPT_USE_TRACKBALL]->set_shortcut_context(this);
tool_option_button[TOOL_OPT_USE_TRACKBALL]->set_accessibility_name(TTRC("Use Trackball"));

tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM] = memnew(Button);
main_menu_hbox->add_child(tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]);
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_toggle_mode(true);
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_theme_type_variation(SceneStringName(FlatButton));
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->connect(SceneStringName(toggled), callable_mp(this, &Node3DEditor::_menu_item_toggled).bind(MENU_TOOL_PRESERVE_CHILDREN_TRANSFORM));
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_shortcut(ED_SHORTCUT("spatial_editor/preserve_children_transform", TTRC("Preserve Children Transform"), Key::P));
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_shortcut_context(this);
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_accessibility_name(TTRC("Preserve Children Transform"));
tool_option_button[TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->set_tooltip_text(TTRC("When enabled, transforming a node will preserve the global transform of its children.\nThis also applies when editing transform properties in the Inspector."));

main_menu_hbox->add_child(memnew(VSeparator));
sun_button = memnew(Button);
sun_button->set_tooltip_text(TTRC("Toggle preview sunlight.\nIf a DirectionalLight3D node is added to the scene, preview sunlight is disabled."));
Expand Down
7 changes: 7 additions & 0 deletions editor/scene/3d/node_3d_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ class Node3DEditorViewport : public Control {
Vector3 initial_click_vector;
Vector3 previous_rotation_vector;
bool gizmo_initiated = false;

HashMap<Node3D *, Transform3D> children_original_globals;
} _edit;

Ref<View3DController> view_3d_controller;
Expand Down Expand Up @@ -583,6 +585,7 @@ class Node3DEditor : public VBoxContainer {
TOOL_OPT_LOCAL_COORDS,
TOOL_OPT_USE_SNAP,
TOOL_OPT_USE_TRACKBALL,
TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM,
TOOL_OPT_MAX
};

Expand Down Expand Up @@ -686,6 +689,7 @@ class Node3DEditor : public VBoxContainer {
MENU_TOOL_LOCAL_COORDS,
MENU_TOOL_USE_SNAP,
MENU_TOOL_USE_TRACKBALL,
MENU_TOOL_PRESERVE_CHILDREN_TRANSFORM,
MENU_TRANSFORM_CONFIGURE_SNAP,
MENU_TRANSFORM_DIALOG,
MENU_VIEW_USE_1_VIEWPORT,
Expand Down Expand Up @@ -877,6 +881,8 @@ class Node3DEditor : public VBoxContainer {

void _update_theme();

void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);

protected:
void _notification(int p_what);
//void _gui_input(InputEvent p_event);
Expand All @@ -901,6 +907,7 @@ class Node3DEditor : public VBoxContainer {
ToolMode get_tool_mode() const { return tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
void set_local_coords_enabled(bool on) const { tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_pressed(on); }
bool is_preserve_children_transform_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_PRESERVE_CHILDREN_TRANSFORM]->is_pressed(); }
bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; }
real_t get_translate_snap() const;
real_t get_rotate_snap() const;
Expand Down
2 changes: 2 additions & 0 deletions modules/csg/csg_shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,8 @@ void CSGShape3D::update_shape() {

set_base(root_mesh->get_rid());

update_gizmos();

#ifndef PHYSICS_3D_DISABLED
_update_collision_faces();
#endif // PHYSICS_3D_DISABLED
Expand Down