Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion core/math/quaternion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Quaternion Quaternion::normalized() const {
}

bool Quaternion::is_normalized() const {
return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); //use less epsilon
return Math::is_equal_approx(length_squared(), 1, (real_t)0.01/*UNIT_EPSILON*/); //use less epsilon // 0.01 is a temporary fix for precision errors

@TokageItLab TokageItLab Oct 19, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Math::is_equal_approx(length_squared(), 1, (real_t)0.01/*UNIT_EPSILON*/); //use less epsilon // 0.01 is a temporary fix for precision errors
return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); //use less epsilon

I feel that the precision threshold should not be changed, and if the precision is insufficient prior to this point, and I think it is possible to increase the precision by using double insteads real_t (float) within the calculation as commented in #98090 (comment).

}

Quaternion Quaternion::inverse() const {
Expand Down
25 changes: 25 additions & 0 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1941,6 +1941,21 @@ void EditorPropertyQuaternion::_set_read_only(bool p_read_only) {
}
}

void EditorPropertyQuaternion::_edit_normalize_quaternion_value() {
if (normalize_quaternion_bttn->is_pressed()) {
Quaternion temp;
for (int i = 0; i < 4; i++) {
temp[i] = (real_t)spin[i]->get_value();
}
temp = temp.normalized();
for (int i = 0; i < 4; i++) {
spin[i]->set_value_no_signal((double)temp[i]);
}
_value_changed(-1, "");
update_property();
}
}

void EditorPropertyQuaternion::_edit_custom_value() {
if (edit_button->is_pressed()) {
edit_custom_bc->show();
Expand Down Expand Up @@ -2025,6 +2040,7 @@ void EditorPropertyQuaternion::_notification(int p_what) {
euler[i]->add_theme_color_override("label_color", colors[i]);
}
edit_button->set_icon(get_editor_theme_icon(SNAME("Edit")));
normalize_quaternion_bttn->set_icon(get_editor_theme_icon(SNAME("Key")));
euler_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("property_color"), SNAME("EditorProperty")));
warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning")));
warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
Expand Down Expand Up @@ -2065,6 +2081,7 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {

VBoxContainer *bc = memnew(VBoxContainer);
edit_custom_bc = memnew(VBoxContainer);
normalize_quaternion = memnew(VBoxContainer);
BoxContainer *edit_custom_layout;
if (horizontal) {
default_layout = memnew(HBoxContainer);
Expand All @@ -2076,10 +2093,12 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
}
edit_custom_bc->hide();
add_child(bc);
normalize_quaternion->set_h_size_flags(SIZE_EXPAND_FILL);
edit_custom_bc->set_h_size_flags(SIZE_EXPAND_FILL);
default_layout->set_h_size_flags(SIZE_EXPAND_FILL);
edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);
bc->add_child(default_layout);
bc->add_child(normalize_quaternion);
bc->add_child(edit_custom_bc);

static const char *desc[4] = { "x", "y", "z", "w" };
Expand All @@ -2095,6 +2114,12 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
}
}

normalize_quaternion_bttn = memnew(Button);
normalize_quaternion_bttn->set_flat(true);
default_layout->add_child(normalize_quaternion_bttn);
normalize_quaternion_bttn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_edit_normalize_quaternion_value));
add_focusable(normalize_quaternion_bttn);

warning = memnew(Button);
warning->set_text(TTR("Temporary Euler may be changed implicitly!"));
warning->set_clip_text(true);
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,14 +487,17 @@ class EditorPropertyQuaternion : public EditorProperty {
AcceptDialog *warning_dialog = nullptr;

Label *euler_label = nullptr;
VBoxContainer *normalize_quaternion = nullptr;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
VBoxContainer *normalize_quaternion = nullptr;
VBoxContainer *normalize_quaternion_btn_container = nullptr;

The name should be clearer.

@TokageItLab TokageItLab May 22, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I felt that the normalization button is overemphasized.

So how about making the letters “Nrm” or “1.0” into icons and placing them to the right of the SpinBox?

Then, it will conflicts with EulerEdit, so I think it would be smartest way is making it function as Nrm button when is_normalized() is false, and as EulerEdit button when is_normalized() is true since EulerEdit shouldn't be used for non-normalized quaternions.

In other words, it places only one button to the right of the SpinBox, whose icon changes depending on the state of is_normalized().

VBoxContainer *edit_custom_bc = nullptr;
EditorSpinSlider *euler[3];
Button *edit_button = nullptr;
Button *normalize_quaternion_bttn = nullptr;
Comment thread
TokageItLab marked this conversation as resolved.

@TokageItLab TokageItLab May 22, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Button *normalize_quaternion_bttn = nullptr;
Button *normalize_quaternion_btn = nullptr;

Nitpick for consistency with other classes.


Vector3 edit_euler;

void _value_changed(double p_val, const String &p_name);
void _edit_custom_value();
void _edit_normalize_quaternion_value();
void _custom_value_changed(double p_val);
void _warning_pressed();

Expand Down
2 changes: 2 additions & 0 deletions scene/3d/skeleton_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,8 @@ void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);

//ERR_FAIL_COND_EDMSG(!p_rotation.is_normalized(), "The quaternion bone " + get_bone_name(p_bone) + " pose rotation must be normalized.");

Comment on lines +868 to +871

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!p_rotation.is_normalized()) {
WARN_PRINT_ED("The quaternion bone " + get_bone_name(p_bone) + " pose rotation must be normalized.");
}

I recommend removing it to focus on PR for adding buttons for now.

If we are going to add checks, we should add some checks to the fundamental of the Skeleton, not only the Rotation check, but also the Scale of 0 check, etc.

bones[p_bone].pose_rotation = p_rotation;
bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
Expand Down