summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--core/bind/core_bind.cpp12
-rw-r--r--core/bind/core_bind.h4
-rw-r--r--core/input/input_map.cpp8
-rw-r--r--core/input/input_map.h4
-rw-r--r--core/math/basis.cpp214
-rw-r--r--core/math/basis.h13
-rw-r--r--core/os/os.cpp10
-rw-r--r--core/os/os.h4
-rw-r--r--core/variant_call.cpp12
-rw-r--r--doc/classes/InputMap.xml2
-rw-r--r--doc/classes/OS.xml2
-rw-r--r--doc/classes/PhysicsMaterial.xml2
-rw-r--r--doc/classes/Skeleton3D.xml39
-rw-r--r--drivers/unix/os_unix.cpp16
-rw-r--r--drivers/unix/os_unix.h4
-rw-r--r--editor/editor_help.cpp63
-rw-r--r--editor/editor_properties.cpp53
-rw-r--r--editor/editor_properties.h3
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp287
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h40
-rw-r--r--main/main.cpp8
-rw-r--r--main/tests/test_basis.cpp325
-rw-r--r--main/tests/test_basis.h40
-rw-r--r--main/tests/test_main.cpp6
-rw-r--r--modules/dds/texture_loader_dds.cpp10
-rw-r--r--modules/gdscript/gdscript_editor.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py4
-rw-r--r--modules/mono/build_scripts/mono_configure.py11
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs48
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs270
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs15
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs33
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs11
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp1
-rw-r--r--platform/windows/os_windows.cpp52
-rw-r--r--platform/windows/os_windows.h4
-rw-r--r--scene/2d/camera_2d.cpp8
-rw-r--r--scene/3d/skeleton_3d.cpp14
-rw-r--r--scene/3d/skeleton_3d.h14
-rw-r--r--scene/main/canvas_item.cpp4
49 files changed, 1334 insertions, 415 deletions
diff --git a/.gitignore b/.gitignore
index ca27e42016..d9537edbf2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,11 @@ gmon.out
*.cflags
*.cxxflags
+# Code::Blocks files
+*.cbp
+*.layout
+*.depend
+
# Eclipse CDT files
.cproject
.settings/
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index e81351a3a6..267391c4d6 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -498,18 +498,10 @@ Dictionary _OS::get_time_zone_info() const {
return infod;
}
-uint64_t _OS::get_unix_time() const {
+double _OS::get_unix_time() const {
return OS::get_singleton()->get_unix_time();
}
-uint64_t _OS::get_system_time_secs() const {
- return OS::get_singleton()->get_system_time_secs();
-}
-
-uint64_t _OS::get_system_time_msecs() const {
- return OS::get_singleton()->get_system_time_msecs();
-}
-
void _OS::delay_usec(uint32_t p_usec) const {
OS::get_singleton()->delay_usec(p_usec);
}
@@ -729,8 +721,6 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_unix_time"), &_OS::get_unix_time);
ClassDB::bind_method(D_METHOD("get_datetime_from_unix_time", "unix_time_val"), &_OS::get_datetime_from_unix_time);
ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime", "datetime"), &_OS::get_unix_time_from_datetime);
- ClassDB::bind_method(D_METHOD("get_system_time_secs"), &_OS::get_system_time_secs);
- ClassDB::bind_method(D_METHOD("get_system_time_msecs"), &_OS::get_system_time_msecs);
ClassDB::bind_method(D_METHOD("get_exit_code"), &_OS::get_exit_code);
ClassDB::bind_method(D_METHOD("set_exit_code", "code"), &_OS::set_exit_code);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 26d0f7b8af..f9f5a4e7d7 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -199,9 +199,7 @@ public:
Dictionary get_datetime_from_unix_time(int64_t unix_time_val) const;
int64_t get_unix_time_from_datetime(Dictionary datetime) const;
Dictionary get_time_zone_info() const;
- uint64_t get_unix_time() const;
- uint64_t get_system_time_secs() const;
- uint64_t get_system_time_msecs() const;
+ double get_unix_time() const;
uint64_t get_static_memory_usage() const;
uint64_t get_static_memory_peak_usage() const;
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 3cb4b43a26..ac032b7d10 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -48,7 +48,7 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events);
- ClassDB::bind_method(D_METHOD("get_action_list", "action"), &InputMap::_get_action_list);
+ ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events);
ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action);
ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals);
}
@@ -152,9 +152,9 @@ void InputMap::action_erase_events(const StringName &p_action) {
input_map[p_action].inputs.clear();
}
-Array InputMap::_get_action_list(const StringName &p_action) {
+Array InputMap::_action_get_events(const StringName &p_action) {
Array ret;
- const List<Ref<InputEvent>> *al = get_action_list(p_action);
+ const List<Ref<InputEvent>> *al = action_get_events(p_action);
if (al) {
for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) {
ret.push_back(E->get());
@@ -164,7 +164,7 @@ Array InputMap::_get_action_list(const StringName &p_action) {
return ret;
}
-const List<Ref<InputEvent>> *InputMap::get_action_list(const StringName &p_action) {
+const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) {
const Map<StringName, Action>::Element *E = input_map.find(p_action);
if (!E) {
return nullptr;
diff --git a/core/input/input_map.h b/core/input/input_map.h
index 3abc224ccf..548553ed31 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -56,7 +56,7 @@ private:
List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr) const;
- Array _get_action_list(const StringName &p_action);
+ Array _action_get_events(const StringName &p_action);
Array _get_actions();
protected:
@@ -76,7 +76,7 @@ public:
void action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event);
void action_erase_events(const StringName &p_action);
- const List<Ref<InputEvent>> *get_action_list(const StringName &p_action);
+ const List<Ref<InputEvent>> *action_get_events(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const;
bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr) const;
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index cbfd09810c..df5199b0f9 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -428,12 +428,9 @@ Vector3 Basis::get_euler_xyz() const {
// -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
Vector3 euler;
-#ifdef MATH_CHECKS
- ERR_FAIL_COND_V(!is_rotation(), euler);
-#endif
real_t sy = elements[0][2];
- if (sy < 1.0) {
- if (sy > -1.0) {
+ if (sy < (1.0 - CMP_EPSILON)) {
+ if (sy > -(1.0 - CMP_EPSILON)) {
// is this a pure Y rotation?
if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) {
// return the simplest form (human friendlier in editor and scripts)
@@ -446,12 +443,12 @@ Vector3 Basis::get_euler_xyz() const {
euler.z = Math::atan2(-elements[0][1], elements[0][0]);
}
} else {
- euler.x = -Math::atan2(elements[0][1], elements[1][1]);
+ euler.x = Math::atan2(elements[2][1], elements[1][1]);
euler.y = -Math_PI / 2.0;
euler.z = 0.0;
}
} else {
- euler.x = Math::atan2(elements[0][1], elements[1][1]);
+ euler.x = Math::atan2(elements[2][1], elements[1][1]);
euler.y = Math_PI / 2.0;
euler.z = 0.0;
}
@@ -481,15 +478,106 @@ void Basis::set_euler_xyz(const Vector3 &p_euler) {
*this = xmat * (ymat * zmat);
}
+Vector3 Basis::get_euler_xzy() const {
+ // Euler angles in XZY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy -sz cz*sy
+ // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx
+ // cy*sx*sz cz*sx cx*cy+sx*sz*sy
+
+ Vector3 euler;
+ real_t sz = elements[0][1];
+ if (sz < (1.0 - CMP_EPSILON)) {
+ if (sz > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::atan2(elements[2][1], elements[1][1]);
+ euler.y = Math::atan2(elements[0][2], elements[0][0]);
+ euler.z = Math::asin(-sz);
+ } else {
+ // It's -1
+ euler.x = -Math::atan2(elements[1][2], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = Math_PI / 2.0;
+ }
+ } else {
+ // It's 1
+ euler.x = -Math::atan2(elements[1][2], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = -Math_PI / 2.0;
+ }
+ return euler;
+}
+
+void Basis::set_euler_xzy(const Vector3 &p_euler) {
+ real_t c, s;
+
+ c = Math::cos(p_euler.x);
+ s = Math::sin(p_euler.x);
+ Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
+
+ c = Math::cos(p_euler.y);
+ s = Math::sin(p_euler.y);
+ Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
+
+ c = Math::cos(p_euler.z);
+ s = Math::sin(p_euler.z);
+ Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
+
+ *this = xmat * zmat * ymat;
+}
+
+Vector3 Basis::get_euler_yzx() const {
+ // Euler angles in YZX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx
+ // sz cz*cx -cz*sx
+ // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx
+
+ Vector3 euler;
+ real_t sz = elements[1][0];
+ if (sz < (1.0 - CMP_EPSILON)) {
+ if (sz > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::atan2(-elements[1][2], elements[1][1]);
+ euler.y = Math::atan2(-elements[2][0], elements[0][0]);
+ euler.z = Math::asin(sz);
+ } else {
+ // It's -1
+ euler.x = Math::atan2(elements[2][1], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = -Math_PI / 2.0;
+ }
+ } else {
+ // It's 1
+ euler.x = Math::atan2(elements[2][1], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = Math_PI / 2.0;
+ }
+ return euler;
+}
+
+void Basis::set_euler_yzx(const Vector3 &p_euler) {
+ real_t c, s;
+
+ c = Math::cos(p_euler.x);
+ s = Math::sin(p_euler.x);
+ Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
+
+ c = Math::cos(p_euler.y);
+ s = Math::sin(p_euler.y);
+ Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
+
+ c = Math::cos(p_euler.z);
+ s = Math::sin(p_euler.z);
+ Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
+
+ *this = ymat * zmat * xmat;
+}
+
// get_euler_yxz returns a vector containing the Euler angles in the YXZ convention,
// as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned
// as the x, y, and z components of a Vector3 respectively.
Vector3 Basis::get_euler_yxz() const {
- /* checking this is a bad idea, because obtaining from scaled transform is a valid use case
-#ifdef MATH_CHECKS
- ERR_FAIL_COND(!is_rotation());
-#endif
-*/
// Euler angles in YXZ convention.
// See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
//
@@ -501,8 +589,8 @@ Vector3 Basis::get_euler_yxz() const {
real_t m12 = elements[1][2];
- if (m12 < 1) {
- if (m12 > -1) {
+ if (m12 < (1 - CMP_EPSILON)) {
+ if (m12 > -(1 - CMP_EPSILON)) {
// is this a pure X rotation?
if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) {
// return the simplest form (human friendlier in editor and scripts)
@@ -516,12 +604,12 @@ Vector3 Basis::get_euler_yxz() const {
}
} else { // m12 == -1
euler.x = Math_PI * 0.5;
- euler.y = -atan2(-elements[0][1], elements[0][0]);
+ euler.y = atan2(elements[0][1], elements[0][0]);
euler.z = 0;
}
} else { // m12 == 1
euler.x = -Math_PI * 0.5;
- euler.y = -atan2(-elements[0][1], elements[0][0]);
+ euler.y = -atan2(elements[0][1], elements[0][0]);
euler.z = 0;
}
@@ -551,6 +639,100 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) {
*this = ymat * xmat * zmat;
}
+Vector3 Basis::get_euler_zxy() const {
+ // Euler angles in ZXY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx
+ // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx
+ // -cx*sy sx cx*cy
+ Vector3 euler;
+ real_t sx = elements[2][1];
+ if (sx < (1.0 - CMP_EPSILON)) {
+ if (sx > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::asin(sx);
+ euler.y = Math::atan2(-elements[2][0], elements[2][2]);
+ euler.z = Math::atan2(-elements[0][1], elements[1][1]);
+ } else {
+ // It's -1
+ euler.x = -Math_PI / 2.0;
+ euler.y = Math::atan2(elements[0][2], elements[0][0]);
+ euler.z = 0;
+ }
+ } else {
+ // It's 1
+ euler.x = Math_PI / 2.0;
+ euler.y = Math::atan2(elements[0][2], elements[0][0]);
+ euler.z = 0;
+ }
+ return euler;
+}
+
+void Basis::set_euler_zxy(const Vector3 &p_euler) {
+ real_t c, s;
+
+ c = Math::cos(p_euler.x);
+ s = Math::sin(p_euler.x);
+ Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
+
+ c = Math::cos(p_euler.y);
+ s = Math::sin(p_euler.y);
+ Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
+
+ c = Math::cos(p_euler.z);
+ s = Math::sin(p_euler.z);
+ Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
+
+ *this = zmat * xmat * ymat;
+}
+
+Vector3 Basis::get_euler_zyx() const {
+ // Euler angles in ZYX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy
+ // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx
+ // -sy cy*sx cy*cx
+ Vector3 euler;
+ real_t sy = elements[2][0];
+ if (sy < (1.0 - CMP_EPSILON)) {
+ if (sy > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::atan2(elements[2][1], elements[2][2]);
+ euler.y = Math::asin(-sy);
+ euler.z = Math::atan2(elements[1][0], elements[0][0]);
+ } else {
+ // It's -1
+ euler.x = 0;
+ euler.y = Math_PI / 2.0;
+ euler.z = -Math::atan2(elements[0][1], elements[1][1]);
+ }
+ } else {
+ // It's 1
+ euler.x = 0;
+ euler.y = -Math_PI / 2.0;
+ euler.z = -Math::atan2(elements[0][1], elements[1][1]);
+ }
+ return euler;
+}
+
+void Basis::set_euler_zyx(const Vector3 &p_euler) {
+ real_t c, s;
+
+ c = Math::cos(p_euler.x);
+ s = Math::sin(p_euler.x);
+ Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
+
+ c = Math::cos(p_euler.y);
+ s = Math::sin(p_euler.y);
+ Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
+
+ c = Math::cos(p_euler.z);
+ s = Math::sin(p_euler.z);
+ Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
+
+ *this = zmat * ymat * xmat;
+}
+
bool Basis::is_equal_approx(const Basis &p_basis) const {
return elements[0].is_equal_approx(p_basis.elements[0]) && elements[1].is_equal_approx(p_basis.elements[1]) && elements[2].is_equal_approx(p_basis.elements[2]);
}
diff --git a/core/math/basis.h b/core/math/basis.h
index d870a6b099..985fb0e44f 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -88,9 +88,22 @@ public:
Vector3 get_euler_xyz() const;
void set_euler_xyz(const Vector3 &p_euler);
+
+ Vector3 get_euler_xzy() const;
+ void set_euler_xzy(const Vector3 &p_euler);
+
+ Vector3 get_euler_yzx() const;
+ void set_euler_yzx(const Vector3 &p_euler);
+
Vector3 get_euler_yxz() const;
void set_euler_yxz(const Vector3 &p_euler);
+ Vector3 get_euler_zxy() const;
+ void set_euler_zxy(const Vector3 &p_euler);
+
+ Vector3 get_euler_zyx() const;
+ void set_euler_zyx(const Vector3 &p_euler);
+
Quat get_quat() const;
void set_quat(const Quat &p_quat);
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 56755bcf51..c842be333c 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -83,15 +83,7 @@ uint64_t OS::get_splash_tick_msec() const {
return _msec_splash;
}
-uint64_t OS::get_unix_time() const {
- return 0;
-}
-
-uint64_t OS::get_system_time_secs() const {
- return 0;
-}
-
-uint64_t OS::get_system_time_msecs() const {
+double OS::get_unix_time() const {
return 0;
}
diff --git a/core/os/os.h b/core/os/os.h
index 9ca034a01c..04e10518dc 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -209,9 +209,7 @@ public:
virtual Time get_time(bool local = false) const = 0;
virtual TimeZoneInfo get_time_zone_info() const = 0;
virtual String get_iso_date_time(bool local = false) const;
- virtual uint64_t get_unix_time() const;
- virtual uint64_t get_system_time_secs() const;
- virtual uint64_t get_system_time_msecs() const;
+ virtual double get_unix_time() const;
virtual void delay_usec(uint32_t p_usec) const = 0;
virtual uint64_t get_ticks_usec() const = 0;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index f1b2a1547d..a8beac1e44 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -923,6 +923,18 @@ struct _VariantCall {
VCALL_PTR1R(Basis, scaled);
VCALL_PTR0R(Basis, get_scale);
VCALL_PTR0R(Basis, get_euler);
+ VCALL_PTR0R(Basis, get_euler_xyz);
+ VCALL_PTR1(Basis, set_euler_xyz);
+ VCALL_PTR0R(Basis, get_euler_xzy);
+ VCALL_PTR1(Basis, set_euler_xzy);
+ VCALL_PTR0R(Basis, get_euler_yzx);
+ VCALL_PTR1(Basis, set_euler_yzx);
+ VCALL_PTR0R(Basis, get_euler_yxz);
+ VCALL_PTR1(Basis, set_euler_yxz);
+ VCALL_PTR0R(Basis, get_euler_zxy);
+ VCALL_PTR1(Basis, set_euler_zxy);
+ VCALL_PTR0R(Basis, get_euler_zyx);
+ VCALL_PTR1(Basis, set_euler_zyx);
VCALL_PTR1R(Basis, tdotx);
VCALL_PTR1R(Basis, tdoty);
VCALL_PTR1R(Basis, tdotz);
diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml
index da93d7fb53..842c69de27 100644
--- a/doc/classes/InputMap.xml
+++ b/doc/classes/InputMap.xml
@@ -95,7 +95,7 @@
Returns [code]true[/code] if the given event is part of an existing action. This method ignores keyboard modifiers if the given [InputEvent] is not pressed (for proper release detection). See [method action_has_event] if you don't want this behavior.
</description>
</method>
- <method name="get_action_list">
+ <method name="action_get_events">
<return type="Array">
</return>
<argument index="0" name="action" type="StringName">
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 03203e2ebb..b131d2728c 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -325,7 +325,7 @@
</description>
</method>
<method name="get_unix_time" qualifiers="const">
- <return type="int">
+ <return type="float">
</return>
<description>
Returns the current UNIX epoch timestamp.
diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml
index 6410626496..0889c238dc 100644
--- a/doc/classes/PhysicsMaterial.xml
+++ b/doc/classes/PhysicsMaterial.xml
@@ -12,6 +12,7 @@
</methods>
<members>
<member name="absorbent" type="bool" setter="set_absorbent" getter="is_absorbent" default="false">
+ If [code]true[/code], subtracts the bounciness from the colliding object's bounciness instead of adding it.
</member>
<member name="bounce" type="float" setter="set_bounce" getter="get_bounce" default="0.0">
The body's bounciness. Values range from [code]0[/code] (no bounce) to [code]1[/code] (full bounciness).
@@ -20,6 +21,7 @@
The body's friction. Values range from [code]0[/code] (frictionless) to [code]1[/code] (maximum friction).
</member>
<member name="rough" type="bool" setter="set_rough" getter="is_rough" default="false">
+ If [code]true[/code], the physics engine will use the friction of the object marked as "rough" when two objects collide. If [code]false[/code], the physics engine will use the lowest friction of all colliding objects instead. If [code]true[/code] for both colliding objects, the physics engine will use the highest friction.
</member>
</members>
<constants>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 640cbe84f5..183fd5396f 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -31,6 +31,16 @@
[i]Deprecated soon.[/i]
</description>
</method>
+ <method name="bone_transform_to_world_transform">
+ <return type="Transform">
+ </return>
+ <argument index="0" name="bone_transform" type="Transform">
+ </argument>
+ <description>
+ Takes the given bone pose/transform and converts it to a world transform, relative to the [Skeleton3D] node.
+ This is useful for using the bone transform in calculations with transforms from [Node3D]-based nodes.
+ </description>
+ </method>
<method name="clear_bones">
<return type="void">
</return>
@@ -42,6 +52,7 @@
<return type="void">
</return>
<description>
+ Removes the global pose override on all bones in the skeleton.
</description>
</method>
<method name="find_bone" qualifiers="const">
@@ -136,12 +147,14 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
+ Returns whether the bone rest for the bone at [code]bone_idx[/code] is disabled.
</description>
</method>
<method name="localize_rests">
<return type="void">
</return>
<description>
+ Returns all bones in the skeleton to their rest poses.
</description>
</method>
<method name="physical_bones_add_collision_exception">
@@ -150,6 +163,8 @@
<argument index="0" name="exception" type="RID">
</argument>
<description>
+ Adds a collision exception to the physical bone.
+ Works just like the [RigidBody3D] node.
</description>
</method>
<method name="physical_bones_remove_collision_exception">
@@ -158,6 +173,8 @@
<argument index="0" name="exception" type="RID">
</argument>
<description>
+ Removes a collision exception to the physical bone.
+ Works just like the [RigidBody3D] node.
</description>
</method>
<method name="physical_bones_start_simulation">
@@ -166,12 +183,15 @@
<argument index="0" name="bones" type="StringName[]" default="[ ]">
</argument>
<description>
+ Tells the [PhysicalBone3D] nodes in the Skeleton to start simulating and reacting to the physics world.
+ Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated.
</description>
</method>
<method name="physical_bones_stop_simulation">
<return type="void">
</return>
<description>
+ Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating.
</description>
</method>
<method name="register_skin">
@@ -180,6 +200,7 @@
<argument index="0" name="skin" type="Skin">
</argument>
<description>
+ Binds the given Skin to the Skeleton.
</description>
</method>
<method name="set_bone_custom_pose">
@@ -190,6 +211,8 @@
<argument index="1" name="custom_pose" type="Transform">
</argument>
<description>
+ Sets the custom pose transform, [code]custom_pose[/code], for the bone at [code]bone_idx[/code]. This pose is an addition to the bone rest pose.
+ [b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
</description>
</method>
<method name="set_bone_disable_rest">
@@ -200,6 +223,7 @@
<argument index="1" name="disable" type="bool">
</argument>
<description>
+ Disables the rest pose for the bone at [code]bone_idx[/code] if [code]true[/code], enables the bone rest if [code]false[/code].
</description>
</method>
<method name="set_bone_global_pose_override">
@@ -214,6 +238,9 @@
<argument index="3" name="persistent" type="bool" default="false">
</argument>
<description>
+ Sets the global pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code].
+ [code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain.
+ [b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
</description>
</method>
<method name="set_bone_parent">
@@ -237,6 +264,7 @@
</argument>
<description>
Returns the pose transform for bone [code]bone_idx[/code].
+ [b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.
</description>
</method>
<method name="set_bone_rest">
@@ -267,6 +295,17 @@
<argument index="0" name="bone_idx" type="int">
</argument>
<description>
+ Unparents the bone at [code]bone_idx[/code] and sets its rest position to that of it's parent prior to being reset.
+ </description>
+ </method>
+ <method name="world_transform_to_bone_transform">
+ <return type="Transform">
+ </return>
+ <argument index="0" name="world_transform" type="Transform">
+ </argument>
+ <description>
+ Takes the given world transform, relative to the [Skeleton3D], and converts it to a bone pose/transform.
+ This is useful for using setting bone poses using transforms from [Node3D]-based nodes.
</description>
</method>
</methods>
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 083cd64116..9a5fc6d1a4 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -163,21 +163,11 @@ String OS_Unix::get_name() const {
return "Unix";
}
-uint64_t OS_Unix::get_unix_time() const {
- return time(nullptr);
-};
-
-uint64_t OS_Unix::get_system_time_secs() const {
- struct timeval tv_now;
- gettimeofday(&tv_now, nullptr);
- return uint64_t(tv_now.tv_sec);
-}
-
-uint64_t OS_Unix::get_system_time_msecs() const {
+double OS_Unix::get_unix_time() const {
struct timeval tv_now;
gettimeofday(&tv_now, nullptr);
- return uint64_t(tv_now.tv_sec) * 1000 + uint64_t(tv_now.tv_usec) / 1000;
-}
+ return (double)tv_now.tv_sec + double(tv_now.tv_usec) / 1000000;
+};
OS::Date OS_Unix::get_date(bool utc) const {
time_t t = time(nullptr);
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 7d235803dc..2982e0c55c 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -77,9 +77,7 @@ public:
virtual Time get_time(bool utc) const;
virtual TimeZoneInfo get_time_zone_info() const;
- virtual uint64_t get_unix_time() const;
- virtual uint64_t get_system_time_secs() const;
- virtual uint64_t get_system_time_msecs() const;
+ virtual double get_unix_time() const;
virtual void delay_usec(uint32_t p_usec) const;
virtual uint64_t get_ticks_usec() const;
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index bad2203423..f50ac6d580 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -243,6 +243,9 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
if (p_overview) {
class_desc->push_cell();
class_desc->push_align(RichTextLabel::ALIGN_RIGHT);
+ } else {
+ static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 };
+ class_desc->add_text(String(prefix));
}
_add_type(p_method.return_type, p_method.return_enum);
@@ -378,7 +381,6 @@ void EditorHelp::_update_doc() {
class_desc->push_color(title_color);
class_desc->push_font(doc_font);
class_desc->add_text(TTR("Inherits:") + " ");
- class_desc->pop();
String inherits = cd.inherits;
@@ -393,6 +395,7 @@ void EditorHelp::_update_doc() {
}
class_desc->pop();
+ class_desc->pop();
class_desc->add_newline();
}
@@ -401,13 +404,12 @@ void EditorHelp::_update_doc() {
bool found = false;
bool prev = false;
+ class_desc->push_font(doc_font);
for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
if (E->get().inherits == cd.name) {
if (!found) {
class_desc->push_color(title_color);
- class_desc->push_font(doc_font);
class_desc->add_text(TTR("Inherited by:") + " ");
- class_desc->pop();
found = true;
}
@@ -419,6 +421,7 @@ void EditorHelp::_update_doc() {
prev = true;
}
}
+ class_desc->pop();
if (found) {
class_desc->pop();
@@ -758,6 +761,8 @@ void EditorHelp::_update_doc() {
signal_line[cd.signals[i].name] = class_desc->get_line_count() - 2; //gets overridden if description
class_desc->push_font(doc_code_font); // monofont
class_desc->push_color(headline_color);
+ static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 };
+ class_desc->add_text(String(prefix));
_add_text(cd.signals[i].name);
class_desc->pop();
class_desc->push_color(symbol_color);
@@ -835,10 +840,10 @@ void EditorHelp::_update_doc() {
for (Map<String, Vector<DocData::ConstantDoc>>::Element *E = enums.front(); E; E = E->next()) {
enum_line[E->key()] = class_desc->get_line_count() - 2;
+ class_desc->push_font(doc_code_font);
class_desc->push_color(title_color);
class_desc->add_text("enum ");
class_desc->pop();
- class_desc->push_font(doc_code_font);
String e = E->key();
if ((e.get_slice_count(".") > 1) && (e.get_slice(".", 0) == edited_class)) {
e = e.get_slice(".", 1);
@@ -851,6 +856,8 @@ void EditorHelp::_update_doc() {
class_desc->push_color(symbol_color);
class_desc->add_text(":");
class_desc->pop();
+
+ class_desc->add_newline();
class_desc->add_newline();
class_desc->push_indent(1);
@@ -869,6 +876,8 @@ void EditorHelp::_update_doc() {
class_desc->push_font(doc_code_font);
class_desc->push_color(headline_color);
+ static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 };
+ class_desc->add_text(String(prefix));
_add_text(enum_list[i].name);
class_desc->pop();
class_desc->push_color(symbol_color);
@@ -880,14 +889,15 @@ void EditorHelp::_update_doc() {
class_desc->pop();
if (enum_list[i].description != "") {
class_desc->push_font(doc_font);
- //class_desc->add_text(" ");
- class_desc->push_indent(1);
class_desc->push_color(comment_color);
+ static const CharType dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 };
+ class_desc->add_text(String(dash));
_add_text(DTR(enum_list[i].description));
class_desc->pop();
class_desc->pop();
- class_desc->pop(); // indent
- class_desc->add_newline();
+ if (DTR(enum_list[i].description).find("\n") > 0) {
+ class_desc->add_newline();
+ }
}
class_desc->add_newline();
@@ -931,6 +941,9 @@ void EditorHelp::_update_doc() {
class_desc->add_text(String(prefix));
class_desc->pop();
}
+ } else {
+ static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 };
+ class_desc->add_text(String(prefix));
}
class_desc->push_color(headline_color);
@@ -946,13 +959,15 @@ void EditorHelp::_update_doc() {
class_desc->pop();
if (constants[i].description != "") {
class_desc->push_font(doc_font);
- class_desc->push_indent(1);
class_desc->push_color(comment_color);
+ static const CharType dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 };
+ class_desc->add_text(String(dash));
_add_text(DTR(constants[i].description));
class_desc->pop();
class_desc->pop();
- class_desc->pop(); // indent
- class_desc->add_newline();
+ if (DTR(constants[i].description).find("\n") > 0) {
+ class_desc->add_newline();
+ }
}
class_desc->add_newline();
@@ -987,6 +1002,9 @@ void EditorHelp::_update_doc() {
class_desc->push_cell();
class_desc->push_font(doc_code_font);
+ static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 };
+ class_desc->add_text(String(prefix));
+
_add_type(cd.properties[i].type, cd.properties[i].enumeration);
class_desc->add_text(" ");
class_desc->pop(); // font
@@ -1015,6 +1033,11 @@ void EditorHelp::_update_doc() {
class_desc->pop(); // font
class_desc->pop(); // cell
+ Map<String, DocData::MethodDoc> method_map;
+ for (int j = 0; j < methods.size(); j++) {
+ method_map[methods[j].name] = methods[j];
+ }
+
if (cd.properties[i].setter != "") {
class_desc->push_cell();
class_desc->pop(); // cell
@@ -1022,7 +1045,14 @@ void EditorHelp::_update_doc() {
class_desc->push_cell();
class_desc->push_font(doc_code_font);
class_desc->push_color(text_color);
- class_desc->add_text(cd.properties[i].setter + TTR("(value)"));
+ if (method_map[cd.properties[i].setter].arguments.size() > 1) {
+ // Setters with additional arguments are exposed in the method list, so we link them here for quick access.
+ class_desc->push_meta("@method " + cd.properties[i].setter);
+ class_desc->add_text(cd.properties[i].setter + TTR("(value)"));
+ class_desc->pop();
+ } else {
+ class_desc->add_text(cd.properties[i].setter + TTR("(value)"));
+ }
class_desc->pop(); // color
class_desc->push_color(comment_color);
class_desc->add_text(" setter");
@@ -1039,7 +1069,14 @@ void EditorHelp::_update_doc() {
class_desc->push_cell();
class_desc->push_font(doc_code_font);
class_desc->push_color(text_color);
- class_desc->add_text(cd.properties[i].getter + "()");
+ if (method_map[cd.properties[i].getter].arguments.size() > 0) {
+ // Getters with additional arguments are exposed in the method list, so we link them here for quick access.
+ class_desc->push_meta("@method " + cd.properties[i].getter);
+ class_desc->add_text(cd.properties[i].getter + "()");
+ class_desc->pop();
+ } else {
+ class_desc->add_text(cd.properties[i].getter + "()");
+ }
class_desc->pop(); //color
class_desc->push_color(comment_color);
class_desc->add_text(" getter");
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index cfa0c7a063..74267452e6 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -1283,14 +1283,25 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) {
}
void EditorPropertyVector3::update_property() {
- Vector3 val = get_edited_object()->get(get_edited_property());
+ update_using_vector(get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyVector3::update_using_vector(Vector3 p_vector) {
setting = true;
- spin[0]->set_value(val.x);
- spin[1]->set_value(val.y);
- spin[2]->set_value(val.z);
+ spin[0]->set_value(p_vector.x);
+ spin[1]->set_value(p_vector.y);
+ spin[2]->set_value(p_vector.z);
setting = false;
}
+Vector3 EditorPropertyVector3::get_vector() {
+ Vector3 v3;
+ v3.x = spin[0]->get_value();
+ v3.y = spin[1]->get_value();
+ v3.z = spin[2]->get_value();
+ return v3;
+}
+
void EditorPropertyVector3::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
Color base = get_theme_color("accent_color", "Editor");
@@ -1434,7 +1445,7 @@ EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) {
setting = false;
}
-///////////////////// RECT2 /////////////////////////
+///////////////////// RECT2i /////////////////////////
void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -1520,7 +1531,7 @@ EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {
setting = false;
}
-///////////////////// VECTOR3 /////////////////////////
+///////////////////// VECTOR3i /////////////////////////
void EditorPropertyVector3i::_value_changed(double val, const String &p_name) {
if (setting) {
@@ -2029,21 +2040,23 @@ void EditorPropertyTransform::_value_changed(double val, const String &p_name) {
}
void EditorPropertyTransform::update_property() {
- Transform val = get_edited_object()->get(get_edited_property());
- setting = true;
- spin[0]->set_value(val.basis[0][0]);
- spin[1]->set_value(val.basis[1][0]);
- spin[2]->set_value(val.basis[2][0]);
- spin[3]->set_value(val.basis[0][1]);
- spin[4]->set_value(val.basis[1][1]);
- spin[5]->set_value(val.basis[2][1]);
- spin[6]->set_value(val.basis[0][2]);
- spin[7]->set_value(val.basis[1][2]);
- spin[8]->set_value(val.basis[2][2]);
- spin[9]->set_value(val.origin[0]);
- spin[10]->set_value(val.origin[1]);
- spin[11]->set_value(val.origin[2]);
+ update_using_transform(get_edited_object()->get(get_edited_property()));
+}
+void EditorPropertyTransform::update_using_transform(Transform p_transform) {
+ setting = true;
+ spin[0]->set_value(p_transform.basis[0][0]);
+ spin[1]->set_value(p_transform.basis[1][0]);
+ spin[2]->set_value(p_transform.basis[2][0]);
+ spin[3]->set_value(p_transform.basis[0][1]);
+ spin[4]->set_value(p_transform.basis[1][1]);
+ spin[5]->set_value(p_transform.basis[2][1]);
+ spin[6]->set_value(p_transform.basis[0][2]);
+ spin[7]->set_value(p_transform.basis[1][2]);
+ spin[8]->set_value(p_transform.basis[2][2]);
+ spin[9]->set_value(p_transform.origin[0]);
+ spin[10]->set_value(p_transform.origin[1]);
+ spin[11]->set_value(p_transform.origin[2]);
setting = false;
}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 61c11f4534..35e6c10d90 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -392,6 +392,8 @@ protected:
public:
virtual void update_property();
+ virtual void update_using_vector(Vector3 p_vector);
+ virtual Vector3 get_vector();
void setup(double p_min, double p_max, double p_step, bool p_no_slider);
EditorPropertyVector3(bool p_force_wide = false);
};
@@ -536,6 +538,7 @@ protected:
public:
virtual void update_property();
+ virtual void update_using_transform(Transform p_transform);
void setup(double p_min, double p_max, double p_step, bool p_no_slider);
EditorPropertyTransform();
};
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 2586f17fe1..52da8dea19 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -62,125 +62,56 @@ void BoneTransformEditor::create_editors() {
enabled_checkbox->set_visible(toggle_enabled);
section->get_vbox()->add_child(enabled_checkbox);
- Label *l1 = memnew(Label(TTR("Translation")));
- section->get_vbox()->add_child(l1);
-
- translation_grid = memnew(GridContainer());
- translation_grid->set_columns(TRANSLATION_COMPONENTS);
- section->get_vbox()->add_child(translation_grid);
-
- Label *l2 = memnew(Label(TTR("Rotation Degrees")));
- section->get_vbox()->add_child(l2);
-
- rotation_grid = memnew(GridContainer());
- rotation_grid->set_columns(ROTATION_DEGREES_COMPONENTS);
- section->get_vbox()->add_child(rotation_grid);
-
- Label *l3 = memnew(Label(TTR("Scale")));
- section->get_vbox()->add_child(l3);
-
- scale_grid = memnew(GridContainer());
- scale_grid->set_columns(SCALE_COMPONENTS);
- section->get_vbox()->add_child(scale_grid);
-
- Label *l4 = memnew(Label(TTR("Transform")));
- section->get_vbox()->add_child(l4);
-
- transform_grid = memnew(GridContainer());
- transform_grid->set_columns(TRANSFORM_CONTROL_COMPONENTS);
- section->get_vbox()->add_child(transform_grid);
-
- static const char *desc[TRANSFORM_COMPONENTS] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" };
-
- for (int i = 0; i < TRANSFORM_CONTROL_COMPONENTS; ++i) {
- translation_slider[i] = memnew(EditorSpinSlider());
- translation_slider[i]->set_label(desc[i]);
- setup_spinner(translation_slider[i], false);
- translation_grid->add_child(translation_slider[i]);
-
- rotation_slider[i] = memnew(EditorSpinSlider());
- rotation_slider[i]->set_label(desc[i]);
- setup_spinner(rotation_slider[i], false);
- rotation_grid->add_child(rotation_slider[i]);
-
- scale_slider[i] = memnew(EditorSpinSlider());
- scale_slider[i]->set_label(desc[i]);
- setup_spinner(scale_slider[i], false);
- scale_grid->add_child(scale_slider[i]);
- }
-
- for (int i = 0; i < TRANSFORM_COMPONENTS; ++i) {
- transform_slider[i] = memnew(EditorSpinSlider());
- transform_slider[i]->set_label(desc[i]);
- setup_spinner(transform_slider[i], true);
- transform_grid->add_child(transform_slider[i]);
- }
-}
-
-void BoneTransformEditor::setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner) {
- spinner->set_flat(true);
- spinner->set_min(-10000);
- spinner->set_max(10000);
- spinner->set_step(0.001f);
- spinner->set_hide_slider(true);
- spinner->set_allow_greater(true);
- spinner->set_allow_lesser(true);
- spinner->set_h_size_flags(SIZE_EXPAND_FILL);
-
- spinner->connect_compat("value_changed", this, "_value_changed", varray(is_transform_spinner));
+ // Translation property
+ translation_property = memnew(EditorPropertyVector3());
+ translation_property->setup(-10000, 10000, 0.001f, true);
+ translation_property->set_label("Translation");
+ translation_property->set_use_folding(true);
+ translation_property->set_read_only(false);
+ translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ section->get_vbox()->add_child(translation_property);
+
+ // Rotation property
+ rotation_property = memnew(EditorPropertyVector3());
+ rotation_property->setup(-10000, 10000, 0.001f, true);
+ rotation_property->set_label("Rotation Degrees");
+ rotation_property->set_use_folding(true);
+ rotation_property->set_read_only(false);
+ rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ section->get_vbox()->add_child(rotation_property);
+
+ // Scale property
+ scale_property = memnew(EditorPropertyVector3());
+ scale_property->setup(-10000, 10000, 0.001f, true);
+ scale_property->set_label("Scale");
+ scale_property->set_use_folding(true);
+ scale_property->set_read_only(false);
+ scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ section->get_vbox()->add_child(scale_property);
+
+ // Transform/Matrix section
+ transform_section = memnew(EditorInspectorSection);
+ transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true);
+ section->get_vbox()->add_child(transform_section);
+
+ // Transform/Matrix property
+ transform_property = memnew(EditorPropertyTransform());
+ transform_property->setup(-10000, 10000, 0.001f, true);
+ transform_property->set_label("Transform");
+ transform_property->set_use_folding(true);
+ transform_property->set_read_only(false);
+ transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform));
+ transform_section->get_vbox()->add_child(transform_property);
}
void BoneTransformEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
create_editors();
- key_button->connect_compat("pressed", this, "_key_button_pressed");
- enabled_checkbox->connect_compat("toggled", this, "_checkbox_toggled");
+ key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed));
+ enabled_checkbox->connect("toggled", callable_mp(this, &BoneTransformEditor::_checkbox_toggled));
[[fallthrough]];
}
- case NOTIFICATION_THEME_CHANGED: {
- const Color base = get_theme_color("accent_color", "Editor");
- const Color bg_color = get_theme_color("property_color", "Editor");
- const Color bg_lbl_color(bg_color.r, bg_color.g, bg_color.b, 0.5);
-
- for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
- Color c = base;
- c.set_hsv(float(i % TRANSLATION_COMPONENTS) / TRANSLATION_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
- if (!translation_slider[i]) {
- continue;
- }
- translation_slider[i]->set_custom_label_color(true, c);
- }
-
- for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
- Color c = base;
- c.set_hsv(float(i % ROTATION_DEGREES_COMPONENTS) / ROTATION_DEGREES_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
- if (!rotation_slider[i]) {
- continue;
- }
- rotation_slider[i]->set_custom_label_color(true, c);
- }
-
- for (int i = 0; i < SCALE_COMPONENTS; i++) {
- Color c = base;
- c.set_hsv(float(i % SCALE_COMPONENTS) / SCALE_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
- if (!scale_slider[i]) {
- continue;
- }
- scale_slider[i]->set_custom_label_color(true, c);
- }
-
- for (int i = 0; i < TRANSFORM_COMPONENTS; i++) {
- Color c = base;
- c.set_hsv(float(i % TRANSFORM_COMPONENTS) / TRANSFORM_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
- if (!transform_slider[i]) {
- continue;
- }
- transform_slider[i]->set_custom_label_color(true, c);
- }
-
- break;
- }
case NOTIFICATION_SORT_CHILDREN: {
const Ref<Font> font = get_theme_font("font", "Tree");
@@ -189,8 +120,8 @@ void BoneTransformEditor::_notification(int p_what) {
buffer.y += font->get_height();
buffer.y += get_theme_constant("vseparation", "Tree");
- const float vector_height = translation_grid->get_size().y;
- const float transform_height = transform_grid->get_size().y;
+ const float vector_height = translation_property->get_size().y;
+ const float transform_height = transform_property->get_size().y;
const float button_height = key_button->get_size().y;
const float width = get_size().x - get_theme_constant("inspector_margin", "Editor");
@@ -202,10 +133,10 @@ void BoneTransformEditor::_notification(int p_what) {
}
if (section->get_vbox()->is_visible()) {
- input_rects.push_back(Rect2(translation_grid->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(rotation_grid->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(scale_grid->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(transform_grid->get_position() + buffer, Size2(width, transform_height)));
+ input_rects.push_back(Rect2(translation_property->get_position() + buffer, Size2(width, vector_height)));
+ input_rects.push_back(Rect2(rotation_property->get_position() + buffer, Size2(width, vector_height)));
+ input_rects.push_back(Rect2(scale_property->get_position() + buffer, Size2(width, vector_height)));
+ input_rects.push_back(Rect2(transform_property->get_position() + buffer, Size2(width, transform_height)));
} else {
const int32_t start = input_rects.size();
const int32_t empty_input_rect_elements = 4;
@@ -234,49 +165,53 @@ void BoneTransformEditor::_notification(int p_what) {
}
}
-void BoneTransformEditor::_value_changed(const double p_value, const bool p_from_transform) {
+void BoneTransformEditor::_value_changed(const double p_value) {
if (updating)
return;
- if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
- const Transform tform = compute_transform(p_from_transform);
+ Transform tform = compute_transform_from_vector3s();
+ _change_transform(tform);
+}
+void BoneTransformEditor::_value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean) {
+ if (updating)
+ return;
+ Transform tform = compute_transform_from_vector3s();
+ _change_transform(tform);
+}
+
+Transform BoneTransformEditor::compute_transform_from_vector3s() const {
+ // Convert rotation from degrees to radians.
+ Vector3 prop_rotation = rotation_property->get_vector();
+ prop_rotation.x = Math::deg2rad(prop_rotation.x);
+ prop_rotation.y = Math::deg2rad(prop_rotation.y);
+ prop_rotation.z = Math::deg2rad(prop_rotation.z);
+
+ return Transform(
+ Basis(prop_rotation, scale_property->get_vector()),
+ translation_property->get_vector());
+}
+
+void BoneTransformEditor::_value_changed_transform(const String p_property_name, const Transform p_transform, const StringName p_edited_property_name, const bool p_boolean) {
+ if (updating)
+ return;
+ _change_transform(p_transform);
+}
+
+void BoneTransformEditor::_change_transform(Transform p_new_transform) {
+ if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);
undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int()));
- undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), tform);
+ undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform);
undo_redo->commit_action();
} else if (property.get_slicec('/', 0) == "bones") {
- const Transform tform = compute_transform(p_from_transform);
-
undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
undo_redo->add_undo_property(skeleton, property, skeleton->get(property));
- undo_redo->add_do_property(skeleton, property, tform);
+ undo_redo->add_do_property(skeleton, property, p_new_transform);
undo_redo->commit_action();
}
}
-Transform BoneTransformEditor::compute_transform(const bool p_from_transform) const {
- // Last modified was a raw transform column...
- if (p_from_transform) {
- Transform tform;
-
- for (int i = 0; i < BASIS_COMPONENTS; ++i) {
- tform.basis[i / BASIS_SPLIT_COMPONENTS][i % BASIS_SPLIT_COMPONENTS] = transform_slider[i]->get_value();
- }
-
- for (int i = 0; i < TRANSLATION_COMPONENTS; ++i) {
- tform.origin[i] = transform_slider[i + BASIS_COMPONENTS]->get_value();
- }
-
- return tform;
- }
-
- return Transform(
- Basis(Vector3(Math::deg2rad(rotation_slider[0]->get_value()), Math::deg2rad(rotation_slider[1]->get_value()), Math::deg2rad(rotation_slider[2]->get_value())),
- Vector3(scale_slider[0]->get_value(), scale_slider[1]->get_value(), scale_slider[2]->get_value())),
- Vector3(translation_slider[0]->get_value(), translation_slider[1]->get_value(), translation_slider[2]->get_value()));
-}
-
void BoneTransformEditor::update_enabled_checkbox() {
if (enabled_checkbox) {
const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
@@ -285,12 +220,6 @@ void BoneTransformEditor::update_enabled_checkbox() {
}
}
-void BoneTransformEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_value_changed", "value"), &BoneTransformEditor::_value_changed);
- ClassDB::bind_method(D_METHOD("_key_button_pressed"), &BoneTransformEditor::_key_button_pressed);
- ClassDB::bind_method(D_METHOD("_checkbox_toggled", "toggled"), &BoneTransformEditor::_checkbox_toggled);
-}
-
void BoneTransformEditor::_update_properties() {
if (updating)
return;
@@ -318,47 +247,22 @@ void BoneTransformEditor::_update_custom_pose_properties() {
}
void BoneTransformEditor::_update_transform_properties(Transform tform) {
- Quat rot = tform.get_basis();
- Vector3 rot_rad = rot.get_euler();
- Vector3 rot_degrees = Vector3(Math::rad2deg(rot_rad.x), Math::rad2deg(rot_rad.y), Math::rad2deg(rot_rad.z));
- Vector3 tr = tform.get_origin();
+ Basis rotation_basis = tform.get_basis();
+ Vector3 rotation_radians = rotation_basis.get_rotation_euler();
+ Vector3 rotation_degrees = Vector3(Math::rad2deg(rotation_radians.x), Math::rad2deg(rotation_radians.y), Math::rad2deg(rotation_radians.z));
+ Vector3 translation = tform.get_origin();
Vector3 scale = tform.basis.get_scale();
- for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
- translation_slider[i]->set_value(tr[i]);
- }
-
- for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
- rotation_slider[i]->set_value(rot_degrees[i]);
- }
-
- for (int i = 0; i < SCALE_COMPONENTS; i++) {
- scale_slider[i]->set_value(scale[i]);
- }
-
- transform_slider[0]->set_value(tform.get_basis()[Vector3::AXIS_X].x);
- transform_slider[1]->set_value(tform.get_basis()[Vector3::AXIS_X].y);
- transform_slider[2]->set_value(tform.get_basis()[Vector3::AXIS_X].z);
- transform_slider[3]->set_value(tform.get_basis()[Vector3::AXIS_Y].x);
- transform_slider[4]->set_value(tform.get_basis()[Vector3::AXIS_Y].y);
- transform_slider[5]->set_value(tform.get_basis()[Vector3::AXIS_Y].z);
- transform_slider[6]->set_value(tform.get_basis()[Vector3::AXIS_Z].x);
- transform_slider[7]->set_value(tform.get_basis()[Vector3::AXIS_Z].y);
- transform_slider[8]->set_value(tform.get_basis()[Vector3::AXIS_Z].z);
-
- for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
- transform_slider[BASIS_COMPONENTS + i]->set_value(tform.get_origin()[i]);
- }
+ translation_property->update_using_vector(translation);
+ rotation_property->update_using_vector(rotation_degrees);
+ scale_property->update_using_vector(scale);
+ transform_property->update_using_transform(tform);
update_enabled_checkbox();
updating = false;
}
BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
- translation_slider(),
- rotation_slider(),
- scale_slider(),
- transform_slider(),
skeleton(p_skeleton),
key_button(nullptr),
enabled_checkbox(nullptr),
@@ -397,7 +301,7 @@ void BoneTransformEditor::_key_button_pressed() {
return;
// Need to normalize the basis before you key it
- Transform tform = compute_transform(true);
+ Transform tform = compute_transform_from_vector3s();
tform.orthonormalize();
AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
}
@@ -627,8 +531,7 @@ void Skeleton3DEditor::update_joint_tree() {
items.insert(-1, root);
const Vector<int> &joint_porder = skeleton->get_bone_process_orders();
-
- Ref<Texture> bone_icon = get_theme_icon("Skeleton3D", "EditorIcons");
+ Ref<Texture> bone_icon = get_theme_icon("BoneAttachment3D", "EditorIcons");
for (int i = 0; i < joint_porder.size(); ++i) {
const int b_idx = joint_porder[i];
@@ -714,11 +617,11 @@ void Skeleton3DEditor::_notification(int p_what) {
update_joint_tree();
update_editors();
- get_tree()->connect_compat("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT);
- joint_tree->connect_compat("item_selected", this, "_joint_tree_selection_changed");
- joint_tree->connect_compat("item_rmb_selected", this, "_joint_tree_rmb_select");
+ get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
+ joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));
+ joint_tree->connect("item_rmb_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));
#ifdef TOOLS_ENABLED
- skeleton->connect_compat("pose_updated", this, "_update_properties");
+ skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
#endif // TOOLS_ENABLED
break;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index 8b0639ed92..a5b8ce80a9 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -41,30 +41,19 @@ class PhysicalBone3D;
class Skeleton3DEditorPlugin;
class Button;
class CheckBox;
+class EditorPropertyTransform;
+class EditorPropertyVector3;
class BoneTransformEditor : public VBoxContainer {
GDCLASS(BoneTransformEditor, VBoxContainer);
- static const int32_t TRANSLATION_COMPONENTS = 3;
- static const int32_t ROTATION_DEGREES_COMPONENTS = 3;
- static const int32_t SCALE_COMPONENTS = 3;
- static const int32_t BASIS_COMPONENTS = 9;
- static const int32_t BASIS_SPLIT_COMPONENTS = 3;
- static const int32_t TRANSFORM_COMPONENTS = 12;
- static const int32_t TRANSFORM_SPLIT_COMPONENTS = 3;
- static const int32_t TRANSFORM_CONTROL_COMPONENTS = 3;
-
EditorInspectorSection *section;
- GridContainer *translation_grid;
- GridContainer *rotation_grid;
- GridContainer *scale_grid;
- GridContainer *transform_grid;
-
- EditorSpinSlider *translation_slider[TRANSLATION_COMPONENTS];
- EditorSpinSlider *rotation_slider[ROTATION_DEGREES_COMPONENTS];
- EditorSpinSlider *scale_slider[SCALE_COMPONENTS];
- EditorSpinSlider *transform_slider[TRANSFORM_COMPONENTS];
+ EditorPropertyVector3 *translation_property;
+ EditorPropertyVector3 *rotation_property;
+ EditorPropertyVector3 *scale_property;
+ EditorInspectorSection *transform_section;
+ EditorPropertyTransform *transform_property;
Rect2 background_rects[5];
@@ -83,17 +72,22 @@ class BoneTransformEditor : public VBoxContainer {
String label;
void create_editors();
- void setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner);
- void _value_changed(const double p_value, const bool p_from_transform);
-
- Transform compute_transform(const bool p_from_transform) const;
+ // Called when one of the EditorSpinSliders are changed.
+ void _value_changed(const double p_value);
+ // Called when the one of the EditorPropertyVector3 are updated.
+ void _value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean);
+ // Called when the transform_property is updated.
+ void _value_changed_transform(const String p_property_name, const Transform p_transform, const StringName p_edited_property_name, const bool p_boolean);
+ // Changes the transform to the given transform and updates the UI accordingly.
+ void _change_transform(Transform p_new_transform);
+ // Creates a Transform using the EditorPropertyVector3 properties.
+ Transform compute_transform_from_vector3s() const;
void update_enabled_checkbox();
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
BoneTransformEditor(Skeleton3D *p_skeleton);
diff --git a/main/main.cpp b/main/main.cpp
index 94dd895a26..5320274add 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -930,6 +930,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
#endif
}
+ // Initialize user data dir.
+ OS::get_singleton()->ensure_user_data_dir();
+
GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60);
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes
GLOBAL_DEF("network/limits/debugger/max_chars_per_second", 32768);
@@ -948,7 +951,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
packed_data->set_disabled(true);
globals->set_disable_feature_overrides(true);
}
-
#endif
GLOBAL_DEF("logging/file_logging/enable_file_logging", false);
@@ -1266,10 +1268,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
Thread::_main_thread_id = p_main_tid_override;
}
- /* Initialize user data dir */
-
- OS::get_singleton()->ensure_user_data_dir();
-
/* Initialize Input */
input = memnew(Input);
diff --git a/main/tests/test_basis.cpp b/main/tests/test_basis.cpp
new file mode 100644
index 0000000000..ac25151fd8
--- /dev/null
+++ b/main/tests/test_basis.cpp
@@ -0,0 +1,325 @@
+/*************************************************************************/
+/* test_fbx.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#include "test_basis.h"
+
+#include "core/math/random_number_generator.h"
+#include "core/os/os.h"
+#include "core/ustring.h"
+
+namespace TestBasis {
+
+enum RotOrder {
+ EulerXYZ,
+ EulerXZY,
+ EulerYZX,
+ EulerYXZ,
+ EulerZXY,
+ EulerZYX
+};
+
+Vector3 deg2rad(const Vector3 &p_rotation) {
+ return p_rotation / 180.0 * Math_PI;
+}
+
+Vector3 rad2deg(const Vector3 &p_rotation) {
+ return p_rotation / Math_PI * 180.0;
+}
+
+Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
+ Basis ret;
+ switch (mode) {
+ case EulerXYZ:
+ ret.set_euler_xyz(p_rotation);
+ break;
+
+ case EulerXZY:
+ ret.set_euler_xzy(p_rotation);
+ break;
+
+ case EulerYZX:
+ ret.set_euler_yzx(p_rotation);
+ break;
+
+ case EulerYXZ:
+ ret.set_euler_yxz(p_rotation);
+ break;
+
+ case EulerZXY:
+ ret.set_euler_zxy(p_rotation);
+ break;
+
+ case EulerZYX:
+ ret.set_euler_zyx(p_rotation);
+ break;
+
+ default:
+ // If you land here, Please integrate all rotation orders.
+ CRASH_NOW_MSG("This is not unreachable.");
+ }
+
+ return ret;
+}
+
+Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) {
+ switch (mode) {
+ case EulerXYZ:
+ return p_rotation.get_euler_xyz();
+
+ case EulerXZY:
+ return p_rotation.get_euler_xzy();
+
+ case EulerYZX:
+ return p_rotation.get_euler_yzx();
+
+ case EulerYXZ:
+ return p_rotation.get_euler_yxz();
+
+ case EulerZXY:
+ return p_rotation.get_euler_zxy();
+
+ case EulerZYX:
+ return p_rotation.get_euler_zyx();
+
+ default:
+ // If you land here, Please integrate all rotation orders.
+ CRASH_NOW_MSG("This is not unreachable.");
+ return Vector3();
+ }
+}
+
+String get_rot_order_name(RotOrder ro) {
+ switch (ro) {
+ case EulerXYZ:
+ return "XYZ";
+ case EulerXZY:
+ return "XZY";
+ case EulerYZX:
+ return "YZX";
+ case EulerYXZ:
+ return "YXZ";
+ case EulerZXY:
+ return "ZXY";
+ case EulerZYX:
+ return "ZYX";
+ default:
+ return "[Not supported]";
+ }
+}
+
+bool test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
+ // This test:
+ // 1. Converts the rotation vector from deg to rad.
+ // 2. Converts euler to basis.
+ // 3. Converts the above basis back into euler.
+ // 4. Converts the above euler into basis again.
+ // 5. Compares the basis obtained in step 2 with the basis of step 4
+ //
+ // The conversion "basis to euler", done in the step 3, may be different from
+ // the original euler, even if the final rotation are the same.
+ // This happens because there are more ways to represents the same rotation,
+ // both valid, using eulers.
+ // For this reason is necessary to convert that euler back to basis and finally
+ // compares it.
+ //
+ // In this way we can assert that both functions: basis to euler / euler to basis
+ // are correct.
+
+ bool pass = true;
+
+ // Euler to rotation
+ const Vector3 original_euler = deg2rad(deg_original_euler);
+ const Basis to_rotation = EulerToBasis(rot_order, original_euler);
+
+ // Euler from rotation
+ const Vector3 euler_from_rotation = BasisToEuler(rot_order, to_rotation);
+ const Basis rotation_from_computed_euler = EulerToBasis(rot_order, euler_from_rotation);
+
+ Basis res = to_rotation.inverse() * rotation_from_computed_euler;
+
+ if ((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() > 0.1) {
+ OS::get_singleton()->print("Fail due to X %ls\n", String(res.get_axis(0)).c_str());
+ pass = false;
+ }
+ if ((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() > 0.1) {
+ OS::get_singleton()->print("Fail due to Y %ls\n", String(res.get_axis(1)).c_str());
+ pass = false;
+ }
+ if ((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() > 0.1) {
+ OS::get_singleton()->print("Fail due to Z %ls\n", String(res.get_axis(2)).c_str());
+ pass = false;
+ }
+
+ if (pass) {
+ // Double check `to_rotation` decomposing with XYZ rotation order.
+ const Vector3 euler_xyz_from_rotation = to_rotation.get_euler_xyz();
+ Basis rotation_from_xyz_computed_euler;
+ rotation_from_xyz_computed_euler.set_euler_xyz(euler_xyz_from_rotation);
+
+ res = to_rotation.inverse() * rotation_from_xyz_computed_euler;
+
+ if ((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() > 0.1) {
+ OS::get_singleton()->print("Double check with XYZ rot order failed, due to X %ls\n", String(res.get_axis(0)).c_str());
+ pass = false;
+ }
+ if ((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() > 0.1) {
+ OS::get_singleton()->print("Double check with XYZ rot order failed, due to Y %ls\n", String(res.get_axis(1)).c_str());
+ pass = false;
+ }
+ if ((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() > 0.1) {
+ OS::get_singleton()->print("Double check with XYZ rot order failed, due to Z %ls\n", String(res.get_axis(2)).c_str());
+ pass = false;
+ }
+ }
+
+ if (pass == false) {
+ // Print phase only if not pass.
+ OS *os = OS::get_singleton();
+ os->print("Rotation order: %ls\n.", get_rot_order_name(rot_order).c_str());
+ os->print("Original Rotation: %ls\n", String(deg_original_euler).c_str());
+ os->print("Quaternion to rotation order: %ls\n", String(rad2deg(euler_from_rotation)).c_str());
+ }
+
+ return pass;
+}
+
+void test_euler_conversion() {
+ Vector<RotOrder> rotorder_to_test;
+ rotorder_to_test.push_back(EulerXYZ);
+ rotorder_to_test.push_back(EulerXZY);
+ rotorder_to_test.push_back(EulerYZX);
+ rotorder_to_test.push_back(EulerYXZ);
+ rotorder_to_test.push_back(EulerZXY);
+ rotorder_to_test.push_back(EulerZYX);
+
+ Vector<Vector3> vectors_to_test;
+
+ // Test the special cases.
+ vectors_to_test.push_back(Vector3(0.0, 0.0, 0.0));
+ vectors_to_test.push_back(Vector3(0.5, 0.5, 0.5));
+ vectors_to_test.push_back(Vector3(-0.5, -0.5, -0.5));
+ vectors_to_test.push_back(Vector3(40.0, 40.0, 40.0));
+ vectors_to_test.push_back(Vector3(-40.0, -40.0, -40.0));
+ vectors_to_test.push_back(Vector3(0.0, 0.0, -90.0));
+ vectors_to_test.push_back(Vector3(0.0, -90.0, 0.0));
+ vectors_to_test.push_back(Vector3(-90.0, 0.0, 0.0));
+ vectors_to_test.push_back(Vector3(0.0, 0.0, 90.0));
+ vectors_to_test.push_back(Vector3(0.0, 90.0, 0.0));
+ vectors_to_test.push_back(Vector3(90.0, 0.0, 0.0));
+ vectors_to_test.push_back(Vector3(0.0, 0.0, -30.0));
+ vectors_to_test.push_back(Vector3(0.0, -30.0, 0.0));
+ vectors_to_test.push_back(Vector3(-30.0, 0.0, 0.0));
+ vectors_to_test.push_back(Vector3(0.0, 0.0, 30.0));
+ vectors_to_test.push_back(Vector3(0.0, 30.0, 0.0));
+ vectors_to_test.push_back(Vector3(30.0, 0.0, 0.0));
+ vectors_to_test.push_back(Vector3(0.5, 50.0, 20.0));
+ vectors_to_test.push_back(Vector3(-0.5, -50.0, -20.0));
+ vectors_to_test.push_back(Vector3(0.5, 0.0, 90.0));
+ vectors_to_test.push_back(Vector3(0.5, 0.0, -90.0));
+ vectors_to_test.push_back(Vector3(360.0, 360.0, 360.0));
+ vectors_to_test.push_back(Vector3(-360.0, -360.0, -360.0));
+ vectors_to_test.push_back(Vector3(-90.0, 60.0, -90.0));
+ vectors_to_test.push_back(Vector3(90.0, 60.0, -90.0));
+ vectors_to_test.push_back(Vector3(90.0, -60.0, -90.0));
+ vectors_to_test.push_back(Vector3(-90.0, -60.0, -90.0));
+ vectors_to_test.push_back(Vector3(-90.0, 60.0, 90.0));
+ vectors_to_test.push_back(Vector3(90.0, 60.0, 90.0));
+ vectors_to_test.push_back(Vector3(90.0, -60.0, 90.0));
+ vectors_to_test.push_back(Vector3(-90.0, -60.0, 90.0));
+ vectors_to_test.push_back(Vector3(60.0, 90.0, -40.0));
+ vectors_to_test.push_back(Vector3(60.0, -90.0, -40.0));
+ vectors_to_test.push_back(Vector3(-60.0, -90.0, -40.0));
+ vectors_to_test.push_back(Vector3(-60.0, 90.0, 40.0));
+ vectors_to_test.push_back(Vector3(60.0, 90.0, 40.0));
+ vectors_to_test.push_back(Vector3(60.0, -90.0, 40.0));
+ vectors_to_test.push_back(Vector3(-60.0, -90.0, 40.0));
+ vectors_to_test.push_back(Vector3(-90.0, 90.0, -90.0));
+ vectors_to_test.push_back(Vector3(90.0, 90.0, -90.0));
+ vectors_to_test.push_back(Vector3(90.0, -90.0, -90.0));
+ vectors_to_test.push_back(Vector3(-90.0, -90.0, -90.0));
+ vectors_to_test.push_back(Vector3(-90.0, 90.0, 90.0));
+ vectors_to_test.push_back(Vector3(90.0, 90.0, 90.0));
+ vectors_to_test.push_back(Vector3(90.0, -90.0, 90.0));
+ vectors_to_test.push_back(Vector3(20.0, 150.0, 30.0));
+ vectors_to_test.push_back(Vector3(20.0, -150.0, 30.0));
+ vectors_to_test.push_back(Vector3(-120.0, -150.0, 30.0));
+ vectors_to_test.push_back(Vector3(-120.0, -150.0, -130.0));
+ vectors_to_test.push_back(Vector3(120.0, -150.0, -130.0));
+ vectors_to_test.push_back(Vector3(120.0, 150.0, -130.0));
+ vectors_to_test.push_back(Vector3(120.0, 150.0, 130.0));
+
+ // Add 1000 random vectors with weirds numbers.
+ RandomNumberGenerator rng;
+ for (int _ = 0; _ < 1000; _ += 1) {
+ vectors_to_test.push_back(Vector3(
+ rng.randf_range(-1800, 1800),
+ rng.randf_range(-1800, 1800),
+ rng.randf_range(-1800, 1800)));
+ }
+
+ bool success = true;
+ for (int h = 0; h < rotorder_to_test.size(); h += 1) {
+ int passed = 0;
+ int failed = 0;
+ for (int i = 0; i < vectors_to_test.size(); i += 1) {
+ if (test_rotation(vectors_to_test[i], rotorder_to_test[h])) {
+ //OS::get_singleton()->print("Success. \n\n");
+ passed += 1;
+ } else {
+ OS::get_singleton()->print("FAILED FAILED FAILED. \n\n");
+ OS::get_singleton()->print("------------>\n");
+ OS::get_singleton()->print("------------>\n");
+ failed += 1;
+ success = false;
+ }
+ }
+
+ if (failed == 0) {
+ OS::get_singleton()->print("%i passed tests for rotation order: %ls.\n", passed, get_rot_order_name(rotorder_to_test[h]).c_str());
+ } else {
+ OS::get_singleton()->print("%i FAILED tests for rotation order: %ls.\n", failed, get_rot_order_name(rotorder_to_test[h]).c_str());
+ }
+ }
+
+ if (success) {
+ OS::get_singleton()->print("Euler conversion checks passed.\n");
+ } else {
+ OS::get_singleton()->print("Euler conversion checks FAILED.\n");
+ }
+}
+
+MainLoop *test() {
+ OS::get_singleton()->print("Start euler conversion checks.\n");
+ test_euler_conversion();
+
+ return NULL;
+}
+
+} // namespace TestBasis
diff --git a/main/tests/test_basis.h b/main/tests/test_basis.h
new file mode 100644
index 0000000000..67c9db8877
--- /dev/null
+++ b/main/tests/test_basis.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* test_fbx.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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_BASIS_H
+#define TEST_BASIS_H
+
+#include "core/os/main_loop.h"
+
+namespace TestBasis {
+MainLoop *test();
+}
+
+#endif
diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp
index 0bb8367240..5ebdaf1741 100644
--- a/main/tests/test_main.cpp
+++ b/main/tests/test_main.cpp
@@ -35,6 +35,7 @@
#ifdef DEBUG_ENABLED
#include "test_astar.h"
+#include "test_basis.h"
#include "test_class_db.h"
#include "test_gdscript.h"
#include "test_gui.h"
@@ -51,6 +52,7 @@ const char **tests_get_names() {
static const char *test_names[] = {
"string",
"math",
+ "basis",
"physics_2d",
"physics_3d",
"render",
@@ -79,6 +81,10 @@ MainLoop *test_main(String p_test, const List<String> &p_args) {
return TestMath::test();
}
+ if (p_test == "basis") {
+ return TestBasis::test();
+ }
+
if (p_test == "physics_2d") {
return TestPhysics2D::test();
}
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index ba425371a8..2f4f7d7a4c 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -29,14 +29,15 @@
/*************************************************************************/
#include "texture_loader_dds.h"
+
#include "core/os/file_access.h"
#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
+// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
+
enum {
DDS_MAGIC = 0x20534444,
- DDSD_CAPS = 0x00000001,
- DDSD_PIXELFORMAT = 0x00001000,
DDSD_PITCH = 0x00000008,
DDSD_LINEARSIZE = 0x00080000,
DDSD_MIPMAPCOUNT = 0x00020000,
@@ -47,7 +48,6 @@ enum {
};
enum DDSFormat {
-
DDS_DXT1,
DDS_DXT3,
DDS_DXT5,
@@ -128,7 +128,9 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
//validate
- if (magic != DDS_MAGIC || hsize != 124 || !(flags & DDSD_PIXELFORMAT) || !(flags & DDSD_CAPS)) {
+ // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
+ // but non-mandatory when reading (as some writers don't set them)...
+ if (magic != DDS_MAGIC || hsize != 124) {
ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 50d8289fd1..3a5db3687b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3093,6 +3093,14 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK;
}
}
+
+ for (int i = 0; i < base_type.class_type->subclasses.size(); i++) {
+ if (base_type.class_type->subclasses[i]->name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->subclasses[i]->line;
+ return OK;
+ }
+ }
}
base_type = base_type.class_type->base_type;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index a06ecb1ea8..d2867cb77a 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2821,6 +2821,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
#ifdef DEBUG_ENABLED
+ pending_newline = -1; // reset for the new block
+
NewLineNode *nl = alloc_node<NewLineNode>();
nl->line = tokenizer->get_token_line();
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
index 7391e8790d..3bbbf29d3b 100644
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -15,7 +15,9 @@ def build_godot_tools(source, target, env):
from .solution_builder import build_solution
- build_solution(env, solution_path, build_config)
+ extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
+
+ build_solution(env, solution_path, build_config, extra_msbuild_args)
# No need to copy targets. The GodotTools csproj takes care of copying them.
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 23f01b3cca..80e3b59325 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -191,17 +191,16 @@ def configure(env, env_mono):
env.Append(LIBS=["psapi"])
env.Append(LIBS=["version"])
else:
- mono_lib_name = find_name_in_dir_files(
- mono_lib_path, mono_lib_names, prefixes=["", "lib"], extensions=lib_suffixes
- )
+ mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
- if not mono_lib_name:
+ if not mono_lib_file:
raise RuntimeError("Could not find mono library in: " + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + ".lib")
+ env.Append(LINKFLAGS=mono_lib_file)
else:
- env.Append(LIBS=[mono_lib_name])
+ mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
+ env.Append(LINKFLAGS=mono_lib_file_path)
mono_bin_path = os.path.join(mono_root, "bin")
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index d069651dd3..572c541412 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -19,9 +19,12 @@ namespace GodotTools.IdeMessaging
private readonly string identity;
private string MetaFilePath { get; }
+ private DateTime? metaFileModifiedTime;
private GodotIdeMetadata godotIdeMetadata;
private readonly FileSystemWatcher fsWatcher;
+ public string GodotEditorExecutablePath => godotIdeMetadata.EditorExecutablePath;
+
private readonly IMessageHandler messageHandler;
private Peer peer;
@@ -123,7 +126,7 @@ namespace GodotTools.IdeMessaging
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
// FileSystemWatcher requires an existing directory
- if (!File.Exists(projectMetadataDir))
+ if (!Directory.Exists(projectMetadataDir))
Directory.CreateDirectory(projectMetadataDir);
fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
@@ -142,6 +145,13 @@ namespace GodotTools.IdeMessaging
if (!File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null && metadata != godotIdeMetadata)
@@ -173,6 +183,13 @@ namespace GodotTools.IdeMessaging
if (IsConnected || !File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null)
@@ -185,7 +202,8 @@ namespace GodotTools.IdeMessaging
private GodotIdeMetadata? ReadMetadataFile()
{
- using (var reader = File.OpenText(MetaFilePath))
+ using (var fileStream = new FileStream(MetaFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ using (var reader = new StreamReader(fileStream))
{
string portStr = reader.ReadLine();
@@ -272,6 +290,7 @@ namespace GodotTools.IdeMessaging
// ReSharper disable once UnusedMember.Global
public async void Start()
{
+ fsWatcher.Created += OnMetaFileChanged;
fsWatcher.Changed += OnMetaFileChanged;
fsWatcher.Deleted += OnMetaFileDeleted;
fsWatcher.EnableRaisingEvents = true;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index 67815959a6..dad6b9ae7a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.2</LangVersion>
<PackageId>GodotTools.IdeMessaging</PackageId>
- <Version>1.1.0</Version>
+ <Version>1.1.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Authors>Godot Engine contributors</Authors>
<Company />
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
index a4e86d6177..10d7e1898e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
@@ -105,49 +105,45 @@ namespace GodotTools.IdeMessaging
try
{
- try
+ if (msg.Kind == MessageKind.Request)
{
- if (msg.Kind == MessageKind.Request)
- {
- var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
- await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
- }
- else if (msg.Kind == MessageKind.Response)
- {
- ResponseAwaiter responseAwaiter;
+ var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
+ await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
+ }
+ else if (msg.Kind == MessageKind.Response)
+ {
+ ResponseAwaiter responseAwaiter;
- using (await requestsSem.UseAsync())
+ using (await requestsSem.UseAsync())
+ {
+ if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
{
- if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
- {
- Logger.LogError($"Received unexpected response: {msg.Id}");
- return;
- }
-
- responseAwaiter = queue.Dequeue();
+ Logger.LogError($"Received unexpected response: {msg.Id}");
+ return;
}
- responseAwaiter.SetResult(msg.Content);
- }
- else
- {
- throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
+ responseAwaiter = queue.Dequeue();
}
+
+ responseAwaiter.SetResult(msg.Content);
}
- catch (Exception e)
+ else
{
- Logger.LogError($"Message handler for '{msg}' failed with exception", e);
+ throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
}
}
catch (Exception e)
{
- Logger.LogError($"Exception thrown from message handler. Message: {msg}", e);
+ Logger.LogError($"Message handler for '{msg}' failed with exception", e);
}
}
}
catch (Exception e)
{
- Logger.LogError("Unhandled exception in the peer loop", e);
+ if (!IsDisposed || !(e is SocketException || e.InnerException is SocketException))
+ {
+ Logger.LogError("Unhandled exception in the peer loop", e);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
index 1dd4f852e5..e93db9377b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
@@ -67,6 +67,19 @@ namespace GodotTools.IdeMessaging.Requests
{
}
+ public sealed class StopPlayRequest : Request
+ {
+ public new const string Id = "StopPlay";
+
+ public StopPlayRequest() : base(Id)
+ {
+ }
+ }
+
+ public sealed class StopPlayResponse : Response
+ {
+ }
+
public sealed class DebugPlayRequest : Request
{
public string DebuggerHost { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
new file mode 100644
index 0000000000..5b3ed0b1b7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net472</TargetFramework>
+ <LangVersion>7.2</LangVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="EnvDTE" Version="8.0.2" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
new file mode 100644
index 0000000000..affb2a47e7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -0,0 +1,270 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text.RegularExpressions;
+using EnvDTE;
+
+namespace GodotTools.OpenVisualStudio
+{
+ internal static class Program
+ {
+ [DllImport("ole32.dll")]
+ private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);
+
+ [DllImport("ole32.dll")]
+ private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
+
+ [DllImport("user32.dll")]
+ private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ private static void ShowHelp()
+ {
+ Console.WriteLine("Opens the file(s) in a Visual Studio instance that is editing the specified solution.");
+ Console.WriteLine("If an existing instance for the solution is not found, a new one is created.");
+ Console.WriteLine();
+ Console.WriteLine("Usage:");
+ Console.WriteLine(@" GodotTools.OpenVisualStudio.exe solution [file[;line[;col]]...]");
+ Console.WriteLine();
+ Console.WriteLine("Lines and columns begin at one. Zero or lower will result in an error.");
+ Console.WriteLine("If a line is specified but a column is not, the line is selected in the text editor.");
+ }
+
+ // STAThread needed, otherwise CoRegisterMessageFilter may return CO_E_NOT_SUPPORTED.
+ [STAThread]
+ private static int Main(string[] args)
+ {
+ if (args.Length == 0 || args[0] == "--help" || args[0] == "-h")
+ {
+ ShowHelp();
+ return 0;
+ }
+
+ string solutionFile = NormalizePath(args[0]);
+
+ var dte = FindInstanceEditingSolution(solutionFile);
+
+ if (dte == null)
+ {
+ // Open a new instance
+
+ var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
+ dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ dte.UserControl = true;
+
+ try
+ {
+ dte.Solution.Open(solutionFile);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("Solution.Open: Invalid path or file not found");
+ return 1;
+ }
+
+ dte.MainWindow.Visible = true;
+ }
+
+ MessageFilter.Register();
+
+ try
+ {
+ // Open files
+
+ for (int i = 1; i < args.Length; i++)
+ {
+ // Both the line number and the column begin at one
+
+ string[] fileArgumentParts = args[i].Split(';');
+
+ string filePath = NormalizePath(fileArgumentParts[0]);
+
+ try
+ {
+ dte.ItemOperations.OpenFile(filePath);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("ItemOperations.OpenFile: Invalid path or file not found");
+ return 1;
+ }
+
+ if (fileArgumentParts.Length > 1)
+ {
+ if (int.TryParse(fileArgumentParts[1], out int line))
+ {
+ var textSelection = (TextSelection)dte.ActiveDocument.Selection;
+
+ if (fileArgumentParts.Length > 2)
+ {
+ if (int.TryParse(fileArgumentParts[2], out int column))
+ {
+ textSelection.MoveToLineAndOffset(line, column);
+ }
+ else
+ {
+ Console.Error.WriteLine("The column part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ else
+ {
+ textSelection.GotoLine(line, Select: true);
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("The line part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ }
+ }
+ finally
+ {
+ var mainWindow = dte.MainWindow;
+ mainWindow.Activate();
+ SetForegroundWindow(new IntPtr(mainWindow.HWnd));
+
+ MessageFilter.Revoke();
+ }
+
+ return 0;
+ }
+
+ private static DTE FindInstanceEditingSolution(string solutionPath)
+ {
+ if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
+ return null;
+
+ try
+ {
+ pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
+ ppenumMoniker.Reset();
+
+ var moniker = new IMoniker[1];
+
+ while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
+ {
+ string ppszDisplayName;
+
+ CreateBindCtx(0, out IBindCtx ppbc);
+
+ try
+ {
+ moniker[0].GetDisplayName(ppbc, null, out ppszDisplayName);
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(ppbc);
+ }
+
+ if (ppszDisplayName == null)
+ continue;
+
+ // The digits after the colon are the process ID
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ continue;
+
+ if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
+ {
+ if (ppunkObject is DTE dte && dte.Solution.FullName.Length > 0)
+ {
+ if (NormalizePath(dte.Solution.FullName) == solutionPath)
+ return dte;
+ }
+ }
+ }
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(pprot);
+ }
+
+ return null;
+ }
+
+ static string NormalizePath(string path)
+ {
+ return new Uri(Path.GetFullPath(path)).LocalPath
+ .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
+ .ToUpperInvariant();
+ }
+
+ #region MessageFilter. See: http: //msdn.microsoft.com/en-us/library/ms228772.aspx
+
+ private class MessageFilter : IOleMessageFilter
+ {
+ // Class containing the IOleMessageFilter
+ // thread error-handling functions
+
+ private static IOleMessageFilter _oldFilter;
+
+ // Start the filter
+ public static void Register()
+ {
+ IOleMessageFilter newFilter = new MessageFilter();
+ int ret = CoRegisterMessageFilter(newFilter, out _oldFilter);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ // Done with the filter, close it
+ public static void Revoke()
+ {
+ int ret = CoRegisterMessageFilter(_oldFilter, out _);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ //
+ // IOleMessageFilter functions
+ // Handle incoming thread requests
+ int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
+ {
+ // Return the flag SERVERCALL_ISHANDLED
+ return 0;
+ }
+
+ // Thread call was rejected, so try again.
+ int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
+ {
+ if (dwRejectType == 2)
+ // flag = SERVERCALL_RETRYLATER
+ {
+ // Retry the thread call immediately if return >= 0 & < 100
+ return 99;
+ }
+
+ // Too busy; cancel call
+ return -1;
+ }
+
+ int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
+ {
+ // Return the flag PENDINGMSG_WAITDEFPROCESS
+ return 2;
+ }
+
+ // Implement the IOleMessageFilter interface
+ [DllImport("ole32.dll")]
+ private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
+ }
+
+ [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IOleMessageFilter
+ {
+ [PreserveSig]
+ int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
+
+ [PreserveSig]
+ int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
+
+ [PreserveSig]
+ int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
+ }
+
+ #endregion
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index fb2beb6995..679d5bb444 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -12,6 +12,11 @@ namespace GodotTools.ProjectEditor
private const string CoreApiProjectName = "GodotSharp";
private const string EditorApiProjectName = "GodotSharpEditor";
+ public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
+ public const string GodotProjectTypeGuid = "{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}";
+
+ public static readonly string GodotDefaultProjectTypeGuids = $"{GodotProjectTypeGuid};{CSharpProjectTypeGuid}";
+
public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
{
string path = Path.Combine(dir, name + ".csproj");
@@ -19,6 +24,7 @@ namespace GodotTools.ProjectEditor
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(name, "Debug", out mainGroup);
+ mainGroup.SetProperty("ProjectTypeGuids", GodotDefaultProjectTypeGuids);
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 069a1edaa3..8774b4ee31 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -165,6 +165,21 @@ namespace GodotTools.ProjectEditor
return result.ToArray();
}
+ public static void EnsureHasProjectTypeGuids(MSBuildProject project)
+ {
+ var root = project.Root;
+
+ bool found = root.PropertyGroups.Any(pg =>
+ string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids"));
+
+ if (found)
+ return;
+
+ root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids);
+
+ project.HasUnsavedChanges = true;
+ }
+
/// Simple function to make sure the Api assembly references are configured correctly
public static void FixApiHintPath(MSBuildProject project)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index f6147eb5bb..ba5379e562 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.BuildLogger", "G
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj", "{92600954-25F0-4291-8E11-1FEE9FC4BE20}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -37,5 +39,9 @@ Global
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index c874025be0..403e25781d 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -238,7 +239,31 @@ namespace GodotTools
// Not an error. Tells the caller to fallback to the global external editor settings or the built-in editor.
return Error.Unavailable;
case ExternalEditorId.VisualStudio:
- throw new NotSupportedException();
+ {
+ string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+
+ var args = new List<string>
+ {
+ GodotSharpDirs.ProjectSlnPath,
+ line >= 0 ? $"{scriptPath};{line + 1};{col + 1}" : scriptPath
+ };
+
+ string command = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "GodotTools.OpenVisualStudio.exe");
+
+ try
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
+
+ OS.RunProcess(command, args);
+ }
+ catch (Exception e)
+ {
+ GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
+ }
+
+ break;
+ }
case ExternalEditorId.VisualStudioForMac:
goto case ExternalEditorId.MonoDevelop;
case ExternalEditorId.Rider:
@@ -458,6 +483,9 @@ namespace GodotTools
// Apply the other fixes only after configurations have been migrated
+ // Make sure the existing project has the ProjectTypeGuids property (for VisualStudio)
+ ProjectUtils.EnsureHasProjectTypeGuids(msbuildProject);
+
// Make sure the existing project has Api assembly references configured correctly
ProjectUtils.FixApiHintPath(msbuildProject);
@@ -501,7 +529,8 @@ namespace GodotTools
if (OS.IsWindows)
{
- settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
+ settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" +
+ $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index ba527ca3b5..3f14629b11 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -33,5 +33,7 @@
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <!-- Include it if this is an SCons build targeting Windows, or if it's not an SCons build but we're on Windows -->
+ <ProjectReference Include="..\GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) " />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 32f264d100..98e8d13be0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -307,6 +307,11 @@ namespace GodotTools.Ides
var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
return await HandleDebugPlay(request);
},
+ [StopPlayRequest.Id] = async (peer, content) =>
+ {
+ var request = JsonConvert.DeserializeObject<StopPlayRequest>(content.Body);
+ return await HandleStopPlay(request);
+ },
[ReloadScriptsRequest.Id] = async (peer, content) =>
{
_ = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body);
@@ -343,6 +348,12 @@ namespace GodotTools.Ides
return Task.FromResult<Response>(new DebugPlayResponse());
}
+ private static Task<Response> HandleStopPlay(StopPlayRequest request)
+ {
+ DispatchToMainThread(Internal.EditorRunStop);
+ return Task.FromResult<Response>(new StopPlayResponse());
+ }
+
private static Task<Response> HandleReloadScripts()
{
DispatchToMainThread(Internal.ScriptEditorDebugger_ReloadScripts);
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 1b4fe68582..e0cf916a01 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -75,7 +75,6 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
if (res == ERROR_MORE_DATA) {
// dwBufferSize now contains the actual size
- Vector<WCHAR> buffer;
buffer.resize(dwBufferSize);
res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
}
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 29eabfdde8..9c1b8f2949 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -343,55 +343,21 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
return ret;
}
-uint64_t OS_Windows::get_unix_time() const {
- FILETIME ft;
- SYSTEMTIME st;
- GetSystemTime(&st);
- SystemTimeToFileTime(&st, &ft);
-
- SYSTEMTIME ep;
- ep.wYear = 1970;
- ep.wMonth = 1;
- ep.wDayOfWeek = 4;
- ep.wDay = 1;
- ep.wHour = 0;
- ep.wMinute = 0;
- ep.wSecond = 0;
- ep.wMilliseconds = 0;
- FILETIME fep;
- SystemTimeToFileTime(&ep, &fep);
-
- // Type punning through unions (rather than pointer cast) as per:
- // https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks
- ULARGE_INTEGER ft_punning;
- ft_punning.LowPart = ft.dwLowDateTime;
- ft_punning.HighPart = ft.dwHighDateTime;
-
- ULARGE_INTEGER fep_punning;
- fep_punning.LowPart = fep.dwLowDateTime;
- fep_punning.HighPart = fep.dwHighDateTime;
-
- return (ft_punning.QuadPart - fep_punning.QuadPart) / 10000000;
-};
-
-uint64_t OS_Windows::get_system_time_secs() const {
- return get_system_time_msecs() / 1000;
-}
-
-uint64_t OS_Windows::get_system_time_msecs() const {
- const uint64_t WINDOWS_TICK = 10000;
- const uint64_t MSEC_TO_UNIX_EPOCH = 11644473600000LL;
+double OS_Windows::get_unix_time() const {
+ // 1 Windows tick is 100ns
+ const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
+ const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
SYSTEMTIME st;
GetSystemTime(&st);
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
- uint64_t ret;
- ret = ft.dwHighDateTime;
- ret <<= 32;
- ret |= ft.dwLowDateTime;
+ uint64_t ticks_time;
+ ticks_time = ft.dwHighDateTime;
+ ticks_time <<= 32;
+ ticks_time |= ft.dwLowDateTime;
- return (uint64_t)(ret / WINDOWS_TICK - MSEC_TO_UNIX_EPOCH);
+ return (double)(ticks_time - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
}
void OS_Windows::delay_usec(uint32_t p_usec) const {
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 11e3533bfd..910a83539a 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -129,9 +129,7 @@ public:
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
virtual TimeZoneInfo get_time_zone_info() const;
- virtual uint64_t get_unix_time() const;
- virtual uint64_t get_system_time_secs() const;
- virtual uint64_t get_system_time_msecs() const;
+ virtual double get_unix_time() const;
virtual Error set_cwd(const String &p_cwd);
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 8f69676da4..68e99445d8 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -343,7 +343,9 @@ void Camera2D::_notification(int p_what) {
void Camera2D::set_offset(const Vector2 &p_offset) {
offset = p_offset;
+ Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
+ smoothed_camera_pos = old_smoothed_camera_pos;
}
Vector2 Camera2D::get_offset() const {
@@ -361,7 +363,9 @@ Camera2D::AnchorMode Camera2D::get_anchor_mode() const {
void Camera2D::set_rotating(bool p_rotating) {
rotating = p_rotating;
+ Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
+ smoothed_camera_pos = old_smoothed_camera_pos;
}
bool Camera2D::is_rotating() const {
@@ -522,7 +526,9 @@ bool Camera2D::is_v_drag_enabled() const {
void Camera2D::set_v_offset(float p_offset) {
v_ofs = p_offset;
v_offset_changed = true;
+ Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
+ smoothed_camera_pos = old_smoothed_camera_pos;
}
float Camera2D::get_v_offset() const {
@@ -532,7 +538,9 @@ float Camera2D::get_v_offset() const {
void Camera2D::set_h_offset(float p_offset) {
h_ofs = p_offset;
h_offset_changed = true;
+ Point2 old_smoothed_camera_pos = smoothed_camera_pos;
_update_scroll();
+ smoothed_camera_pos = old_smoothed_camera_pos;
}
float Camera2D::get_h_offset() const {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index a09424fa17..6723ca04b9 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -67,6 +67,8 @@ SkinReference::~SkinReference() {
RS::get_singleton()->free(skeleton);
}
+///////////////////////////////////////
+
bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;
@@ -853,6 +855,15 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
return skin_ref;
}
+// helper functions
+Transform Skeleton3D::bone_transform_to_world_transform(Transform p_bone_transform) {
+ return get_global_transform() * p_bone_transform;
+}
+
+Transform Skeleton3D::world_transform_to_bone_transform(Transform p_world_transform) {
+ return get_global_transform().affine_inverse() * p_world_transform;
+}
+
void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders);
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
@@ -892,6 +903,9 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose);
ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose);
+ ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton3D::bone_transform_to_world_transform);
+ ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton3D::world_transform_to_bone_transform);
+
#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones);
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 66706a9450..a21891a32e 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -71,10 +71,6 @@ class Skeleton3D : public Node3D {
private:
friend class SkinReference;
- Set<SkinReference *> skin_bindings;
-
- void _skin_changed();
-
struct Bone {
String name;
@@ -116,6 +112,10 @@ private:
}
};
+ Set<SkinReference *> skin_bindings;
+
+ void _skin_changed();
+
bool animate_physical_bones;
Vector<Bone> bones;
Vector<int> process_order;
@@ -200,6 +200,10 @@ public:
Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
+ // Helper functions
+ Transform bone_transform_to_world_transform(Transform p_transform);
+ Transform world_transform_to_bone_transform(Transform p_transform);
+
#ifndef _3D_DISABLED
// Physical bone API
@@ -213,7 +217,7 @@ public:
PhysicalBone3D *get_physical_bone_parent(int p_bone);
private:
- /// This is a slow API os it's cached
+ /// This is a slow API, so it's cached
PhysicalBone3D *_get_physical_bone_parent(int p_bone);
void _rebuild_physical_bones_cache();
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 524ff346d1..721f573f39 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -1222,8 +1222,8 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Texture", "texture_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "ParentNode,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "ParentNode,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat");
ADD_GROUP("Material", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");