diff --git a/include/core/Basis.hpp b/include/core/Basis.hpp index dad20b6a..b49b94cd 100644 --- a/include/core/Basis.hpp +++ b/include/core/Basis.hpp @@ -64,9 +64,13 @@ public: Vector3 get_scale() const; - Vector3 get_euler() const; + Vector3 get_euler_xyz() const; + void set_euler_xyz(const Vector3 &p_euler); + Vector3 get_euler_yxz() const; + void set_euler_yxz(const Vector3 &p_euler); - void set_euler(const Vector3& p_euler); + inline Vector3 get_euler() const { return get_euler_yxz(); } + inline void set_euler(const Vector3& p_euler) { set_euler_yxz(p_euler); } // transposed dot products real_t tdotx(const Vector3& v) const; diff --git a/include/core/Quat.hpp b/include/core/Quat.hpp index 927d4a37..74bb1367 100644 --- a/include/core/Quat.hpp +++ b/include/core/Quat.hpp @@ -23,12 +23,16 @@ public: Quat inverse() const; - void set_euler(const Vector3& p_euler); + void set_euler_xyz(const Vector3& p_euler); + Vector3 get_euler_xyz() const; + void set_euler_yxz(const Vector3& p_euler); + Vector3 get_euler_yxz() const; + + inline void set_euler(const Vector3& p_euler) { set_euler_yxz(p_euler); } + inline Vector3 get_euler() const { return get_euler_yxz(); } real_t dot(const Quat& q) const; - Vector3 get_euler() const; - Quat slerp(const Quat& q, const real_t& t) const; Quat slerpni(const Quat& q, const real_t& t) const; diff --git a/include/core/String.hpp b/include/core/String.hpp index f03c997a..de65f3cf 100644 --- a/include/core/String.hpp +++ b/include/core/String.hpp @@ -37,6 +37,14 @@ public: ~String(); + static String num(double p_num, int p_decimals = -1); + static String num_scientific(double p_num); + static String num_real(double p_num); + static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false); + static String chr(godot_char_type p_char); + static String md5(const uint8_t *p_md5); + static String hex_encode_buffer(const uint8_t *p_buffer, int p_len); + wchar_t &operator[](const int idx); wchar_t operator[](const int idx) const; diff --git a/src/core/AABB.cpp b/src/core/AABB.cpp index 58030092..cb50583a 100644 --- a/src/core/AABB.cpp +++ b/src/core/AABB.cpp @@ -633,8 +633,7 @@ void AABB::get_edge(int p_edge,Vector3& r_from,Vector3& r_to) const { AABB::operator String() const { - //return String()+position +" - "+ size; - return String(); // @Todo + return String() + position + " - " + size; } } diff --git a/src/core/Basis.cpp b/src/core/Basis.cpp index b4b24d12..5919558e 100644 --- a/src/core/Basis.cpp +++ b/src/core/Basis.cpp @@ -59,7 +59,7 @@ void Basis::invert() elements[0][2] * co[2]; - ERR_FAIL_COND(det != 0); + ERR_FAIL_COND(det == 0); real_t s = 1.0/det; @@ -179,8 +179,18 @@ Vector3 Basis::get_scale() const ); } -Vector3 Basis::get_euler() const -{ +// get_euler_xyz returns a vector containing the Euler angles in the format +// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last +// (following the convention they are commonly defined in the literature). +// +// The current implementation uses XYZ convention (Z is the first rotation), +// so euler.z is the angle of the (first) rotation around Z axis and so on, +// +// And thus, assuming the matrix is a rotation matrix, this function returns +// the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates +// around the z-axis by a and so on. +Vector3 Basis::get_euler_xyz() const { + // Euler angles in XYZ convention. // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix // @@ -190,50 +200,130 @@ Vector3 Basis::get_euler() const Vector3 euler; - if (is_rotation() == false) - return euler; - - euler.y = ::asin(elements[0][2]); - if ( euler.y < Math_PI*0.5) { - if ( euler.y > -Math_PI*0.5) { - euler.x = ::atan2(-elements[1][2],elements[2][2]); - euler.z = ::atan2(-elements[0][1],elements[0][0]); + ERR_FAIL_COND_V(is_rotation() == false, euler); + real_t sy = elements[0][2]; + if (sy < 1.0) { + if (sy > -1.0) { + // 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) + euler.x = 0; + euler.y = atan2(elements[0][2], elements[0][0]); + euler.z = 0; + } else { + euler.x = ::atan2(-elements[1][2], elements[2][2]); + euler.y = ::asin(sy); + euler.z = ::atan2(-elements[0][1], elements[0][0]); + } } else { - real_t r = ::atan2(elements[1][0],elements[1][1]); + euler.x = -::atan2(elements[0][1], elements[1][1]); + euler.y = -Math_PI / 2.0; euler.z = 0.0; - euler.x = euler.z - r; - } } else { - real_t r = ::atan2(elements[0][1],elements[1][1]); + euler.x = ::atan2(elements[0][1], elements[1][1]); + euler.y = Math_PI / 2.0; + euler.z = 0.0; + } + return euler; +} + +// set_euler_xyz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// The current implementation uses XYZ convention (Z is the first rotation). +void Basis::set_euler_xyz(const Vector3 &p_euler) { + + real_t c, s; + + c = ::cos(p_euler.x); + s = ::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = ::cos(p_euler.y); + s = ::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = ::cos(p_euler.z); + s = ::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + //optimizer will optimize away all this anyway + *this = xmat * (ymat * zmat); +} + +// 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 { + + // Euler angles in YXZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy + // cx*sz cx*cz -sx + // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx + + Vector3 euler; + + ERR_FAIL_COND_V(is_rotation() == false, euler); + + real_t m12 = elements[1][2]; + + if (m12 < 1) { + if (m12 > -1) { + // 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) + euler.x = atan2(-m12, elements[1][1]); + euler.y = 0; + euler.z = 0; + } else { + euler.x = asin(-m12); + euler.y = atan2(elements[0][2], elements[2][2]); + euler.z = atan2(elements[1][0], elements[1][1]); + } + } else { // m12 == -1 + euler.x = Math_PI * 0.5; + 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.z = 0; - euler.x = r - euler.z; } return euler; } -void Basis::set_euler(const Vector3& p_euler) -{ +// set_euler_yxz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// The current implementation uses YXZ convention (Z is the first rotation). +void Basis::set_euler_yxz(const Vector3 &p_euler) { + real_t c, s; c = ::cos(p_euler.x); s = ::sin(p_euler.x); - Basis xmat(1.0,0.0,0.0,0.0,c,-s,0.0,s,c); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); c = ::cos(p_euler.y); s = ::sin(p_euler.y); - Basis ymat(c,0.0,s,0.0,1.0,0.0,-s,0.0,c); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); c = ::cos(p_euler.z); s = ::sin(p_euler.z); - Basis zmat(c,-s,0.0,s,c,0.0,0.0,0.0,1.0); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); //optimizer will optimize away all this anyway - *this = xmat*(ymat*zmat); + *this = ymat * xmat * zmat; } + + // transposed dot products real_t Basis::tdotx(const Vector3& v) const { return elements[0][0] * v[0] + elements[1][0] * v[1] + elements[2][0] * v[2]; @@ -344,7 +434,16 @@ Basis Basis::operator*(real_t p_val) const { Basis::operator String() const { String s; - // @Todo + for (int i = 0; i < 3; i++) { + + for (int j = 0; j < 3; j++) { + + if (i != 0 || j != 0) + s += ", "; + + s += String::num(elements[i][j]); + } + } return s; } @@ -398,7 +497,7 @@ Basis Basis::transpose_xform(const Basis& m) const void Basis::orthonormalize() { - ERR_FAIL_COND(determinant() != 0); + ERR_FAIL_COND(determinant() == 0); // Gram-Schmidt Process @@ -617,7 +716,8 @@ Basis::Basis(const Vector3& p_axis, real_t p_phi) { } Basis::operator Quat() const { - ERR_FAIL_COND_V(is_rotation() == false, Quat()); + //commenting this check because precision issues cause it to fail when it shouldn't + //ERR_FAIL_COND_V(is_rotation() == false, Quat()); real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; real_t temp[4]; diff --git a/src/core/Color.cpp b/src/core/Color.cpp index 46d66092..4fea716d 100644 --- a/src/core/Color.cpp +++ b/src/core/Color.cpp @@ -388,7 +388,7 @@ String Color::to_html(bool p_alpha) const Color::operator String() const { - return String(); // @Todo + return String::num(r) + ", " + String::num(g) + ", " + String::num(b) + ", " + String::num(a); } diff --git a/src/core/Quat.cpp b/src/core/Quat.cpp index 14d4f45e..8739be38 100644 --- a/src/core/Quat.cpp +++ b/src/core/Quat.cpp @@ -7,6 +7,76 @@ namespace godot { +// set_euler_xyz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses XYZ convention (Z is the first rotation). +void Quat::set_euler_xyz(const Vector3 &p_euler) { + real_t half_a1 = p_euler.x * 0.5; + real_t half_a2 = p_euler.y * 0.5; + real_t half_a3 = p_euler.z * 0.5; + + // R = X(a1).Y(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-2) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = ::cos(half_a1); + real_t sin_a1 = ::sin(half_a1); + real_t cos_a2 = ::cos(half_a2); + real_t sin_a2 = ::sin(half_a2); + real_t cos_a3 = ::cos(half_a3); + real_t sin_a3 = ::sin(half_a3); + + set(sin_a1 * cos_a2 * cos_a3 + sin_a2 * sin_a3 * cos_a1, + -sin_a1 * sin_a3 * cos_a2 + sin_a2 * cos_a1 * cos_a3, + sin_a1 * sin_a2 * cos_a3 + sin_a3 * cos_a1 * cos_a2, + -sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); +} + +// get_euler_xyz returns a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses XYZ convention (Z is the first rotation). +Vector3 Quat::get_euler_xyz() const { + Basis m(*this); + return m.get_euler_xyz(); +} + +// set_euler_yxz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses YXZ convention (Z is the first rotation). +void Quat::set_euler_yxz(const Vector3 &p_euler) { + real_t half_a1 = p_euler.y * 0.5; + real_t half_a2 = p_euler.x * 0.5; + real_t half_a3 = p_euler.z * 0.5; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = ::cos(half_a1); + real_t sin_a1 = ::sin(half_a1); + real_t cos_a2 = ::cos(half_a2); + real_t sin_a2 = ::sin(half_a2); + real_t cos_a3 = ::cos(half_a3); + real_t sin_a3 = ::sin(half_a3); + + set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3, + sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3, + -sin_a1 * sin_a2 * cos_a3 + cos_a1 * sin_a2 * sin_a3, + sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); +} + +// get_euler_yxz returns a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses YXZ convention (Z is the first rotation). +Vector3 Quat::get_euler_yxz() const { + Basis m(*this); + return m.get_euler_yxz(); +} + real_t Quat::length() const { return ::sqrt(length_squared()); @@ -27,29 +97,6 @@ Quat Quat::inverse() const return Quat( -x, -y, -z, w ); } -void Quat::set_euler(const Vector3& p_euler) -{ - real_t half_a1 = p_euler.x * 0.5; - real_t half_a2 = p_euler.y * 0.5; - real_t half_a3 = p_euler.z * 0.5; - - // R = X(a1).Y(a2).Z(a3) convention for Euler angles. - // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-2) - // a3 is the angle of the first rotation, following the notation in this reference. - - real_t cos_a1 = ::cos(half_a1); - real_t sin_a1 = ::sin(half_a1); - real_t cos_a2 = ::cos(half_a2); - real_t sin_a2 = ::sin(half_a2); - real_t cos_a3 = ::cos(half_a3); - real_t sin_a3 = ::sin(half_a3); - - set(sin_a1*cos_a2*cos_a3 + sin_a2*sin_a3*cos_a1, - -sin_a1*sin_a3*cos_a2 + sin_a2*cos_a1*cos_a3, - sin_a1*sin_a2*cos_a3 + sin_a3*cos_a1*cos_a2, - -sin_a1*sin_a2*sin_a3 + cos_a1*cos_a2*cos_a3); -} - Quat Quat::slerp(const Quat& q, const real_t& t) const { Quat to1; @@ -263,11 +310,4 @@ bool Quat::operator!=(const Quat& p_quat) const { return x!=p_quat.x || y!=p_quat.y || z!=p_quat.z || w!=p_quat.w; } - -Vector3 Quat::get_euler() const -{ - Basis m(*this); - return m.get_euler(); -} - } diff --git a/src/core/String.cpp b/src/core/String.cpp index a277b60f..3d822b1c 100644 --- a/src/core/String.cpp +++ b/src/core/String.cpp @@ -24,6 +24,55 @@ const char *godot::CharString::get_data() const { return godot::api->godot_char_string_get_data(&_char_string); } +String String::num(double p_num, int p_decimals) { + String new_string; + new_string._godot_string = godot::api->godot_string_num_with_decimals(p_num, p_decimals); + + return new_string; +} + +String String::num_scientific(double p_num) { + String new_string; + new_string._godot_string = godot::api->godot_string_num_scientific(p_num); + + return new_string; +} + +String String::num_real(double p_num) { + String new_string; + new_string._godot_string = godot::api->godot_string_num_real(p_num); + + return new_string; +} + +String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { + String new_string; + new_string._godot_string = godot::api->godot_string_num_int64_capitalized(p_num, base, capitalize_hex); + + return new_string; +} + +String String::chr(godot_char_type p_char) { + String new_string; + new_string._godot_string = godot::api->godot_string_chr(p_char); + + return new_string; +} + +String String::md5(const uint8_t *p_md5) { + String new_string; + new_string._godot_string = godot::api->godot_string_md5(p_md5); + + return new_string; +} + +String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { + String new_string; + new_string._godot_string = godot::api->godot_string_hex_encode_buffer(p_buffer, p_len); + + return new_string; +} + godot::String::String() { godot::api->godot_string_new(&_godot_string); } diff --git a/src/core/Transform2D.cpp b/src/core/Transform2D.cpp index 1e96e981..e528465e 100644 --- a/src/core/Transform2D.cpp +++ b/src/core/Transform2D.cpp @@ -340,8 +340,7 @@ Transform2D Transform2D::interpolate_with(const Transform2D& p_transform, real_t Transform2D::operator String() const { - //return String(String()+elements[0]+", "+elements[1]+", "+elements[2]); - return String(); // @Todo + return String(String() + elements[0] + ", " + elements[1] + ", " + elements[2]); } } diff --git a/src/core/Vector2.cpp b/src/core/Vector2.cpp index 7dc78b69..ecb73789 100644 --- a/src/core/Vector2.cpp +++ b/src/core/Vector2.cpp @@ -252,7 +252,7 @@ Vector2 Vector2::snapped(const Vector2& p_by) const Vector2::operator String() const { - return String(); /* @Todo String::num() */ + return String::num(x) + ", " + String::num(y); } diff --git a/src/core/Vector3.cpp b/src/core/Vector3.cpp index 7e0f2eeb..a22bdc0b 100644 --- a/src/core/Vector3.cpp +++ b/src/core/Vector3.cpp @@ -327,7 +327,7 @@ Vector3 Vector3::snapped(const float by) Vector3::operator String() const { - return String(); // @Todo + return String::num(x) + ", " + String::num(y) + ", " + String::num(z); }