diff options
-rw-r--r-- | doc/classes/Skin.xml | 7 | ||||
-rw-r--r-- | editor/animation_bezier_editor.cpp | 88 | ||||
-rw-r--r-- | editor/animation_bezier_editor.h | 9 | ||||
-rw-r--r-- | editor/animation_track_editor.cpp | 102 | ||||
-rw-r--r-- | editor/animation_track_editor.h | 15 | ||||
-rw-r--r-- | editor/debugger/editor_debugger_inspector.cpp | 7 | ||||
-rw-r--r-- | editor/debugger/script_editor_debugger.cpp | 19 | ||||
-rw-r--r-- | editor/debugger/script_editor_debugger.h | 1 | ||||
-rw-r--r-- | editor/editor_node.cpp | 2 | ||||
-rw-r--r-- | editor/inspector_dock.cpp | 24 | ||||
-rw-r--r-- | editor/plugins/animation_blend_tree_editor_plugin.cpp | 4 | ||||
-rw-r--r-- | editor/plugins/polygon_2d_editor_plugin.cpp | 50 | ||||
-rw-r--r-- | editor/plugins/polygon_2d_editor_plugin.h | 9 | ||||
-rw-r--r-- | scene/gui/view_panner.cpp | 12 | ||||
-rw-r--r-- | scene/gui/view_panner.h | 17 | ||||
-rw-r--r-- | scene/resources/skin.cpp | 1 | ||||
-rw-r--r-- | servers/rendering/shader_compiler.cpp | 21 | ||||
-rw-r--r-- | tests/core/math/test_vector2.h | 366 | ||||
-rw-r--r-- | tests/core/math/test_vector2i.h | 144 | ||||
-rw-r--r-- | tests/core/math/test_vector3.h | 395 | ||||
-rw-r--r-- | tests/core/math/test_vector3i.h | 145 | ||||
-rw-r--r-- | tests/test_main.cpp | 4 |
22 files changed, 1306 insertions, 136 deletions
diff --git a/doc/classes/Skin.xml b/doc/classes/Skin.xml index d24963a887..572558c3f5 100644 --- a/doc/classes/Skin.xml +++ b/doc/classes/Skin.xml @@ -14,6 +14,13 @@ <description> </description> </method> + <method name="add_named_bind"> + <return type="void" /> + <argument index="0" name="name" type="String" /> + <argument index="1" name="pose" type="Transform3D" /> + <description> + </description> + </method> <method name="clear_binds"> <return type="void" /> <description> diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 1085d34c4e..3678642521 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor_scale.h" +#include "scene/gui/view_panner.h" #include "scene/resources/text_line.h" float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) { @@ -216,6 +217,9 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V } void AnimationBezierTrackEdit::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + panner->set_control_scheme((ViewPanner::ControlScheme)EDITOR_GET("interface/editors/animation_editors_panning_scheme").operator int()); + } if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) { close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); @@ -610,6 +614,11 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); + if (panner->gui_input(p_event)) { + accept_event(); + return; + } + if (p_event->is_pressed()) { if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->matches_event(p_event)) { duplicate_selection(); @@ -623,40 +632,24 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) { - const float v_zoom_orig = v_zoom; - if (mb->is_command_pressed()) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); - } else { + if (mb.is_valid() && mb->is_pressed() && mb->is_alt_pressed()) { + // Alternate zoom (doesn't affect timeline). + if (mb->get_button_index() == MouseButton::WHEEL_DOWN) { + const float v_zoom_orig = v_zoom; if (v_zoom < 100000) { v_zoom *= 1.2; } + v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig); + update(); } - v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig); - update(); - } - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { - const float v_zoom_orig = v_zoom; - if (mb->is_command_pressed()) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); - } else { + if (mb->get_button_index() == MouseButton::WHEEL_UP) { + const float v_zoom_orig = v_zoom; if (v_zoom > 0.000001) { v_zoom /= 1.2; } - } - v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig); - update(); - } - - if (mb.is_valid() && mb->get_button_index() == MouseButton::MIDDLE) { - if (mb->is_pressed()) { - int x = mb->get_position().x - timeline->get_name_limit(); - panning_timeline_from = x / timeline->get_zoom_scale(); - panning_timeline = true; - panning_timeline_at = timeline->get_value(); - } else { - panning_timeline = false; + v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig); + update(); } } @@ -934,22 +927,6 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) { - v_scroll += mm->get_relative().y * v_zoom; - if (v_scroll > 100000) { - v_scroll = 100000; - } - if (v_scroll < -100000) { - v_scroll = -100000; - } - - int x = mm->get_position().x - timeline->get_name_limit(); - float ofs = x / timeline->get_zoom_scale(); - float diff = ofs - panning_timeline_from; - timeline->set_value(panning_timeline_at - diff); - - update(); - } if (moving_selection_attempt && mm.is_valid()) { if (!moving_selection) { moving_selection = true; @@ -1038,6 +1015,28 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } } +void AnimationBezierTrackEdit::_scroll_callback(Vector2 p_scroll_vec) { + _pan_callback(-p_scroll_vec * 32); +} + +void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec) { + v_scroll += p_scroll_vec.y * v_zoom; + v_scroll = CLAMP(v_scroll, -100000, 100000); + timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); + update(); +} + +void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) { + const float v_zoom_orig = v_zoom; + if (p_scroll_vec.y > 0) { + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); + } else { + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); + } + v_scroll = v_scroll + (p_origin.y - get_size().y / 2) * (v_zoom - v_zoom_orig); + update(); +} + void AnimationBezierTrackEdit::_menu_selected(int p_index) { switch (p_index) { case MENU_KEY_INSERT: { @@ -1171,6 +1170,11 @@ void AnimationBezierTrackEdit::_bind_methods() { } AnimationBezierTrackEdit::AnimationBezierTrackEdit() { + panner.instantiate(); + panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_scroll_callback), callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); + panner->set_disable_rmb(true); + panner->set_control_scheme(ViewPanner::SCROLL_PANS); + play_position = memnew(Control); play_position->set_mouse_filter(MOUSE_FILTER_PASS); add_child(play_position); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index d9bc85a258..6a5b97a7da 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -33,6 +33,8 @@ #include "animation_track_editor.h" +class ViewPanner; + class AnimationBezierTrackEdit : public Control { GDCLASS(AnimationBezierTrackEdit, Control); @@ -123,9 +125,10 @@ class AnimationBezierTrackEdit : public Control { Set<int> selection; - bool panning_timeline = false; - float panning_timeline_from; - float panning_timeline_at; + Ref<ViewPanner> panner; + void _scroll_callback(Vector2 p_scroll_vec); + void _pan_callback(Vector2 p_scroll_vec); + void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin); void _draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right); void _draw_track(int p_track, const Color &p_color); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index f4e719f552..13e9d58744 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -32,12 +32,12 @@ #include "animation_track_editor_plugins.h" #include "core/input/input.h" -#include "core/os/keyboard.h" #include "editor/animation_bezier_editor.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor_node.h" #include "editor_scale.h" #include "scene/animation/animation_player.h" +#include "scene/gui/view_panner.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" @@ -1458,6 +1458,10 @@ int AnimationTimelineEdit::get_name_limit() const { } void AnimationTimelineEdit::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + panner->set_control_scheme((ViewPanner::ControlScheme)EDITOR_GET("interface/editors/animation_editors_panning_scheme").operator int()); + } + if (p_what == NOTIFICATION_ENTER_TREE) { add_track->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); @@ -1760,17 +1764,12 @@ void AnimationTimelineEdit::_play_position_draw() { void AnimationTimelineEdit::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - const Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { - get_zoom()->set_value(get_zoom()->get_value() * 1.05); + if (panner->gui_input(p_event)) { accept_event(); + return; } - if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) { - get_zoom()->set_value(get_zoom()->get_value() / 1.05); - accept_event(); - } + const Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->is_alt_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { if (track_edit) { @@ -1796,29 +1795,19 @@ void AnimationTimelineEdit::gui_input(const Ref<InputEvent> &p_event) { dragging_hsize = false; } if (mb.is_valid() && mb->get_position().x > get_name_limit() && mb->get_position().x < (get_size().width - get_buttons_width())) { - if (!panning_timeline && mb->get_button_index() == MouseButton::LEFT) { + if (!panner->is_panning() && mb->get_button_index() == MouseButton::LEFT) { int x = mb->get_position().x - get_name_limit(); float ofs = x / get_zoom_scale() + get_value(); emit_signal(SNAME("timeline_changed"), ofs, false, Input::get_singleton()->is_key_pressed(Key::ALT)); dragging_timeline = true; } - if (!dragging_timeline && mb->get_button_index() == MouseButton::MIDDLE) { - int x = mb->get_position().x - get_name_limit(); - panning_timeline_from = x / get_zoom_scale(); - panning_timeline = true; - panning_timeline_at = get_value(); - } } if (dragging_timeline && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { dragging_timeline = false; } - if (panning_timeline && mb.is_valid() && mb->get_button_index() == MouseButton::MIDDLE && !mb->is_pressed()) { - panning_timeline = false; - } - Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { @@ -1839,17 +1828,29 @@ void AnimationTimelineEdit::gui_input(const Ref<InputEvent> &p_event) { if (dragging_timeline) { int x = mm->get_position().x - get_name_limit(); float ofs = x / get_zoom_scale() + get_value(); - emit_signal(SNAME("timeline_changed"), ofs, false, Input::get_singleton()->is_key_pressed(Key::ALT)); - } - if (panning_timeline) { - int x = mm->get_position().x - get_name_limit(); - float ofs = x / get_zoom_scale(); - float diff = ofs - panning_timeline_from; - set_value(panning_timeline_at - diff); + emit_signal(SNAME("timeline_changed"), ofs, false, mm->is_alt_pressed()); } } } +void AnimationTimelineEdit::_scroll_callback(Vector2 p_scroll_vec) { + // Timeline has no vertical scroll, so we change it to horizontal. + p_scroll_vec.x += p_scroll_vec.y; + _pan_callback(-p_scroll_vec * 32); +} + +void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec) { + set_value(get_value() - p_scroll_vec.x / get_zoom_scale()); +} + +void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) { + if (p_scroll_vec.y < 0) { + get_zoom()->set_value(get_zoom()->get_value() * 1.05); + } else { + get_zoom()->set_value(get_zoom()->get_value() / 1.05); + } +} + void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { use_fps = p_use_fps; update_values(); @@ -1926,10 +1927,14 @@ AnimationTimelineEdit::AnimationTimelineEdit() { add_track->get_popup()->connect("index_pressed", callable_mp(this, &AnimationTimelineEdit::_track_added)); len_hb->hide(); - panning_timeline = false; dragging_timeline = false; dragging_hsize = false; + panner.instantiate(); + panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_scroll_callback), callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_disable_rmb(true); + panner->set_control_scheme(ViewPanner::SCROLL_PANS); + set_layout_direction(Control::LAYOUT_DIRECTION_LTR); } @@ -4500,6 +4505,10 @@ MenuButton *AnimationTrackEditor::get_edit_menu() { } void AnimationTrackEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + panner->set_control_scheme((ViewPanner::ControlScheme)EDITOR_GET("interface/editors/animation_editors_panning_scheme").operator int()); + } + if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) { zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons"))); snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); @@ -5212,17 +5221,12 @@ void AnimationTrackEditor::_box_selection_draw() { } void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) { - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); + if (panner->gui_input(p_event)) { scroll->accept_event(); + return; } - if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); - scroll->accept_event(); - } + Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->is_alt_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { goto_prev_step(true); @@ -5262,10 +5266,6 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) { - timeline->set_value(timeline->get_value() - mm->get_relative().x / timeline->get_zoom_scale()); - } - if (mm.is_valid() && box_selecting) { if ((mm->get_button_mask() & MouseButton::MASK_LEFT) == MouseButton::NONE) { // No longer. @@ -5302,6 +5302,23 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) { } } +void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec) { + _pan_callback(-p_scroll_vec * 32); +} + +void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec) { + timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); + scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); +} + +void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) { + if (p_scroll_vec.y < 0) { + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); + } else { + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); + } +} + void AnimationTrackEditor::_cancel_bezier_edit() { bezier_edit->hide(); scroll->show(); @@ -6088,6 +6105,11 @@ AnimationTrackEditor::AnimationTrackEditor() { timeline->connect("value_changed", callable_mp(this, &AnimationTrackEditor::_timeline_value_changed)); timeline->connect("length_changed", callable_mp(this, &AnimationTrackEditor::_update_length)); + panner.instantiate(); + panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_scroll_callback), callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); + panner->set_disable_rmb(true); + panner->set_control_scheme(ViewPanner::SCROLL_PANS); + scroll = memnew(ScrollContainer); timeline_vbox->add_child(scroll); scroll->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index b5d44bc0d3..2a2b20ada9 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -48,8 +48,8 @@ #include "scene_tree_editor.h" class AnimationPlayer; - class AnimationTrackEdit; +class ViewPanner; class AnimationTimelineEdit : public Range { GDCLASS(AnimationTimelineEdit, Range); @@ -81,9 +81,11 @@ class AnimationTimelineEdit : public Range { bool editing; bool use_fps; - bool panning_timeline; - float panning_timeline_from; - float panning_timeline_at; + Ref<ViewPanner> panner; + void _scroll_callback(Vector2 p_scroll_vec); + void _pan_callback(Vector2 p_scroll_vec); + void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin); + bool dragging_timeline; bool dragging_hsize; float dragging_hsize_from; @@ -374,6 +376,11 @@ class AnimationTrackEditor : public VBoxContainer { PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr); + Ref<ViewPanner> panner; + void _scroll_callback(Vector2 p_scroll_vec); + void _pan_callback(Vector2 p_scroll_vec); + void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin); + void _timeline_value_changed(double); float insert_key_from_track_call_ofs; diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index ddcd32c16f..38bdbe2870 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -262,11 +262,18 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) { variables->prop_values[type + n] = v; variables->update(); edit(variables); + + // To prevent constantly resizing when using filtering. + int size_x = get_size().x; + if (size_x > get_custom_minimum_size().x) { + set_custom_minimum_size(Size2(size_x, 0)); + } } void EditorDebuggerInspector::clear_stack_variables() { variables->clear(); variables->update(); + set_custom_minimum_size(Size2(0, 0)); } String EditorDebuggerInspector::get_stack_variable(const String &p_var) { diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index b72a20ee2f..df9f02023a 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -790,6 +790,7 @@ void ScriptEditorDebugger::_notification(int p_what) { error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated)); vmem_refresh->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); vmem_export->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons"))); + search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); reason->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); @@ -864,6 +865,7 @@ void ScriptEditorDebugger::_notification(int p_what) { docontinue->set_icon(get_theme_icon(SNAME("DebugContinue"), SNAME("EditorIcons"))); vmem_refresh->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); vmem_export->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons"))); + search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); } break; } } @@ -1658,14 +1660,29 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected)); sc->add_child(stack_dump); + VBoxContainer *inspector_vbox = memnew(VBoxContainer); + sc->add_child(inspector_vbox); + + HBoxContainer *tools_hb = memnew(HBoxContainer); + inspector_vbox->add_child(tools_hb); + + search = memnew(LineEdit); + search->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search->set_placeholder(TTR("Filter stack variables")); + search->set_clear_button_enabled(true); + tools_hb->add_child(search); + inspector = memnew(EditorDebuggerInspector); inspector->set_h_size_flags(SIZE_EXPAND_FILL); + inspector->set_v_size_flags(SIZE_EXPAND_FILL); inspector->set_enable_capitalize_paths(false); inspector->set_read_only(true); inspector->connect("object_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected)); inspector->connect("object_edited", callable_mp(this, &ScriptEditorDebugger::_remote_object_edited)); inspector->connect("object_property_updated", callable_mp(this, &ScriptEditorDebugger::_remote_object_property_updated)); - sc->add_child(inspector); + inspector->register_text_enter(search); + inspector->set_use_filter(true); + inspector_vbox->add_child(inspector); tabs->add_child(dbg); } diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index ff1a852f26..ceb30e4565 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -134,6 +134,7 @@ private: LineEdit *vmem_total; Tree *stack_dump; + LineEdit *search = nullptr; EditorDebuggerInspector *inspector; SceneDebuggerTree *scene_tree; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index a8cb2e791c..9b4b99b32d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -6039,8 +6039,10 @@ EditorNode::EditorNode() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle", PROPERTY_USAGE_DEFAULT)); EDITOR_DEF("run/auto_save/save_before_running", true); EDITOR_DEF("interface/editors/sub_editor_panning_scheme", 0); + EDITOR_DEF("interface/editors/animation_editors_panning_scheme", 1); // Should be in sync with ControlScheme in ViewPanner. EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/editors/sub_editor_panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans", PROPERTY_USAGE_DEFAULT)); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/editors/animation_editors_panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans", PROPERTY_USAGE_DEFAULT)); const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false); for (const String &E : textfile_ext) { diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index ccb287e433..f56e868286 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -406,6 +406,7 @@ Container *InspectorDock::get_addon_area() { void InspectorDock::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { @@ -415,6 +416,7 @@ void InspectorDock::_notification(int p_what) { resource_load_button->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons"))); resource_save_button->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons"))); resource_extra_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + open_docs_button->set_icon(get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons"))); PopupMenu *resource_extra_popup = resource_extra_button->get_popup(); resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons"))); @@ -430,6 +432,7 @@ void InspectorDock::_notification(int p_what) { history_menu->set_icon(get_theme_icon(SNAME("History"), SNAME("EditorIcons"))); object_menu->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); + search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); } break; @@ -562,7 +565,6 @@ void InspectorDock::update_keying() { InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { set_name("Inspector"); - set_theme(p_editor->get_gui_base()->get_theme()); editor = p_editor; editor_data = &p_editor_data; @@ -573,7 +575,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { resource_new_button = memnew(Button); resource_new_button->set_flat(true); resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it.")); - resource_new_button->set_icon(get_theme_icon(SNAME("New"), SNAME("EditorIcons"))); general_options_hb->add_child(resource_new_button); resource_new_button->connect("pressed", callable_mp(this, &InspectorDock::_new_resource)); resource_new_button->set_focus_mode(Control::FOCUS_NONE); @@ -581,14 +582,12 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { resource_load_button = memnew(Button); resource_load_button->set_flat(true); resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it.")); - resource_load_button->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons"))); general_options_hb->add_child(resource_load_button); resource_load_button->connect("pressed", callable_mp(this, &InspectorDock::_open_resource_selector)); resource_load_button->set_focus_mode(Control::FOCUS_NONE); resource_save_button = memnew(MenuButton); resource_save_button->set_tooltip(TTR("Save the currently edited resource.")); - resource_save_button->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons"))); general_options_hb->add_child(resource_save_button); resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE); resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS); @@ -597,7 +596,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { resource_save_button->set_disabled(true); resource_extra_button = memnew(MenuButton); - resource_extra_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); resource_extra_button->set_tooltip(TTR("Extra resource options.")); general_options_hb->add_child(resource_extra_button); resource_extra_button->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_resource_extra_popup)); @@ -614,11 +612,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { backward_button = memnew(Button); backward_button->set_flat(true); general_options_hb->add_child(backward_button); - if (is_layout_rtl()) { - backward_button->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons"))); - } else { - backward_button->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons"))); - } backward_button->set_tooltip(TTR("Go to the previous edited object in history.")); backward_button->set_disabled(true); backward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_back)); @@ -626,18 +619,12 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { forward_button = memnew(Button); forward_button->set_flat(true); general_options_hb->add_child(forward_button); - if (is_layout_rtl()) { - forward_button->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons"))); - } else { - forward_button->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons"))); - } forward_button->set_tooltip(TTR("Go to the next edited object in history.")); forward_button->set_disabled(true); forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward)); history_menu = memnew(MenuButton); history_menu->set_tooltip(TTR("History of recently edited objects.")); - history_menu->set_icon(get_theme_icon(SNAME("History"), SNAME("EditorIcons"))); general_options_hb->add_child(history_menu); history_menu->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_history)); history_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_select_history)); @@ -652,7 +639,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { open_docs_button->set_flat(true); open_docs_button->set_disabled(true); open_docs_button->set_tooltip(TTR("Open documentation for this object.")); - open_docs_button->set_icon(get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons"))); open_docs_button->set_shortcut(ED_SHORTCUT("property_editor/open_help", TTR("Open Documentation"))); subresource_hb->add_child(open_docs_button); open_docs_button->connect("pressed", callable_mp(this, &InspectorDock::_menu_option), varray(OBJECT_REQUEST_HELP)); @@ -668,13 +654,11 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { search = memnew(LineEdit); search->set_h_size_flags(Control::SIZE_EXPAND_FILL); search->set_placeholder(TTR("Filter properties")); - search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); search->set_clear_button_enabled(true); property_tools_hb->add_child(search); object_menu = memnew(MenuButton); object_menu->set_shortcut_context(this); - object_menu->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); property_tools_hb->add_child(object_menu); object_menu->set_tooltip(TTR("Manage object properties.")); object_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_menu_option)); @@ -682,8 +666,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { warning = memnew(Button); add_child(warning); warning->set_text(TTR("Changes may be lost!")); - warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); - warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); warning->set_clip_text(true); warning->hide(); warning->connect("pressed", callable_mp(this, &InspectorDock::_warning_pressed)); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 9ebdede4e9..10e2f5f1d0 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -732,6 +732,10 @@ void AnimationNodeBlendTreeEditor::_removed_from_graph() { } void AnimationNodeBlendTreeEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + graph->set_panning_scheme((GraphEdit::PanningScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int()); + } + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index e272b96778..c6d1d99c08 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -38,6 +38,8 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "scene/2d/skeleton_2d.h" +#include "scene/gui/scroll_container.h" +#include "scene/gui/view_panner.h" Node2D *Polygon2DEditor::_get_node() const { return node; @@ -63,9 +65,8 @@ int Polygon2DEditor::_get_polygon_count() const { void Polygon2DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_THEME_CHANGED: { - uv_edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); - bone_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + uv_panner->set_control_scheme((ViewPanner::ControlScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int()); } break; case NOTIFICATION_READY: { button_uv->set_icon(get_theme_icon(SNAME("Uv"), SNAME("EditorIcons"))); @@ -88,6 +89,11 @@ void Polygon2DEditor::_notification(int p_what) { uv_vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE); uv_hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + uv_edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + bone_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { @@ -440,6 +446,11 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { return; } + if (uv_panner->gui_input(p_input)) { + accept_event(); + return; + } + Transform2D mtx; mtx.elements[2] = -uv_draw_ofs; mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom)); @@ -767,23 +778,13 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } uv_edit_draw->update(); - - } else if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) { - uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * mb->get_factor()))); - } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) { - uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * mb->get_factor()))); } } Ref<InputEventMouseMotion> mm = p_input; if (mm.is_valid()) { - if ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || Input::get_singleton()->is_key_pressed(Key::SPACE)) { - Vector2 drag = mm->get_relative(); - uv_hscroll->set_value(uv_hscroll->get_value() - drag.x); - uv_vscroll->set_value(uv_vscroll->get_value() - drag.y); - - } else if (uv_drag) { + if (uv_drag) { Vector2 uv_drag_to = mm->get_position(); uv_drag_to = snap_point(uv_drag_to); // FIXME: Only works correctly with 'UV_MODE_EDIT_POINT', it's imprecise with the rest. Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from); @@ -925,6 +926,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } +void Polygon2DEditor::_uv_scroll_callback(Vector2 p_scroll_vec) { + _uv_pan_callback(-p_scroll_vec * 32); +} + +void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec) { + uv_hscroll->set_value(uv_hscroll->get_value() - p_scroll_vec.x); + uv_vscroll->set_value(uv_vscroll->get_value() - p_scroll_vec.y); +} + +void Polygon2DEditor::_uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) { + if (p_scroll_vec.y < 0) { + uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * Math::abs(p_scroll_vec.y)))); + } else { + uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * Math::abs(p_scroll_vec.y)))); + } +} + void Polygon2DEditor::_uv_scroll_changed(real_t) { if (updating_uv_scroll) { return; @@ -1262,6 +1280,10 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_edit_mode[2]->connect("pressed", callable_mp(this, &Polygon2DEditor::_uv_edit_mode_select), varray(2)); uv_edit_mode[3]->connect("pressed", callable_mp(this, &Polygon2DEditor::_uv_edit_mode_select), varray(3)); + uv_panner.instantiate(); + uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_scroll_callback), callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); + uv_panner->set_disable_rmb(true); + uv_mode_hb->add_child(memnew(VSeparator)); uv_main_vb->add_child(uv_mode_hb); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index a04179dcad..959c230d7b 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -32,7 +32,9 @@ #define POLYGON_2D_EDITOR_PLUGIN_H #include "editor/plugins/abstract_polygon_2d_editor.h" -#include "scene/gui/scroll_container.h" + +class ViewPanner; +class ScrollContainer; class Polygon2DEditor : public AbstractPolygon2DEditor { GDCLASS(Polygon2DEditor, AbstractPolygon2DEditor); @@ -78,6 +80,11 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { MenuButton *uv_menu; TextureRect *uv_icon_zoom; + Ref<ViewPanner> uv_panner; + void _uv_scroll_callback(Vector2 p_scroll_vec); + void _uv_pan_callback(Vector2 p_scroll_vec); + void _uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin); + VBoxContainer *bone_scroll_main_vb; ScrollContainer *bone_scroll; VBoxContainer *bone_scroll_vb; diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index 375b3732a4..ba5e8d4a17 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -36,11 +36,18 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { + // Alt modifier is unused, so ignore such events. + if (mb->is_alt_pressed()) { + return false; + } + Vector2i scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP)); if (scroll_vec != Vector2()) { if (control_scheme == SCROLL_PANS) { if (mb->is_ctrl_pressed()) { + scroll_vec.y *= mb->get_factor(); callback_helper(zoom_callback, scroll_vec, mb->get_position()); + return true; } else { Vector2 panning; if (mb->is_shift_pressed()) { @@ -51,7 +58,6 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) panning.x += mb->get_factor() * scroll_vec.x; } callback_helper(scroll_callback, panning); - return true; } } else { @@ -65,16 +71,16 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) panning.x += mb->get_factor() * scroll_vec.x; } callback_helper(scroll_callback, panning); - return true; } else if (!mb->is_shift_pressed()) { + scroll_vec.y *= mb->get_factor(); callback_helper(zoom_callback, scroll_vec, mb->get_position()); return true; } } } - if (mb->get_button_index() == MouseButton::MIDDLE || (mb->get_button_index() == MouseButton::RIGHT && !disable_rmb) || (mb->get_button_index() == MouseButton::LEFT && (Input::get_singleton()->is_key_pressed(Key::SPACE) || !mb->is_pressed()))) { + if (mb->get_button_index() == MouseButton::MIDDLE || (mb->get_button_index() == MouseButton::RIGHT && !disable_rmb) || (mb->get_button_index() == MouseButton::LEFT && (Input::get_singleton()->is_key_pressed(Key::SPACE) || (is_dragging && !mb->is_pressed())))) { if (mb->is_pressed()) { is_dragging = true; } else { diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h index e083d83de4..0a92cb3dfd 100644 --- a/scene/gui/view_panner.h +++ b/scene/gui/view_panner.h @@ -38,6 +38,13 @@ class InputEvent; class ViewPanner : public RefCounted { GDCLASS(ViewPanner, RefCounted); +public: + enum ControlScheme { + SCROLL_ZOOMS, + SCROLL_PANS, + }; + +private: bool is_dragging = false; bool disable_rmb = false; @@ -46,17 +53,15 @@ class ViewPanner : public RefCounted { Callable zoom_callback; void callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2 = Vector2()); - -public: - enum ControlScheme { - SCROLL_ZOOMS, - SCROLL_PANS, - }; ControlScheme control_scheme = SCROLL_ZOOMS; +public: void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback); void set_control_scheme(ControlScheme p_scheme); void set_disable_rmb(bool p_disable); + + bool is_panning() const { return is_dragging; } + bool gui_input(const Ref<InputEvent> &p_ev, Rect2 p_canvas_rect = Rect2()); }; diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index d371598cc0..54ed71999c 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -143,6 +143,7 @@ void Skin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bind_count"), &Skin::get_bind_count); ClassDB::bind_method(D_METHOD("add_bind", "bone", "pose"), &Skin::add_bind); + ClassDB::bind_method(D_METHOD("add_named_bind", "name", "pose"), &Skin::add_named_bind); ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose); ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose); diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 114e7e66cb..b0629de2f0 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -692,17 +692,36 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene vcode += _prestr(varying.precision, ShaderLanguage::is_float_type(varying.type)); vcode += _typestr(varying.type); vcode += " " + _mkid(varying_name); + uint32_t inc = 1U; + if (varying.array_size > 0) { + inc = (uint32_t)varying.array_size; + vcode += "["; vcode += itos(varying.array_size); vcode += "]"; } + + switch (varying.type) { + case SL::TYPE_MAT2: + inc *= 2U; + break; + case SL::TYPE_MAT3: + inc *= 3U; + break; + case SL::TYPE_MAT4: + inc *= 4U; + break; + default: + break; + } + vcode += ";\n"; r_gen_code.stage_globals[STAGE_VERTEX] += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode; r_gen_code.stage_globals[STAGE_FRAGMENT] += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode; - index++; + index += inc; } if (var_frag_to_light.size() > 0) { diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h new file mode 100644 index 0000000000..7dd14736e8 --- /dev/null +++ b/tests/core/math/test_vector2.h @@ -0,0 +1,366 @@ +/*************************************************************************/ +/* test_vector2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR2_H +#define TEST_VECTOR2_H + +#include "core/math/vector2.h" +#include "tests/test_macros.h" + +namespace TestVector2 { + +TEST_CASE("[Vector2] Angle methods") { + const Vector2 vector_x = Vector2(1, 0); + const Vector2 vector_y = Vector2(0, 1); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4), + "Vector2 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_y.angle_to(vector_x), (real_t)-Math_TAU / 4), + "Vector2 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to_point(vector_y), (real_t)Math_TAU * 3 / 8), + "Vector2 angle_to_point should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_y.angle_to_point(vector_x), (real_t)-Math_TAU / 8), + "Vector2 angle_to_point should work as expected."); +} + +TEST_CASE("[Vector2] Axis methods") { + Vector2 vector = Vector2(1.2, 3.4); + CHECK_MESSAGE( + vector.max_axis_index() == Vector2::Axis::AXIS_Y, + "Vector2 max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector2::Axis::AXIS_X, + "Vector2 min_axis_index should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == (real_t)1.2, + "Vector2 array operator should work as expected."); + vector[Vector2::Axis::AXIS_Y] = 3.7; + CHECK_MESSAGE( + vector[Vector2::Axis::AXIS_Y] == (real_t)3.7, + "Vector2 array operator setter should work as expected."); +} + +TEST_CASE("[Vector2] Interpolation methods") { + const Vector2 vector1 = Vector2(1, 2); + const Vector2 vector2 = Vector2(4, 5); + CHECK_MESSAGE( + vector1.lerp(vector2, 0.5) == Vector2(2.5, 3.5), + "Vector2 lerp should work as expected."); + CHECK_MESSAGE( + vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector2(2, 3)), + "Vector2 lerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 0.5).is_equal_approx(Vector2(0.538953602313995361, 0.84233558177947998)), + "Vector2 slerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 1.0 / 3.0).is_equal_approx(Vector2(0.508990883827209473, 0.860771894454956055)), + "Vector2 slerp should work as expected."); + CHECK_MESSAGE( + Vector2(5, 0).slerp(Vector2(0, 5), 0.5).is_equal_approx(Vector2(5, 5) * Math_SQRT12), + "Vector2 slerp with non-normalized values should work as expected."); + CHECK_MESSAGE( + Vector2().slerp(Vector2(), 0.5) == Vector2(), + "Vector2 slerp with both inputs as zero vectors should return a zero vector."); + CHECK_MESSAGE( + Vector2().slerp(Vector2(1, 1), 0.5) == Vector2(0.5, 0.5), + "Vector2 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Vector2(1, 1).slerp(Vector2(), 0.5) == Vector2(0.5, 0.5), + "Vector2 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)4.31959610746631919), + "Vector2 slerp with different length input should return a vector with an interpolated length."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)), + "Vector2 slerp with different length input should return a vector with an interpolated angle."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 0.5) == Vector2(2.375, 3.5), + "Vector2 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 1.0 / 3.0).is_equal_approx(Vector2(1.851851940155029297, 2.962963104248046875)), + "Vector2 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + Vector2(1, 0).move_toward(Vector2(10, 0), 3) == Vector2(4, 0), + "Vector2 move_toward should work as expected."); +} + +TEST_CASE("[Vector2] Length methods") { + const Vector2 vector1 = Vector2(10, 10); + const Vector2 vector2 = Vector2(20, 30); + CHECK_MESSAGE( + vector1.length_squared() == 200, + "Vector2 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT2), + "Vector2 length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 1300, + "Vector2 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), (real_t)36.05551275463989293119), + "Vector2 length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 500, + "Vector2 distance_squared_to should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.distance_to(vector2), (real_t)22.36067977499789696409), + "Vector2 distance_to should work as expected."); +} + +TEST_CASE("[Vector2] Limiting methods") { + const Vector2 vector = Vector2(10, 10); + CHECK_MESSAGE( + vector.limit_length().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 limit_length should work as expected."); + CHECK_MESSAGE( + vector.limit_length(5).is_equal_approx(5 * Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 limit_length should work as expected."); + + CHECK_MESSAGE( + Vector2(-5, 15).clamp(Vector2(), vector).is_equal_approx(Vector2(0, 10)), + "Vector2 clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector2(0, 15), Vector2(5, 20)).is_equal_approx(Vector2(5, 15)), + "Vector2 clamp should work as expected."); +} + +TEST_CASE("[Vector2] Normalization methods") { + CHECK_MESSAGE( + Vector2(1, 0).is_normalized() == true, + "Vector2 is_normalized should return true for a normalized vector."); + CHECK_MESSAGE( + Vector2(1, 1).is_normalized() == false, + "Vector2 is_normalized should return false for a non-normalized vector."); + CHECK_MESSAGE( + Vector2(1, 0).normalized() == Vector2(1, 0), + "Vector2 normalized should return the same vector for a normalized vector."); + CHECK_MESSAGE( + Vector2(1, 1).normalized().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 normalized should work as expected."); +} + +TEST_CASE("[Vector2] Operators") { + const Vector2 decimal1 = Vector2(2.3, 4.9); + const Vector2 decimal2 = Vector2(1.2, 3.4); + const Vector2 power1 = Vector2(0.75, 1.5); + const Vector2 power2 = Vector2(0.5, 0.125); + const Vector2 int1 = Vector2(4, 5); + const Vector2 int2 = Vector2(1, 2); + + CHECK_MESSAGE( + (decimal1 + decimal2).is_equal_approx(Vector2(3.5, 8.3)), + "Vector2 addition should behave as expected."); + CHECK_MESSAGE( + (power1 + power2) == Vector2(1.25, 1.625), + "Vector2 addition with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 + int2) == Vector2(5, 7), + "Vector2 addition with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 - decimal2).is_equal_approx(Vector2(1.1, 1.5)), + "Vector2 subtraction should behave as expected."); + CHECK_MESSAGE( + (power1 - power2) == Vector2(0.25, 1.375), + "Vector2 subtraction with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 - int2) == Vector2(3, 3), + "Vector2 subtraction with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * decimal2).is_equal_approx(Vector2(2.76, 16.66)), + "Vector2 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * power2) == Vector2(0.375, 0.1875), + "Vector2 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * int2) == Vector2(4, 10), + "Vector2 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / decimal2).is_equal_approx(Vector2(1.91666666666666666, 1.44117647058823529)), + "Vector2 division should behave as expected."); + CHECK_MESSAGE( + (power1 / power2) == Vector2(1.5, 12.0), + "Vector2 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / int2) == Vector2(4, 2.5), + "Vector2 division with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * 2).is_equal_approx(Vector2(4.6, 9.8)), + "Vector2 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * 2) == Vector2(1.5, 3), + "Vector2 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * 2) == Vector2(8, 10), + "Vector2 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / 2).is_equal_approx(Vector2(1.15, 2.45)), + "Vector2 division should behave as expected."); + CHECK_MESSAGE( + (power1 / 2) == Vector2(0.375, 0.75), + "Vector2 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / 2) == Vector2(2, 2.5), + "Vector2 division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector2i)decimal1) == Vector2i(2, 4), + "Vector2 cast to Vector2i should work as expected."); + CHECK_MESSAGE( + ((Vector2i)decimal2) == Vector2i(1, 3), + "Vector2 cast to Vector2i should work as expected."); + CHECK_MESSAGE( + Vector2(Vector2i(1, 2)) == Vector2(1, 2), + "Vector2 constructed from Vector2i should work as expected."); +} + +TEST_CASE("[Vector2] Other methods") { + const Vector2 vector = Vector2(1.2, 3.4); + CHECK_MESSAGE( + Math::is_equal_approx(vector.aspect(), (real_t)1.2 / (real_t)3.4), + "Vector2 aspect should work as expected."); + CHECK_MESSAGE( + vector.direction_to(Vector2()).is_equal_approx(-vector.normalized()), + "Vector2 direction_to should work as expected."); + CHECK_MESSAGE( + Vector2(1, 1).direction_to(Vector2(2, 2)).is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 direction_to should work as expected."); + CHECK_MESSAGE( + vector.posmod(2).is_equal_approx(Vector2(1.2, 1.4)), + "Vector2 posmod should work as expected."); + CHECK_MESSAGE( + (-vector).posmod(2).is_equal_approx(Vector2(0.8, 0.6)), + "Vector2 posmod should work as expected."); + CHECK_MESSAGE( + vector.posmodv(Vector2(1, 2)).is_equal_approx(Vector2(0.2, 1.4)), + "Vector2 posmodv should work as expected."); + CHECK_MESSAGE( + (-vector).posmodv(Vector2(2, 3)).is_equal_approx(Vector2(0.8, 2.6)), + "Vector2 posmodv should work as expected."); + CHECK_MESSAGE( + vector.rotated(Math_TAU / 4).is_equal_approx(Vector2(-3.4, 1.2)), + "Vector2 rotated should work as expected."); + CHECK_MESSAGE( + vector.snapped(Vector2(1, 1)) == Vector2(1, 3), + "Vector2 snapped to integers should be the same as rounding."); + CHECK_MESSAGE( + Vector2(3.4, 5.6).snapped(Vector2(1, 1)) == Vector2(3, 6), + "Vector2 snapped to integers should be the same as rounding."); + CHECK_MESSAGE( + vector.snapped(Vector2(0.25, 0.25)) == Vector2(1.25, 3.5), + "Vector2 snapped to 0.25 should give exact results."); +} + +TEST_CASE("[Vector2] Plane methods") { + const Vector2 vector = Vector2(1.2, 3.4); + const Vector2 vector_y = Vector2(0, 1); + CHECK_MESSAGE( + vector.bounce(vector_y) == Vector2(1.2, -3.4), + "Vector2 bounce on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.reflect(vector_y) == Vector2(-1.2, 3.4), + "Vector2 reflect on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.project(vector_y) == Vector2(0, 3.4), + "Vector2 projected on the X axis should only give the Y component."); + CHECK_MESSAGE( + vector.slide(vector_y) == Vector2(1.2, 0), + "Vector2 slide on a plane with normal of the Y axis should set the Y to zero."); +} + +TEST_CASE("[Vector2] Rounding methods") { + const Vector2 vector1 = Vector2(1.2, 5.6); + const Vector2 vector2 = Vector2(1.2, -5.6); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector2 abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector2 abs should work as expected."); + + CHECK_MESSAGE( + vector1.ceil() == Vector2(2, 6), + "Vector2 ceil should work as expected."); + CHECK_MESSAGE( + vector2.ceil() == Vector2(2, -5), + "Vector2 ceil should work as expected."); + + CHECK_MESSAGE( + vector1.floor() == Vector2(1, 5), + "Vector2 floor should work as expected."); + CHECK_MESSAGE( + vector2.floor() == Vector2(1, -6), + "Vector2 floor should work as expected."); + + CHECK_MESSAGE( + vector1.round() == Vector2(1, 6), + "Vector2 round should work as expected."); + CHECK_MESSAGE( + vector2.round() == Vector2(1, -6), + "Vector2 round should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector2(1, 1), + "Vector2 sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector2(1, -1), + "Vector2 sign should work as expected."); +} + +TEST_CASE("[Vector2] Linear algebra methods") { + const Vector2 vector_x = Vector2(1, 0); + const Vector2 vector_y = Vector2(0, 1); + CHECK_MESSAGE( + vector_x.cross(vector_y) == 1, + "Vector2 cross product of X and Y should give 1."); + CHECK_MESSAGE( + vector_y.cross(vector_x) == -1, + "Vector2 cross product of Y and X should give negative 1."); + + CHECK_MESSAGE( + vector_x.dot(vector_y) == 0.0, + "Vector2 dot product of perpendicular vectors should be zero."); + CHECK_MESSAGE( + vector_x.dot(vector_x) == 1.0, + "Vector2 dot product of identical unit vectors should be one."); + CHECK_MESSAGE( + (vector_x * 10).dot(vector_x * 10) == 100.0, + "Vector2 dot product of same direction vectors should behave as expected."); +} +} // namespace TestVector2 + +#endif // TEST_VECTOR2_H diff --git a/tests/core/math/test_vector2i.h b/tests/core/math/test_vector2i.h new file mode 100644 index 0000000000..86e254654d --- /dev/null +++ b/tests/core/math/test_vector2i.h @@ -0,0 +1,144 @@ +/*************************************************************************/ +/* test_vector2i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR2I_H +#define TEST_VECTOR2I_H + +#include "core/math/vector2.h" +#include "tests/test_macros.h" + +namespace TestVector2i { + +TEST_CASE("[Vector2i] Axis methods") { + Vector2i vector = Vector2i(2, 3); + CHECK_MESSAGE( + vector.max_axis_index() == Vector2i::Axis::AXIS_Y, + "Vector2i max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector2i::Axis::AXIS_X, + "Vector2i min_axis_index should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == 2, + "Vector2i array operator should work as expected."); + vector[Vector2i::Axis::AXIS_Y] = 5; + CHECK_MESSAGE( + vector[Vector2i::Axis::AXIS_Y] == 5, + "Vector2i array operator setter should work as expected."); +} + +TEST_CASE("[Vector2i] Clamp method") { + const Vector2i vector = Vector2i(10, 10); + CHECK_MESSAGE( + Vector2i(-5, 15).clamp(Vector2i(), vector) == Vector2i(0, 10), + "Vector2i clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector2i(0, 15), Vector2i(5, 20)) == Vector2i(5, 15), + "Vector2i clamp should work as expected."); +} + +TEST_CASE("[Vector2i] Length methods") { + const Vector2i vector1 = Vector2i(10, 10); + const Vector2i vector2 = Vector2i(20, 30); + CHECK_MESSAGE( + vector1.length_squared() == 200, + "Vector2i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * Math_SQRT2), + "Vector2i length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 1300, + "Vector2i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), 36.05551275463989293119), + "Vector2i length should work as expected."); +} + +TEST_CASE("[Vector2i] Operators") { + const Vector2i vector1 = Vector2i(5, 9); + const Vector2i vector2 = Vector2i(2, 3); + + CHECK_MESSAGE( + (vector1 + vector2) == Vector2i(7, 12), + "Vector2i addition with integers should give exact results."); + CHECK_MESSAGE( + (vector1 - vector2) == Vector2i(3, 6), + "Vector2i subtraction with integers should give exact results."); + CHECK_MESSAGE( + (vector1 * vector2) == Vector2i(10, 27), + "Vector2i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / vector2) == Vector2i(2, 3), + "Vector2i division with integers should give exact results."); + + CHECK_MESSAGE( + (vector1 * 2) == Vector2i(10, 18), + "Vector2i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / 2) == Vector2i(2, 4), + "Vector2i division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector2)vector1) == Vector2(5, 9), + "Vector2i cast to Vector2 should work as expected."); + CHECK_MESSAGE( + ((Vector2)vector2) == Vector2(2, 3), + "Vector2i cast to Vector2 should work as expected."); + CHECK_MESSAGE( + Vector2i(Vector2(1.1, 2.9)) == Vector2i(1, 2), + "Vector2i constructed from Vector2 should work as expected."); +} + +TEST_CASE("[Vector2i] Other methods") { + const Vector2i vector = Vector2i(1, 3); + CHECK_MESSAGE( + Math::is_equal_approx(vector.aspect(), (real_t)1.0 / (real_t)3.0), + "Vector2i aspect should work as expected."); +} + +TEST_CASE("[Vector2i] Abs and sign methods") { + const Vector2i vector1 = Vector2i(1, 3); + const Vector2i vector2 = Vector2i(1, -3); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector2i abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector2i abs should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector2i(1, 1), + "Vector2i sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector2i(1, -1), + "Vector2i sign should work as expected."); +} +} // namespace TestVector2i + +#endif // TEST_VECTOR2I_H diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h new file mode 100644 index 0000000000..97da035046 --- /dev/null +++ b/tests/core/math/test_vector3.h @@ -0,0 +1,395 @@ +/*************************************************************************/ +/* test_vector3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR3_H +#define TEST_VECTOR3_H + +#include "core/math/vector3.h" +#include "tests/test_macros.h" + +#define Math_SQRT13 0.57735026918962576450914878050196 +#define Math_SQRT3 1.7320508075688772935274463415059 + +namespace TestVector3 { + +TEST_CASE("[Vector3] Angle methods") { + const Vector3 vector_x = Vector3(1, 0, 0); + const Vector3 vector_y = Vector3(0, 1, 0); + const Vector3 vector_yz = Vector3(0, 1, 1); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4), + "Vector3 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to(vector_yz), (real_t)Math_TAU / 4), + "Vector3 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_yz.angle_to(vector_x), (real_t)Math_TAU / 4), + "Vector3 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_y.angle_to(vector_yz), (real_t)Math_TAU / 8), + "Vector3 angle_to should work as expected."); + + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.signed_angle_to(vector_y, vector_y), (real_t)Math_TAU / 4), + "Vector3 signed_angle_to edge case should be postiive."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.signed_angle_to(vector_yz, vector_y), (real_t)Math_TAU / -4), + "Vector3 signed_angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_yz.signed_angle_to(vector_x, vector_y), (real_t)Math_TAU / 4), + "Vector3 signed_angle_to should work as expected."); +} + +TEST_CASE("[Vector3] Axis methods") { + Vector3 vector = Vector3(1.2, 3.4, 5.6); + CHECK_MESSAGE( + vector.max_axis_index() == Vector3::Axis::AXIS_Z, + "Vector3 max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector3::Axis::AXIS_X, + "Vector3 min_axis_index should work as expected."); + CHECK_MESSAGE( + vector.get_axis(vector.max_axis_index()) == (real_t)5.6, + "Vector3 get_axis should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == (real_t)1.2, + "Vector3 array operator should work as expected."); + + vector.set_axis(Vector3::Axis::AXIS_Y, 4.7); + CHECK_MESSAGE( + vector.get_axis(Vector3::Axis::AXIS_Y) == (real_t)4.7, + "Vector3 set_axis should work as expected."); + vector[Vector3::Axis::AXIS_Y] = 3.7; + CHECK_MESSAGE( + vector[Vector3::Axis::AXIS_Y] == (real_t)3.7, + "Vector3 array operator setter should work as expected."); +} + +TEST_CASE("[Vector3] Interpolation methods") { + const Vector3 vector1 = Vector3(1, 2, 3); + const Vector3 vector2 = Vector3(4, 5, 6); + CHECK_MESSAGE( + vector1.lerp(vector2, 0.5) == Vector3(2.5, 3.5, 4.5), + "Vector3 lerp should work as expected."); + CHECK_MESSAGE( + vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector3(2, 3, 4)), + "Vector3 lerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 0.5).is_equal_approx(Vector3(0.363866806030273438, 0.555698215961456299, 0.747529566287994385)), + "Vector3 slerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 1.0 / 3.0).is_equal_approx(Vector3(0.332119762897491455, 0.549413740634918213, 0.766707837581634521)), + "Vector3 slerp should work as expected."); + CHECK_MESSAGE( + Vector3(5, 0, 0).slerp(Vector3(0, 3, 4), 0.5).is_equal_approx(Vector3(3.535533905029296875, 2.121320486068725586, 2.828427314758300781)), + "Vector3 slerp with non-normalized values should work as expected."); + CHECK_MESSAGE( + Vector3().slerp(Vector3(), 0.5) == Vector3(), + "Vector3 slerp with both inputs as zero vectors should return a zero vector."); + CHECK_MESSAGE( + Vector3().slerp(Vector3(1, 1, 1), 0.5) == Vector3(0.5, 0.5, 0.5), + "Vector3 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Vector3(1, 1, 1).slerp(Vector3(), 0.5) == Vector3(0.5, 0.5, 0.5), + "Vector3 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)6.25831088708303172), + "Vector3 slerp with different length input should return a vector with an interpolated length."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)), + "Vector3 slerp with different length input should return a vector with an interpolated angle."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 0.5) == Vector3(2.375, 3.5, 4.625), + "Vector3 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 1.0 / 3.0).is_equal_approx(Vector3(1.851851940155029297, 2.962963104248046875, 4.074074268341064453)), + "Vector3 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + Vector3(1, 0, 0).move_toward(Vector3(10, 0, 0), 3) == Vector3(4, 0, 0), + "Vector3 move_toward should work as expected."); +} + +TEST_CASE("[Vector3] Length methods") { + const Vector3 vector1 = Vector3(10, 10, 10); + const Vector3 vector2 = Vector3(20, 30, 40); + CHECK_MESSAGE( + vector1.length_squared() == 300, + "Vector3 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT3), + "Vector3 length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 2900, + "Vector3 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), (real_t)53.8516480713450403125), + "Vector3 length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 1400, + "Vector3 distance_squared_to should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.distance_to(vector2), (real_t)37.41657386773941385584), + "Vector3 distance_to should work as expected."); +} + +TEST_CASE("[Vector3] Limiting methods") { + const Vector3 vector = Vector3(10, 10, 10); + CHECK_MESSAGE( + vector.limit_length().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 limit_length should work as expected."); + CHECK_MESSAGE( + vector.limit_length(5).is_equal_approx(5 * Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 limit_length should work as expected."); + + CHECK_MESSAGE( + Vector3(-5, 5, 15).clamp(Vector3(), vector) == Vector3(0, 5, 10), + "Vector3 clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector3(0, 10, 15), Vector3(5, 10, 20)) == Vector3(5, 10, 15), + "Vector3 clamp should work as expected."); +} + +TEST_CASE("[Vector3] Normalization methods") { + CHECK_MESSAGE( + Vector3(1, 0, 0).is_normalized() == true, + "Vector3 is_normalized should return true for a normalized vector."); + CHECK_MESSAGE( + Vector3(1, 1, 1).is_normalized() == false, + "Vector3 is_normalized should return false for a non-normalized vector."); + CHECK_MESSAGE( + Vector3(1, 0, 0).normalized() == Vector3(1, 0, 0), + "Vector3 normalized should return the same vector for a normalized vector."); + CHECK_MESSAGE( + Vector3(1, 1, 0).normalized().is_equal_approx(Vector3(Math_SQRT12, Math_SQRT12, 0)), + "Vector3 normalized should work as expected."); + CHECK_MESSAGE( + Vector3(1, 1, 1).normalized().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 normalized should work as expected."); +} + +TEST_CASE("[Vector3] Operators") { + const Vector3 decimal1 = Vector3(2.3, 4.9, 7.8); + const Vector3 decimal2 = Vector3(1.2, 3.4, 5.6); + const Vector3 power1 = Vector3(0.75, 1.5, 0.625); + const Vector3 power2 = Vector3(0.5, 0.125, 0.25); + const Vector3 int1 = Vector3(4, 5, 9); + const Vector3 int2 = Vector3(1, 2, 3); + + CHECK_MESSAGE( + (decimal1 + decimal2).is_equal_approx(Vector3(3.5, 8.3, 13.4)), + "Vector3 addition should behave as expected."); + CHECK_MESSAGE( + (power1 + power2) == Vector3(1.25, 1.625, 0.875), + "Vector3 addition with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 + int2) == Vector3(5, 7, 12), + "Vector3 addition with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 - decimal2).is_equal_approx(Vector3(1.1, 1.5, 2.2)), + "Vector3 subtraction should behave as expected."); + CHECK_MESSAGE( + (power1 - power2) == Vector3(0.25, 1.375, 0.375), + "Vector3 subtraction with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 - int2) == Vector3(3, 3, 6), + "Vector3 subtraction with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * decimal2).is_equal_approx(Vector3(2.76, 16.66, 43.68)), + "Vector3 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * power2) == Vector3(0.375, 0.1875, 0.15625), + "Vector3 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * int2) == Vector3(4, 10, 27), + "Vector3 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / decimal2).is_equal_approx(Vector3(1.91666666666666666, 1.44117647058823529, 1.39285714285714286)), + "Vector3 division should behave as expected."); + CHECK_MESSAGE( + (power1 / power2) == Vector3(1.5, 12.0, 2.5), + "Vector3 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / int2) == Vector3(4, 2.5, 3), + "Vector3 division with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * 2).is_equal_approx(Vector3(4.6, 9.8, 15.6)), + "Vector3 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * 2) == Vector3(1.5, 3, 1.25), + "Vector3 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * 2) == Vector3(8, 10, 18), + "Vector3 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / 2).is_equal_approx(Vector3(1.15, 2.45, 3.9)), + "Vector3 division should behave as expected."); + CHECK_MESSAGE( + (power1 / 2) == Vector3(0.375, 0.75, 0.3125), + "Vector3 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / 2) == Vector3(2, 2.5, 4.5), + "Vector3 division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector3i)decimal1) == Vector3i(2, 4, 7), + "Vector3 cast to Vector3i should work as expected."); + CHECK_MESSAGE( + ((Vector3i)decimal2) == Vector3i(1, 3, 5), + "Vector3 cast to Vector3i should work as expected."); + CHECK_MESSAGE( + Vector3(Vector3i(1, 2, 3)) == Vector3(1, 2, 3), + "Vector3 constructed from Vector3i should work as expected."); +} + +TEST_CASE("[Vector3] Other methods") { + const Vector3 vector = Vector3(1.2, 3.4, 5.6); + CHECK_MESSAGE( + vector.direction_to(Vector3()).is_equal_approx(-vector.normalized()), + "Vector3 direction_to should work as expected."); + CHECK_MESSAGE( + Vector3(1, 1, 1).direction_to(Vector3(2, 2, 2)).is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 direction_to should work as expected."); + CHECK_MESSAGE( + vector.inverse().is_equal_approx(Vector3(1 / 1.2, 1 / 3.4, 1 / 5.6)), + "Vector3 inverse should work as expected."); + CHECK_MESSAGE( + vector.posmod(2).is_equal_approx(Vector3(1.2, 1.4, 1.6)), + "Vector3 posmod should work as expected."); + CHECK_MESSAGE( + (-vector).posmod(2).is_equal_approx(Vector3(0.8, 0.6, 0.4)), + "Vector3 posmod should work as expected."); + CHECK_MESSAGE( + vector.posmodv(Vector3(1, 2, 3)).is_equal_approx(Vector3(0.2, 1.4, 2.6)), + "Vector3 posmodv should work as expected."); + CHECK_MESSAGE( + (-vector).posmodv(Vector3(2, 3, 4)).is_equal_approx(Vector3(0.8, 2.6, 2.4)), + "Vector3 posmodv should work as expected."); + CHECK_MESSAGE( + vector.rotated(Vector3(0, 1, 0), Math_TAU / 4).is_equal_approx(Vector3(5.6, 3.4, -1.2)), + "Vector3 rotated should work as expected."); + CHECK_MESSAGE( + vector.snapped(Vector3(1, 1, 1)) == Vector3(1, 3, 6), + "Vector3 snapped to integers should be the same as rounding."); + CHECK_MESSAGE( + vector.snapped(Vector3(0.25, 0.25, 0.25)) == Vector3(1.25, 3.5, 5.5), + "Vector3 snapped to 0.25 should give exact results."); +} + +TEST_CASE("[Vector3] Plane methods") { + const Vector3 vector = Vector3(1.2, 3.4, 5.6); + const Vector3 vector_y = Vector3(0, 1, 0); + CHECK_MESSAGE( + vector.bounce(vector_y) == Vector3(1.2, -3.4, 5.6), + "Vector3 bounce on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.reflect(vector_y) == Vector3(-1.2, 3.4, -5.6), + "Vector3 reflect on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.project(vector_y) == Vector3(0, 3.4, 0), + "Vector3 projected on the X axis should only give the Y component."); + CHECK_MESSAGE( + vector.slide(vector_y) == Vector3(1.2, 0, 5.6), + "Vector3 slide on a plane with normal of the Y axis should set the Y to zero."); +} + +TEST_CASE("[Vector3] Rounding methods") { + const Vector3 vector1 = Vector3(1.2, 3.4, 5.6); + const Vector3 vector2 = Vector3(1.2, -3.4, -5.6); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector3 abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector3 abs should work as expected."); + + CHECK_MESSAGE( + vector1.ceil() == Vector3(2, 4, 6), + "Vector3 ceil should work as expected."); + CHECK_MESSAGE( + vector2.ceil() == Vector3(2, -3, -5), + "Vector3 ceil should work as expected."); + + CHECK_MESSAGE( + vector1.floor() == Vector3(1, 3, 5), + "Vector3 floor should work as expected."); + CHECK_MESSAGE( + vector2.floor() == Vector3(1, -4, -6), + "Vector3 floor should work as expected."); + + CHECK_MESSAGE( + vector1.round() == Vector3(1, 3, 6), + "Vector3 round should work as expected."); + CHECK_MESSAGE( + vector2.round() == Vector3(1, -3, -6), + "Vector3 round should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector3(1, 1, 1), + "Vector3 sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector3(1, -1, -1), + "Vector3 sign should work as expected."); +} + +TEST_CASE("[Vector3] Linear algebra methods") { + const Vector3 vector_x = Vector3(1, 0, 0); + const Vector3 vector_y = Vector3(0, 1, 0); + const Vector3 vector_z = Vector3(0, 0, 1); + CHECK_MESSAGE( + vector_x.cross(vector_y) == vector_z, + "Vector3 cross product of X and Y should give Z."); + CHECK_MESSAGE( + vector_y.cross(vector_x) == -vector_z, + "Vector3 cross product of Y and X should give negative Z."); + CHECK_MESSAGE( + vector_y.cross(vector_z) == vector_x, + "Vector3 cross product of Y and Z should give X."); + CHECK_MESSAGE( + vector_z.cross(vector_x) == vector_y, + "Vector3 cross product of Z and X should give Y."); + + CHECK_MESSAGE( + vector_x.dot(vector_y) == 0.0, + "Vector3 dot product of perpendicular vectors should be zero."); + CHECK_MESSAGE( + vector_x.dot(vector_x) == 1.0, + "Vector3 dot product of identical unit vectors should be one."); + CHECK_MESSAGE( + (vector_x * 10).dot(vector_x * 10) == 100.0, + "Vector3 dot product of same direction vectors should behave as expected."); +} +} // namespace TestVector3 + +#endif // TEST_VECTOR3_H diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h new file mode 100644 index 0000000000..b1c6944eba --- /dev/null +++ b/tests/core/math/test_vector3i.h @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* test_vector3i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR3I_H +#define TEST_VECTOR3I_H + +#include "core/math/vector3i.h" +#include "tests/test_macros.h" + +namespace TestVector3i { + +TEST_CASE("[Vector3i] Axis methods") { + Vector3i vector = Vector3i(1, 2, 3); + CHECK_MESSAGE( + vector.max_axis_index() == Vector3i::Axis::AXIS_Z, + "Vector3i max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector3i::Axis::AXIS_X, + "Vector3i min_axis_index should work as expected."); + CHECK_MESSAGE( + vector.get_axis(vector.max_axis_index()) == 3, + "Vector3i get_axis should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == 1, + "Vector3i array operator should work as expected."); + + vector.set_axis(Vector3i::Axis::AXIS_Y, 4); + CHECK_MESSAGE( + vector.get_axis(Vector3i::Axis::AXIS_Y) == 4, + "Vector3i set_axis should work as expected."); + vector[Vector3i::Axis::AXIS_Y] = 5; + CHECK_MESSAGE( + vector[Vector3i::Axis::AXIS_Y] == 5, + "Vector3i array operator setter should work as expected."); +} + +TEST_CASE("[Vector3i] Clamp method") { + const Vector3i vector = Vector3i(10, 10, 10); + CHECK_MESSAGE( + Vector3i(-5, 5, 15).clamp(Vector3i(), vector) == Vector3i(0, 5, 10), + "Vector3i clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector3i(0, 10, 15), Vector3i(5, 10, 20)) == Vector3i(5, 10, 15), + "Vector3i clamp should work as expected."); +} + +TEST_CASE("[Vector3i] Length methods") { + const Vector3i vector1 = Vector3i(10, 10, 10); + const Vector3i vector2 = Vector3i(20, 30, 40); + CHECK_MESSAGE( + vector1.length_squared() == 300, + "Vector3i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * Math_SQRT3), + "Vector3i length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 2900, + "Vector3i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), 53.8516480713450403125), + "Vector3i length should work as expected."); +} + +TEST_CASE("[Vector3i] Operators") { + const Vector3i vector1 = Vector3i(4, 5, 9); + const Vector3i vector2 = Vector3i(1, 2, 3); + + CHECK_MESSAGE( + (vector1 + vector2) == Vector3i(5, 7, 12), + "Vector3i addition with integers should give exact results."); + CHECK_MESSAGE( + (vector1 - vector2) == Vector3i(3, 3, 6), + "Vector3i subtraction with integers should give exact results."); + CHECK_MESSAGE( + (vector1 * vector2) == Vector3i(4, 10, 27), + "Vector3i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / vector2) == Vector3i(4, 2, 3), + "Vector3i division with integers should give exact results."); + + CHECK_MESSAGE( + (vector1 * 2) == Vector3i(8, 10, 18), + "Vector3i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / 2) == Vector3i(2, 2, 4), + "Vector3i division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector3)vector1) == Vector3(4, 5, 9), + "Vector3i cast to Vector3 should work as expected."); + CHECK_MESSAGE( + ((Vector3)vector2) == Vector3(1, 2, 3), + "Vector3i cast to Vector3 should work as expected."); + CHECK_MESSAGE( + Vector3i(Vector3(1.1, 2.9, 3.9)) == Vector3i(1, 2, 3), + "Vector3i constructed from Vector3 should work as expected."); +} + +TEST_CASE("[Vector3i] Abs and sign methods") { + const Vector3i vector1 = Vector3i(1, 3, 5); + const Vector3i vector2 = Vector3i(1, -3, -5); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector3i abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector3i abs should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector3i(1, 1, 1), + "Vector3i sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector3i(1, -1, -1), + "Vector3i sign should work as expected."); +} +} // namespace TestVector3i + +#endif // TEST_VECTOR3I_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 2b2c89fbf1..3826c03cad 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -48,6 +48,10 @@ #include "tests/core/math/test_math.h" #include "tests/core/math/test_random_number_generator.h" #include "tests/core/math/test_rect2.h" +#include "tests/core/math/test_vector2.h" +#include "tests/core/math/test_vector2i.h" +#include "tests/core/math/test_vector3.h" +#include "tests/core/math/test_vector3i.h" #include "tests/core/object/test_class_db.h" #include "tests/core/object/test_method_bind.h" #include "tests/core/object/test_object.h" |