diff options
Diffstat (limited to 'core/math')
-rw-r--r-- | core/math/geometry.h | 28 | ||||
-rw-r--r-- | core/math/math_2d.cpp | 13 | ||||
-rw-r--r-- | core/math/math_2d.h | 5 | ||||
-rw-r--r-- | core/math/math_funcs.cpp | 15 | ||||
-rw-r--r-- | core/math/math_funcs.h | 14 | ||||
-rw-r--r-- | core/math/matrix3.cpp | 63 | ||||
-rw-r--r-- | core/math/matrix3.h | 15 | ||||
-rw-r--r-- | core/math/quat.cpp | 2 | ||||
-rw-r--r-- | core/math/quick_hull.cpp | 2 | ||||
-rw-r--r-- | core/math/transform.cpp | 4 | ||||
-rw-r--r-- | core/math/triangulate.cpp | 42 | ||||
-rw-r--r-- | core/math/triangulate.h | 5 | ||||
-rw-r--r-- | core/math/vector3.h | 6 |
13 files changed, 167 insertions, 47 deletions
diff --git a/core/math/geometry.h b/core/math/geometry.h index ca4363e129..be998aef0b 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -502,16 +502,15 @@ public: } static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { - int as_x = s.x - a.x; - int as_y = s.y - a.y; + Vector2 an = a - s; + Vector2 bn = b - s; + Vector2 cn = c - s; - bool s_ab = (b.x - a.x) * as_y - (b.y - a.y) * as_x > 0; + bool orientation = an.cross(bn) > 0; - if (((c.x - a.x) * as_y - (c.y - a.y) * as_x > 0) == s_ab) return false; + if ((bn.cross(cn) > 0) != orientation) return false; - if (((c.x - b.x) * (s.y - b.y) - (c.y - b.y) * (s.x - b.x) > 0) != s_ab) return false; - - return true; + return (cn.cross(an) > 0) == orientation; } static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon); @@ -530,6 +529,21 @@ public: return p_segment[0] + n * d; // inside } + static bool line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) { + + // see http://paulbourke.net/geometry/pointlineplane/ + + const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y; + if (Math::abs(denom) < CMP_EPSILON) { // parallel? + return false; + } + + const Vector2 v = p_from_a - p_from_b; + const real_t t = (p_dir_b.x * v.y - p_dir_b.y * v.x) / denom; + r_result = p_from_a + t * p_dir_a; + return true; + } + static bool segment_intersects_segment_2d(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b, Vector2 *r_result) { Vector2 B = p_to_a - p_from_a; diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp index d2e4101999..a053ffbd93 100644 --- a/core/math/math_2d.cpp +++ b/core/math/math_2d.cpp @@ -98,14 +98,19 @@ real_t Vector2::cross(const Vector2 &p_other) const { return x * p_other.y - y * p_other.x; } -Vector2 Vector2::cross(real_t p_other) const { +Vector2 Vector2::floor() const { - return Vector2(p_other * y, -p_other * x); + return Vector2(Math::floor(x), Math::floor(y)); } -Vector2 Vector2::floor() const { +Vector2 Vector2::ceil() const { - return Vector2(Math::floor(x), Math::floor(y)); + return Vector2(Math::ceil(x), Math::ceil(y)); +} + +Vector2 Vector2::round() const { + + return Vector2(Math::round(x), Math::round(y)); } Vector2 Vector2::rotated(real_t p_by) const { diff --git a/core/math/math_2d.h b/core/math/math_2d.h index 8928349a44..611d47e3ff 100644 --- a/core/math/math_2d.h +++ b/core/math/math_2d.h @@ -104,7 +104,6 @@ struct Vector2 { real_t dot(const Vector2 &p_other) const; real_t cross(const Vector2 &p_other) const; - Vector2 cross(real_t p_other) const; Vector2 project(const Vector2 &p_vec) const; Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; @@ -163,6 +162,8 @@ struct Vector2 { } Vector2 floor() const; + Vector2 ceil() const; + Vector2 round() const; Vector2 snapped(const Vector2 &p_by) const; real_t aspect() const { return width / height; } @@ -304,7 +305,7 @@ struct Rect2 { inline real_t distance_to(const Vector2 &p_point) const { - real_t dist; + real_t dist = 0.0; bool inside = true; if (p_point.x < position.x) { diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index f060a8e4ab..5c8512d8bd 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -177,18 +177,3 @@ float Math::random(float from, float to) { float ret = (float)r / (float)RANDOM_MAX; return (ret) * (to - from) + from; } - -int Math::wrapi(int value, int min, int max) { - --max; - int rng = max - min + 1; - value = ((value - min) % rng); - if (value < 0) - return max + 1 + value; - else - return min + value; -} - -float Math::wrapf(float value, float min, float max) { - float rng = max - min; - return min + (value - min) - (rng * floor((value - min) / rng)); -} diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index e15abc6b50..20001bb9a6 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -209,8 +209,18 @@ public: static _ALWAYS_INLINE_ double round(double p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); } static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); } - static int wrapi(int value, int min, int max); - static float wrapf(float value, float min, float max); + static _ALWAYS_INLINE_ int wrapi(int value, int min, int max) { + int rng = max - min; + return min + ((((value - min) % rng) + rng) % rng); + } + static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) { + double rng = max - min; + return value - (rng * Math::floor((value - min) / rng)); + } + static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) { + float rng = max - min; + return value - (rng * Math::floor((value - min) / rng)); + } // double only, as these functions are mainly used by the editor and not performance-critical, static double ease(double p_x, double p_c); diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp index 189b1ef9b3..b0b05d1ec8 100644 --- a/core/math/matrix3.cpp +++ b/core/math/matrix3.cpp @@ -254,7 +254,7 @@ void Basis::set_scale(const Vector3 &p_scale) { set_axis(2, get_axis(2).normalized() * p_scale.z); } -Vector3 Basis::get_scale() const { +Vector3 Basis::get_scale_abs() const { return Vector3( Vector3(elements[0][0], elements[1][0], elements[2][0]).length(), @@ -262,7 +262,13 @@ Vector3 Basis::get_scale() const { Vector3(elements[0][2], elements[1][2], elements[2][2]).length()); } -Vector3 Basis::get_signed_scale() const { +Vector3 Basis::get_scale_local() const { + real_t det_sign = determinant() > 0 ? 1 : -1; + return det_sign * Vector3(elements[0].length(), elements[1].length(), elements[2].length()); +} + +// get_scale works with get_rotation, use get_scale_abs if you need to enforce positive signature. +Vector3 Basis::get_scale() const { // FIXME: We are assuming M = R.S (R is rotation and S is scaling), and use polar decomposition to extract R and S. // A polar decomposition is M = O.P, where O is an orthogonal matrix (meaning rotation and reflection) and // P is a positive semi-definite matrix (meaning it contains absolute values of scaling along its diagonal). @@ -342,6 +348,14 @@ void Basis::rotate(const Vector3 &p_euler) { *this = rotated(p_euler); } +Basis Basis::rotated(const Quat &p_quat) const { + return Basis(p_quat) * (*this); +} + +void Basis::rotate(const Quat &p_quat) { + *this = rotated(p_quat); +} + // TODO: rename this to get_rotation_euler Vector3 Basis::get_rotation() const { // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, @@ -371,6 +385,22 @@ void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const { m.get_axis_angle(p_axis, p_angle); } +void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const { + // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S, + // and returns the Euler angles corresponding to the rotation part, complementing get_scale(). + // See the comment in get_scale() for further information. + Basis m = transposed(); + m.orthonormalize(); + real_t det = m.determinant(); + if (det < 0) { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + m.scale(Vector3(-1, -1, -1)); + } + + m.get_axis_angle(p_axis, p_angle); + p_angle = -p_angle; +} + // 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). @@ -767,3 +797,32 @@ void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { elements[2][1] = p_axis.y * p_axis.z * (1.0 - cosine) + p_axis.x * sine; elements[2][2] = axis_sq.z + cosine * (1.0 - axis_sq.z); } + +void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { + set_diagonal(p_scale); + rotate(p_axis, p_phi); +} + +void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) { + set_diagonal(p_scale); + rotate(p_euler); +} + +void Basis::set_quat_scale(const Quat &p_quat, const Vector3 &p_scale) { + set_diagonal(p_scale); + rotate(p_quat); +} + +void Basis::set_diagonal(const Vector3 p_diag) { + elements[0][0] = p_diag.x; + elements[0][1] = 0; + elements[0][2] = 0; + + elements[1][0] = 0; + elements[1][1] = p_diag.y; + elements[1][2] = 0; + + elements[2][0] = 0; + elements[2][1] = 0; + elements[2][2] = p_diag.z; +} diff --git a/core/math/matrix3.h b/core/math/matrix3.h index c426435729..fd383fc673 100644 --- a/core/math/matrix3.h +++ b/core/math/matrix3.h @@ -81,8 +81,12 @@ public: void rotate(const Vector3 &p_euler); Basis rotated(const Vector3 &p_euler) const; + void rotate(const Quat &p_quat); + Basis rotated(const Quat &p_quat) const; + Vector3 get_rotation() const; void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const; + void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const; Vector3 rotref_posscale_decomposition(Basis &rotref) const; @@ -108,7 +112,12 @@ public: void set_scale(const Vector3 &p_scale); Vector3 get_scale() const; - Vector3 get_signed_scale() const; + Vector3 get_scale_abs() const; + Vector3 get_scale_local() const; + + void set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale); + void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale); + void set_quat_scale(const Quat &p_quat, const Vector3 &p_scale); // transposed dot products _FORCE_INLINE_ real_t tdotx(const Vector3 &v) const { @@ -140,6 +149,8 @@ public: int get_orthogonal_index() const; void set_orthogonal_index(int p_index); + void set_diagonal(const Vector3 p_diag); + bool is_orthogonal() const; bool is_diagonal() const; bool is_rotation() const; @@ -219,6 +230,8 @@ public: Basis(const Quat &p_quat) { set_quat(p_quat); }; Basis(const Vector3 &p_euler) { set_euler(p_euler); } Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } + Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } + Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { elements[0] = row0; diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 9aa8b537d2..4f61401ac7 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -89,7 +89,7 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) { 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 * cos_a3 + cos_a1 * cos_a2 * sin_a3, sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); } diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 102e454e02..fc90417413 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -74,7 +74,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me int longest_axis = aabb.get_longest_axis_index(); //first two vertices are the most distant - int simplex[4]; + int simplex[4] = { 0 }; { real_t max = 0, min = 0; diff --git a/core/math/transform.cpp b/core/math/transform.cpp index f727d00e30..7cd186ca60 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -119,11 +119,11 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) /* not sure if very "efficient" but good enough? */ - Vector3 src_scale = basis.get_signed_scale(); + Vector3 src_scale = basis.get_scale(); Quat src_rot = basis.orthonormalized(); Vector3 src_loc = origin; - Vector3 dst_scale = p_transform.basis.get_signed_scale(); + Vector3 dst_scale = p_transform.basis.get_scale(); Quat dst_rot = p_transform.basis; Vector3 dst_loc = p_transform.origin; diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 957e16f92c..563ba7268f 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -51,7 +51,8 @@ real_t Triangulate::get_area(const Vector<Vector2> &contour) { bool Triangulate::is_inside_triangle(real_t Ax, real_t Ay, real_t Bx, real_t By, real_t Cx, real_t Cy, - real_t Px, real_t Py) + real_t Px, real_t Py, + bool include_edges) { real_t ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; @@ -74,10 +75,14 @@ bool Triangulate::is_inside_triangle(real_t Ax, real_t Ay, cCROSSap = cx * apy - cy * apx; bCROSScp = bx * cpy - by * cpx; - return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); + if (include_edges) { + return ((aCROSSbp > 0.0) && (bCROSScp > 0.0) && (cCROSSap > 0.0)); + } else { + return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); + } }; -bool Triangulate::snip(const Vector<Vector2> &p_contour, int u, int v, int w, int n, const Vector<int> &V) { +bool Triangulate::snip(const Vector<Vector2> &p_contour, int u, int v, int w, int n, const Vector<int> &V, bool relaxed) { int p; real_t Ax, Ay, Bx, By, Cx, Cy, Px, Py; const Vector2 *contour = &p_contour[0]; @@ -91,13 +96,20 @@ bool Triangulate::snip(const Vector<Vector2> &p_contour, int u, int v, int w, in Cx = contour[V[w]].x; Cy = contour[V[w]].y; - if (CMP_EPSILON > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) return false; + // It can happen that the triangulation ends up with three aligned vertices to deal with. + // In this scenario, making the check below strict may reject the possibility of + // forming a last triangle with these aligned vertices, preventing the triangulatiom + // from completing. + // To avoid that we allow zero-area triangles if all else failed. + float threshold = relaxed ? -CMP_EPSILON : CMP_EPSILON; + + if (threshold > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax)))) return false; for (p = 0; p < n; p++) { if ((p == u) || (p == v) || (p == w)) continue; Px = contour[V[p]].x; Py = contour[V[p]].y; - if (is_inside_triangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py)) return false; + if (is_inside_triangle(Ax, Ay, Bx, By, Cx, Cy, Px, Py, relaxed)) return false; } return true; @@ -121,6 +133,8 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul for (int v = 0; v < n; v++) V[v] = (n - 1) - v; + bool relaxed = false; + int nv = n; /* remove nv-2 Vertices, creating 1 triangle every time */ @@ -129,8 +143,20 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul for (int v = nv - 1; nv > 2;) { /* if we loop, it is probably a non-simple polygon */ if (0 >= (count--)) { - //** Triangulate: ERROR - probable bad polygon! - return false; + if (relaxed) { + //** Triangulate: ERROR - probable bad polygon! + return false; + } else { + // There may be aligned vertices that the strict + // checks prevent from triangulating. In this situation + // we are better off adding flat triangles than + // failing, so we relax the checks and try one last + // round. + // Only relaxing the constraints as a last resort avoids + // degenerate triangles when they aren't necessary. + count = 2 * nv; + relaxed = true; + } } /* three consecutive vertices in current polygon, <u,v,w> */ @@ -141,7 +167,7 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul int w = v + 1; if (nv <= w) w = 0; /* next */ - if (snip(contour, u, v, w, nv, V)) { + if (snip(contour, u, v, w, nv, V, relaxed)) { int a, b, c, s, t; /* true names of the vertices */ diff --git a/core/math/triangulate.h b/core/math/triangulate.h index e336dc5756..b1a583d0c5 100644 --- a/core/math/triangulate.h +++ b/core/math/triangulate.h @@ -51,10 +51,11 @@ public: static bool is_inside_triangle(real_t Ax, real_t Ay, real_t Bx, real_t By, real_t Cx, real_t Cy, - real_t Px, real_t Py); + real_t Px, real_t Py, + bool include_edges); private: - static bool snip(const Vector<Vector2> &p_contour, int u, int v, int w, int n, const Vector<int> &V); + static bool snip(const Vector<Vector2> &p_contour, int u, int v, int w, int n, const Vector<int> &V, bool relaxed); }; #endif diff --git a/core/math/vector3.h b/core/math/vector3.h index 10ec4f5641..3bbfd7627c 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -103,6 +103,7 @@ struct Vector3 { _FORCE_INLINE_ Vector3 floor() const; _FORCE_INLINE_ Vector3 sign() const; _FORCE_INLINE_ Vector3 ceil() const; + _FORCE_INLINE_ Vector3 round() const; _FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const; _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const; @@ -204,6 +205,11 @@ Vector3 Vector3::ceil() const { return Vector3(Math::ceil(x), Math::ceil(y), Math::ceil(z)); } +Vector3 Vector3::round() const { + + return Vector3(Math::round(x), Math::round(y), Math::round(z)); +} + Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { return Vector3( |