Compare commits
37 Commits
Prerelease
...
Release-1
Author | SHA1 | Date | |
---|---|---|---|
d7b2157b0c | |||
0aff68b63e | |||
815e914c7f | |||
b2b5fd841d | |||
fb7aba71b1 | |||
de108630b6 | |||
f6abe5c430 | |||
4085a1700c | |||
06bb959e3f | |||
802c321115 | |||
d1529f05b0 | |||
dc41dcf520 | |||
f4c6337f12 | |||
212c1d3bc4 | |||
d60c71373b | |||
4cb497be29 | |||
cd58676ece | |||
9f60f296c6 | |||
e8ed68f3c7 | |||
44b8bb8172 | |||
4aaf430f68 | |||
232bfebbef | |||
c50719de36 | |||
405800dbc5 | |||
718f63a3c8 | |||
8049fd3a60 | |||
2e7bba8d87 | |||
c5628b028b | |||
fd2e3f894a | |||
efead577a5 | |||
92a20a9347 | |||
00c0d30d6d | |||
8fa94c1519 | |||
e18a2634de | |||
cb5e6b4f99 | |||
9a5f12e505 | |||
d37b685df9 |
@@ -29,35 +29,8 @@ file(GLOB_RECURSE J3ML_SRC "src/J3ML/*.c" "src/J3ML/*.cpp")
|
||||
include_directories("include")
|
||||
|
||||
add_library(J3ML SHARED ${J3ML_SRC}
|
||||
src/J3ML/LinearAlgebra/AxisAngle.cpp
|
||||
include/J3ML/LinearAlgebra/Vector.h
|
||||
include/J3ML/Geometry/Plane.h
|
||||
include/J3ML/Geometry/AABB.h
|
||||
include/J3ML/Geometry/Frustum.h
|
||||
include/J3ML/Geometry/OBB.h
|
||||
include/J3ML/Geometry/Capsule.h
|
||||
include/J3ML/Geometry/Sphere.h
|
||||
include/J3ML/Geometry/Ray.h
|
||||
include/J3ML/Geometry/QuadTree.h
|
||||
include/J3ML/Geometry/LineSegment.h
|
||||
include/J3ML/Geometry/TriangleMesh.h
|
||||
include/J3ML/Geometry/Polygon.h
|
||||
include/J3ML/Geometry/Triangle.h
|
||||
include/J3ML/Geometry/Triangle2D.h
|
||||
src/J3ML/Geometry/AABB.cpp
|
||||
src/J3ML/Geometry/Plane.cpp
|
||||
src/J3ML/Geometry/Sphere.cpp
|
||||
src/J3ML/Geometry/Frustum.cpp
|
||||
src/J3ML/Geometry/OBB.cpp
|
||||
src/J3ML/Geometry/Ray.cpp
|
||||
src/J3ML/Geometry/Capsule.cpp
|
||||
src/J3ML/Geometry/TriangleMesh.cpp
|
||||
src/J3ML/Geometry/QuadTree.cpp
|
||||
src/J3ML/Geometry/LineSegment.cpp
|
||||
include/J3ML/Geometry/AABB2D.h
|
||||
src/J3ML/Geometry/Polygon.cpp
|
||||
include/J3ML/Geometry/Polyhedron.h
|
||||
src/J3ML/Geometry/Polyhedron.cpp)
|
||||
include/J3ML/Geometry/Common.h
|
||||
src/J3ML/Geometry/Triangle.cpp)
|
||||
set_target_properties(J3ML PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
|
||||
|
42
include/J3ML/Algorithm/DifferentialSolvers.h
Normal file
42
include/J3ML/Algorithm/DifferentialSolvers.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Created by dawsh on 2/8/24.
|
||||
//
|
||||
|
||||
namespace J3ML::Algorithm
|
||||
{
|
||||
/// Implementations for a variety of Differential Equation Solving algorithms
|
||||
namespace Solvers
|
||||
{
|
||||
|
||||
// Consider a differential equation
|
||||
// dy/dx = (x + y + xy)
|
||||
float eq(float x, float y)
|
||||
{
|
||||
return (x + y + x*y);
|
||||
}
|
||||
|
||||
// Accelleration = Velocity / Time
|
||||
// Velocity = Position / Time
|
||||
// Position = Vector3
|
||||
|
||||
//
|
||||
|
||||
float euler(float x0, float y, float h, float x)
|
||||
{
|
||||
float temp = -0.f;
|
||||
// Iterating till the point at which we need approximation
|
||||
while (x0 < x) {
|
||||
temp = y;
|
||||
y = y + h * eq(x0, y);
|
||||
x0 = x0 + h;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
class EulerMethodSolver {};
|
||||
class SemiImplicitEulerMethodSolver {};
|
||||
class GaussSeidelMethodSolver {};
|
||||
class GradientDescentSolver {};
|
||||
class VerletIntegrationSolver {};
|
||||
}
|
||||
}
|
60
include/J3ML/Algorithm/GJK.h
Normal file
60
include/J3ML/Algorithm/GJK.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// @file GJK.h
|
||||
/// Implementation of the Gilbert-Johnson-Keerthi (GJK) convex polyhedron intersection test
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/Geometry.h>
|
||||
#pragma once
|
||||
|
||||
namespace J3ML::Algorithms
|
||||
{
|
||||
|
||||
Vector3 UpdateSimplex(Vector3 *s, int &n);
|
||||
|
||||
#define SUPPORT(dir, minS, maxS) (a.ExtremePoint(dir, maxS) - b.ExtremePoint(-dir, minS));
|
||||
|
||||
template <typename A, typename B>
|
||||
bool GJKIntersect(const A &a, const B &b)
|
||||
{
|
||||
Vector3 support[4];
|
||||
// Start with an arbitrary point in the Minkowski set shape.
|
||||
support[0] = a.AnyPointFast() - b.AnyPointFast();
|
||||
if (support[0].LengthSquared() < 1e-7f) // Robustness check: Test if the first arbitrary point we guessed produced the zero vector we are looking for!
|
||||
return true;
|
||||
Vector3 d = -support[0]; // First search direction is straight toward the origin from the found point.
|
||||
int n = 1; // Stores the current number of points in the search simplex.
|
||||
int nIterations = 50; // Robustness check: Limit the maximum number of iterations to perform to avoid infinite loop if types A or B are buggy!
|
||||
while (nIterations-- > 0)
|
||||
{
|
||||
// Compute the extreme point to the direction d in the Minkowski set shape.
|
||||
float maxS, minS;
|
||||
Vector3 newSupport = SUPPORT(d, minS, maxS);
|
||||
// If the most extreme point in that search direction did not walk past the origin, then the origin cannot be contained in the Minkowski
|
||||
// convex shape, and the two convex objects a and b do not share a common point - no intersection!
|
||||
if (minS + maxS < 0.f)
|
||||
return false;
|
||||
|
||||
// Add the newly evaluated point to the search simplex
|
||||
assert(n < 4);
|
||||
support[n++] = newSupport;
|
||||
// Examine the current simplex, prune a redundant part of it, and produce the next search direction.
|
||||
d = UpdateSimplex(support, n);
|
||||
|
||||
if (n == 0) // Was the origin contained in the current simplex? If so, then the convex shapes a and b do share a common point - intersection!
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This computes GJL intersection, but by first translating both objects to a coordinate frame that is as closely
|
||||
// centered around world origin as possible, to gain floating point precision.
|
||||
template <typename A, typename B>
|
||||
bool FloatingPointOffsetedGJKIntersect(const A &a, const B &b)
|
||||
{
|
||||
AABB ab = a.MinimalEnclosingAABB();
|
||||
AABB bb = b.MinimalEnclosingAABB();
|
||||
|
||||
Vector3 offset = (Vector3::Min(ab.minPoint, bb.minPoint) + Vector3::Max(ab.maxPoint, bb.maxPoint)) * 0.5f;
|
||||
const Vector3 floatingPtPrecisionOffset = -offset;
|
||||
return GJLIntersect(a.Translated(floatingPtPrecisionOffset), b.Translated(floatingPtPrecisionOffset));
|
||||
}
|
||||
}
|
99
include/J3ML/Algorithm/RNG.h
Normal file
99
include/J3ML/Algorithm/RNG.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "J3ML/J3ML.h"
|
||||
|
||||
namespace J3ML::Algorithm
|
||||
{
|
||||
/** @brief A linear congruential random number generator.
|
||||
|
||||
Uses D.H. Lehmer's Linear Congruential Method (1949) for generating random numbers.
|
||||
Supports both Multiplicative Congruential Method (increment==0) and
|
||||
Mixed Congruential Method (increment!=0)
|
||||
It is perhaps the simplest and fastest method to generate pseudo-random numbers on
|
||||
a computer. Per default uses the values for Minimal Standard LCG.
|
||||
http://en.wikipedia.org/wiki/Linear_congruential_generator
|
||||
http://www.math.rutgers.edu/~greenfie/currentcourses/sem090/pdfstuff/jp.pdf
|
||||
|
||||
Pros:
|
||||
<ul>
|
||||
<li> Easy to implement.
|
||||
<li> Fast.
|
||||
</ul>
|
||||
|
||||
Cons:
|
||||
<ul>
|
||||
<li> NOT safe for cryptography because of the easily calculatable sequential
|
||||
correlation between successive calls. A case study:
|
||||
http://www.cigital.com/papers/download/developer_gambling.php
|
||||
|
||||
<li> Tends to have less random low-order bits (compared to the high-order bits)
|
||||
Thus, NEVER do something like this:
|
||||
|
||||
u32 numBetween1And10 = 1 + LCGRand.Int() % 10;
|
||||
|
||||
Instead, take into account EVERY bit of the generated number, like this:
|
||||
|
||||
u32 numBetween1And10 = 1 + (int)(10.0 * (double)LCGRand.Int()
|
||||
/(LCGRand.Max()+1.0));
|
||||
or simply
|
||||
|
||||
u32 numBetween1And10 = LCGRand.Float(1.f, 10.f);
|
||||
</ul> */
|
||||
|
||||
|
||||
class RNG {
|
||||
public:
|
||||
/// Initializes the generator from the current system clock.
|
||||
RNG();
|
||||
RNG(u32 seed, u32 multiplier = 69621,
|
||||
u32 increment = 0, u32 modulus = 0x7FFFFFFF) // 2^31 - 1
|
||||
{
|
||||
Seed(seed, multiplier, increment, modulus);
|
||||
}
|
||||
/// Reinitializes the generator to the new settings.
|
||||
void Seed(u32 seed, u32 multiplier, u32 increment, u32 modulus = 0x7FFFFFFF);
|
||||
/// Returns a random integer picked uniformly in the range [0, MaxInt()]
|
||||
u32 Int();
|
||||
/// Returns the biggest number the generator can yield. [modulus - 1]
|
||||
u32 MaxInt() const { return modulus - 1;}
|
||||
/// Returns a random integer picked uniformly in the range [0, 2^32-1].
|
||||
/// @note The configurable modulus and increment are not used by this function, but are always increment == 0, modulus=2^32
|
||||
u32 IntFast();
|
||||
/// Returns a random integer picked uniformly in the range [a, b]
|
||||
/// @param a Lower bound, inclusive.
|
||||
/// @param b Upper bound, inclusive.
|
||||
/// @return A random integer picked uniformly in the range [a, b]
|
||||
int Int(int a, int b);
|
||||
|
||||
/// Returns a random float picked uniformly in the range [0, 1].
|
||||
float Float();
|
||||
|
||||
/// Returns a random float picked uniformly in the range [0, 1].
|
||||
/// @note this is much slower than Float()! Prefer that function if possible.
|
||||
float Float01Incl();
|
||||
|
||||
/// Returns a random float picked uniformly in the range ]-1, 1[.
|
||||
/// @note This function has one more bit of randomness compared to Float(), but has a theoretical bias
|
||||
/// towards 0.0, since floating point has two representations for 0 (+0, and -0).
|
||||
float FloatNeg1_1();
|
||||
|
||||
/// Returns a random float picked uniformly in the range [a, b[.
|
||||
/// @param a Lower bound, inclusive.
|
||||
/// @param b Upper bound, exclusive.
|
||||
/// @return A random float picked uniformly in the range [a, b[
|
||||
/// @note This function is slower than RNG::FloatIncl(). If you don't care about the open/closed interval, prefer that function.
|
||||
float Float(float a, float b);
|
||||
|
||||
/// Returns a random float picked uniformly in the range [a, b].
|
||||
/// @param a Lower bound, inclusive.
|
||||
/// @param b Upper bound, inclusive.
|
||||
/// @return A random float picked uniformly in the range [a, b]
|
||||
float FloatIncl(float a, float b);
|
||||
|
||||
u32 multiplier;
|
||||
u32 increment;
|
||||
u32 modulus;
|
||||
u32 lastNumber;
|
||||
};
|
||||
}
|
21
include/J3ML/Algorithm/Spring.h
Normal file
21
include/J3ML/Algorithm/Spring.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by dawsh on 2/8/24.
|
||||
//
|
||||
|
||||
namespace J3ML::Algorithm
|
||||
{
|
||||
// Numerical model of a "Spring" object
|
||||
// Simulates any oscillating system i.e. a mass suspended from a spring.
|
||||
class Spring
|
||||
{
|
||||
float Dampening;
|
||||
float Stiffness;
|
||||
float Goal;
|
||||
float RestLength = 1.f;
|
||||
|
||||
bool Overdamped() const;
|
||||
bool Undamped() const;
|
||||
bool Underdamped() const;
|
||||
bool CriticallyDamped() const;
|
||||
};
|
||||
}
|
@@ -1,32 +1,23 @@
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Geometry {
|
||||
using Vector2 = LinearAlgebra::Vector2;
|
||||
using Vector3 = LinearAlgebra::Vector3;
|
||||
#include <J3ML/Geometry/AABB2D.h>
|
||||
#include <J3ML/Geometry/Plane.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
#include <J3ML/Geometry/Line.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Frustum.h>
|
||||
#include <J3ML/Geometry/OBB.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
#include <J3ML/Geometry/QuadTree.h>
|
||||
#include <J3ML/Geometry/Ray.h>
|
||||
#include <J3ML/Geometry/Shape.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
#include <J3ML/Geometry/Triangle.h>
|
||||
#include <J3ML/Geometry/Triangle2D.h>
|
||||
#include <J3ML/Geometry/TriangleMesh.h>
|
||||
|
||||
class LineSegment2D
|
||||
{
|
||||
Vector2 A;
|
||||
Vector2 B;
|
||||
};
|
||||
|
||||
class Rectangle;
|
||||
class AABB;
|
||||
class OBB;
|
||||
class Capsule;
|
||||
class Frustum;
|
||||
class OBB2D;
|
||||
class Line2D;
|
||||
class Ray2D;
|
||||
class Triangle2D;
|
||||
class Polygon2D;
|
||||
|
||||
|
||||
struct IntersectionResult2D {};
|
||||
|
||||
bool Intersects2D(LineSegment2D seg, Rectangle rect);
|
||||
IntersectionResult2D GetIntersection2D(LineSegment2D seg, Rectangle rect);
|
||||
}
|
||||
using namespace J3ML::Geometry;
|
@@ -1,31 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/Geometry.h>
|
||||
|
||||
#include <J3ML/Geometry/Plane.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
#include <J3ML/Geometry/OBB.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Triangle.h>
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
#include <J3ML/Geometry/Frustum.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <J3ML/Geometry/Ray.h>
|
||||
#include <J3ML/Geometry/TriangleMesh.h>
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include <J3ML/Geometry/Shape.h>
|
||||
#include "J3ML/Algorithm/RNG.h"
|
||||
|
||||
|
||||
// TODO: Fix circular include between AABB and OBB
|
||||
|
||||
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
using namespace LinearAlgebra;
|
||||
|
||||
using namespace J3ML::LinearAlgebra;
|
||||
using J3ML::Algorithm::RNG;
|
||||
// A 3D axis-aligned bounding box
|
||||
// This data structure can be used to represent coarse bounds of objects, in situations where detailed triangle-level
|
||||
// computations can be avoided. In physics systems, bounding boxes are used as an efficient early-out test for geometry
|
||||
@@ -35,106 +21,90 @@ namespace Geometry
|
||||
// be arbitrarily oriented in the space with respect to each other.
|
||||
// If you need to represent a box in 3D space with arbitrary orientation, see the class OBB. */
|
||||
|
||||
class AABB
|
||||
{
|
||||
class AABB : public Shape {
|
||||
public:
|
||||
Vector3 minPoint;
|
||||
Vector3 maxPoint;
|
||||
|
||||
public:
|
||||
static int NumFaces() { return 6; }
|
||||
static int NumEdges() { return 12;}
|
||||
static int NumVertices() { return 8;}
|
||||
static int NumEdges() { return 12; }
|
||||
static int NumVertices() { return 8; }
|
||||
public:
|
||||
AABB();
|
||||
AABB(const Vector3& min, const Vector3& max);
|
||||
|
||||
static AABB FromCenterAndSize(const Vector3& center, const Vector3& size)
|
||||
{
|
||||
Vector3 halfSize = size * 0.5f;
|
||||
return {center - halfSize, center + halfSize};
|
||||
}
|
||||
float MinX() const { return minPoint.x; }
|
||||
float MinY() const { return minPoint.y; }
|
||||
float MinZ() const { return minPoint.z; }
|
||||
Vector3 HalfDiagonal() const { return HalfSize(); }
|
||||
|
||||
float MaxX() const { return maxPoint.x; }
|
||||
float MaxY() const { return maxPoint.y; }
|
||||
float MaxZ() const { return maxPoint.z; }
|
||||
static AABB FromCenterAndSize(const Vector3 ¢er, const Vector3 &size);
|
||||
|
||||
float MinX() const;
|
||||
float MinY() const;
|
||||
float MinZ() const;
|
||||
float MaxX() const;
|
||||
float MaxY() const;
|
||||
float MaxZ() const;
|
||||
|
||||
/// Returns the smallest sphere that contains this AABB.
|
||||
/// This function computes the minimal volume sphere that contains all the points inside this AABB
|
||||
Sphere MinimalEnclosingSphere() const
|
||||
{
|
||||
return Sphere(Centroid(), Size().Length()*0.5f);
|
||||
}
|
||||
Sphere MinimalEnclosingSphere() const;
|
||||
|
||||
Vector3 HalfSize() const {
|
||||
return this->Size()/2.f;
|
||||
}
|
||||
Vector3 HalfSize() const;
|
||||
|
||||
/// Returns the largest sphere that can fit inside this AABB
|
||||
/// This function computes the largest sphere that can fit inside this AABB.
|
||||
Sphere MaximalContainedSphere() const;
|
||||
|
||||
bool IsFinite() const;
|
||||
|
||||
Vector3 Centroid() const;
|
||||
|
||||
Vector3 Size() const;
|
||||
|
||||
// Returns the largest sphere that can fit inside this AABB
|
||||
// This function computes the largest sphere that can fit inside this AABB.
|
||||
Sphere MaximalContainedSphere() const
|
||||
{
|
||||
Vector3 halfSize = HalfSize();
|
||||
return Sphere(Centroid(), std::min(halfSize.x, std::min(halfSize.y, halfSize.z)));
|
||||
}
|
||||
bool IsFinite() const
|
||||
{
|
||||
return minPoint.IsFinite() && maxPoint.IsFinite();
|
||||
}
|
||||
Vector3 Centroid() const
|
||||
{
|
||||
return (minPoint+maxPoint) * 0.5f;
|
||||
}
|
||||
Vector3 Size() const
|
||||
{
|
||||
return this->maxPoint - this->minPoint;
|
||||
}
|
||||
// Quickly returns an arbitrary point inside this AABB
|
||||
Vector3 AnyPointFast() const;
|
||||
|
||||
Vector3 PointInside(float x, float y, float z) const
|
||||
{
|
||||
Vector3 d = maxPoint - minPoint;
|
||||
return minPoint + d.Mul({x, y, z});
|
||||
}
|
||||
Vector3 PointInside(float x, float y, float z) const;
|
||||
|
||||
// Returns an edge of this AABB
|
||||
LineSegment Edge(int edgeIndex) const
|
||||
{
|
||||
switch(edgeIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return LineSegment(minPoint, {minPoint.x, minPoint.y, maxPoint.z});
|
||||
}
|
||||
}
|
||||
Vector3 CornerPoint(int cornerIndex);
|
||||
Vector3 ExtremePoint(const Vector3& direction) const;
|
||||
Vector3 ExtremePoint(const Vector3& direction, float projectionDistance);
|
||||
LineSegment Edge(int edgeIndex) const;
|
||||
|
||||
Vector3 CornerPoint(int cornerIndex) const;
|
||||
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
Vector3 PointOnEdge(int edgeIndex, float u) const;
|
||||
|
||||
Vector3 FaceCenterPoint(int faceIndex) const;
|
||||
|
||||
Vector3 FacePoint(int faceIndex, float u, float v) const;
|
||||
|
||||
Vector3 FaceNormal(int faceIndex) const;
|
||||
Plane FacePlane(int faceIndex);
|
||||
static AABB MinimalEnclosingAABB(const Vector3* pointArray, int numPoints);
|
||||
Vector3 GetVolume();
|
||||
float GetVolumeCubed();
|
||||
float GetSurfaceArea();
|
||||
Vector3 GetRandomPointInside();
|
||||
Vector3 GetRandomPointOnSurface();
|
||||
Vector3 GetRandomPointOnEdge();
|
||||
Vector3 GetRandomCornerPoint();
|
||||
|
||||
Plane FacePlane(int faceIndex) const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
static AABB MinimalEnclosingAABB(const Vector3 *pointArray, int numPoints);
|
||||
float GetVolume() const;
|
||||
float GetSurfaceArea() const;
|
||||
Vector3 GetClosestPoint(const Vector3& point) const;
|
||||
|
||||
void Translate(const Vector3& offset);
|
||||
AABB Translated(const Vector3& offset) const;
|
||||
AABB TransformAABB(const Matrix3x3& transform);
|
||||
AABB TransformAABB(const Matrix4x4& transform);
|
||||
AABB TransformAABB(const Quaternion& transform);
|
||||
OBB Transform(const Matrix3x3& transform);
|
||||
OBB Transform(const Matrix4x4& transform);
|
||||
OBB Transform(const Quaternion& transform);
|
||||
OBB Transform(const Matrix3x3& transform) const;
|
||||
OBB Transform(const Matrix4x4& transform) const;
|
||||
OBB Transform(const Quaternion& transform) const;
|
||||
bool Contains(const Vector3& point) const;
|
||||
bool Contains(const Vector3& aabbMinPoint, const Vector3& aabbMaxPoint) const;
|
||||
bool Contains(const LineSegment& lineSegment) const;
|
||||
bool Contains(const AABB& aabb) const;
|
||||
bool Contains(const OBB& obb) const;
|
||||
bool Contains(const Sphere& sphere) const;
|
||||
bool Contains(const Triangle& triange) const;
|
||||
bool Contains(const Triangle& triangle) const;
|
||||
bool Contains(const Polygon& polygon) const;
|
||||
bool Contains(const Frustum& frustum) const;
|
||||
bool Contains(const Polyhedron& polyhedron) const;
|
||||
@@ -147,7 +117,59 @@ namespace Geometry
|
||||
bool Intersects(const Frustum& frustum) const;
|
||||
bool Intersects(const Polyhedron& polyhedron) const;
|
||||
TriangleMesh Triangulate(int numFacesX, int numFacesY, int numFacesZ, bool ccwIsFrontFacing) const;
|
||||
|
||||
/// Finds the set intersection of this and the given AABB.
|
||||
/** @return This function returns the AABB that is contained in both this and the given AABB.
|
||||
@todo Add Intersection(OBB/Polyhedron). */
|
||||
AABB Intersection(const AABB& rhs) const;
|
||||
|
||||
|
||||
void SetFrom(const Vector3 *pVector3, int i);
|
||||
|
||||
void SetFromCenterAndSize(const Vector3 ¢er, const Vector3 &size);
|
||||
|
||||
void SetFrom(const OBB &obb);
|
||||
|
||||
void SetFrom(const Sphere &s);
|
||||
|
||||
Vector3 GetRandomPointInside(RNG& rng) const;
|
||||
Vector3 GetRandomPointOnSurface(RNG& rng) const;
|
||||
Vector3 GetRandomPointOnEdge(RNG& rng) const;
|
||||
Vector3 GetRandomCornerPoint(RNG& rng) const;
|
||||
|
||||
void SetNegativeInfinity();
|
||||
|
||||
void Enclose(const Vector3 &point);
|
||||
|
||||
void Enclose(const Vector3 &aabbMinPt, const Vector3 &aabbMaxPt);
|
||||
|
||||
void Enclose(const LineSegment &lineSegment);
|
||||
|
||||
void Enclose(const OBB &obb);
|
||||
|
||||
bool TestAxis(const Vector3& axis, const Vector3& v0, const Vector3& v1, const Vector3& v2) const;
|
||||
|
||||
bool Intersects(const LineSegment &lineSegment) const;
|
||||
/// Computes the intersection of a line, ray or line segment and an AABB.
|
||||
/** Based on "T. Kay, J. Kajiya. Ray Tracing Complex Scenes. SIGGRAPH 1986 vol 20, number 4. pp. 269-"
|
||||
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm
|
||||
@param linePos The starting position of the line.
|
||||
@param lineDir The direction of the line. This direction vector must be normalized!
|
||||
@param tNear [in, out] For the test, the input line is treated as a line segment. Pass in the signed distance
|
||||
from the line origin to the start of the line. For a Line-AABB test, -FLOAT_INF is typically passed here.
|
||||
For a Ray-AABB test, 0.0f should be inputted. If intersection occurs, the signed distance from line origin
|
||||
to the line entry point in the AABB is returned here.
|
||||
@param tFar [in, out] Pass in the signed distance from the line origin to the end of the line. For Line-AABB and
|
||||
Ray-AABB tests, pass in FLOAT_INF. For a LineSegment-AABB test, pass in the length of the line segment here.
|
||||
If intersection occurs, the signed distance from line origin to the line exit point in the AABB
|
||||
is returned here.
|
||||
@return True if an intersection occurs, false otherwise.
|
||||
@note This is a low level utility function. It may be more convenient to use one of the AABB::Intersects()
|
||||
functions instead.
|
||||
@see Intersects(). */
|
||||
bool IntersectLineAABB(const Vector3& linePos, const Vector3& lineDir, float tNear, float tFar) const;
|
||||
bool IntersectLineAABB_CPP(const Vector3 &linePos, const Vector3 &lineDir, float &tNear, float &tFar) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include "Shape.h"
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
using LinearAlgebra::Vector2;
|
||||
// CaveGame AABB
|
||||
class AABB2D
|
||||
class AABB2D : public Shape2D
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -17,90 +18,37 @@ namespace Geometry
|
||||
minPoint(min), maxPoint(max)
|
||||
{}
|
||||
|
||||
float Width() const { return maxPoint.x - minPoint.x; }
|
||||
float Height() const { return maxPoint.y - minPoint.y; }
|
||||
float Width() const;
|
||||
float Height() const;
|
||||
|
||||
float DistanceSq(const Vector2& pt) const
|
||||
{
|
||||
Vector2 cp = pt.Clamp(minPoint, maxPoint);
|
||||
return cp.DistanceSq(pt);
|
||||
}
|
||||
float DistanceSq(const Vector2& pt) const;
|
||||
|
||||
void SetNegativeInfinity();
|
||||
|
||||
void Enclose(const Vector2& point)
|
||||
{
|
||||
minPoint = Vector2::Min(minPoint, point);
|
||||
maxPoint = Vector2::Max(maxPoint, point);
|
||||
}
|
||||
void Enclose(const Vector2& point);
|
||||
|
||||
bool Intersects(const AABB2D& rhs) const
|
||||
{
|
||||
return maxPoint.x >= rhs.minPoint.x &&
|
||||
maxPoint.y >= rhs.minPoint.y &&
|
||||
rhs.maxPoint.x >= minPoint.x &&
|
||||
rhs.maxPoint.y >= minPoint.y;
|
||||
}
|
||||
bool Intersects(const AABB2D& rhs) const;
|
||||
|
||||
bool Contains(const AABB2D& rhs) const
|
||||
{
|
||||
return rhs.minPoint.x >= minPoint.x && rhs.minPoint.y >= minPoint.y
|
||||
&& rhs.maxPoint.x <= maxPoint.x && rhs.maxPoint.y <= maxPoint.y;
|
||||
}
|
||||
bool Contains(const AABB2D& rhs) const;
|
||||
|
||||
bool Contains(const Vector2& pt) const
|
||||
{
|
||||
return pt.x >= minPoint.x && pt.y >= minPoint.y
|
||||
&& pt.x <= maxPoint.x && pt.y <= maxPoint.y;
|
||||
}
|
||||
bool Contains(const Vector2& pt) const;
|
||||
|
||||
bool Contains(int x, int y) const
|
||||
{
|
||||
return x >= minPoint.x && y >= minPoint.y
|
||||
&& x <= maxPoint.x && y <= maxPoint.y;
|
||||
}
|
||||
bool Contains(int x, int y) const;
|
||||
|
||||
bool IsDegenerate() const
|
||||
{
|
||||
return minPoint.x >= maxPoint.x || minPoint.y >= maxPoint.y;
|
||||
}
|
||||
bool IsDegenerate() const;
|
||||
|
||||
bool HasNegativeVolume() const
|
||||
{
|
||||
return maxPoint.x < minPoint.x || maxPoint.y < minPoint.y;
|
||||
}
|
||||
bool HasNegativeVolume() const;
|
||||
|
||||
bool IsFinite() const
|
||||
{
|
||||
return minPoint.IsFinite() && maxPoint.IsFinite() && minPoint.MinElement() > -1e5f && maxPoint.MaxElement() < 1e5f;
|
||||
}
|
||||
bool IsFinite() const;
|
||||
|
||||
Vector2 PosInside(const Vector2 &normalizedPos) const
|
||||
{
|
||||
return minPoint + normalizedPos.Mul(maxPoint - minPoint);
|
||||
}
|
||||
Vector2 PosInside(const Vector2 &normalizedPos) const;
|
||||
|
||||
Vector2 ToNormalizedLocalSpace(const Vector2 &pt) const
|
||||
{
|
||||
return (pt - minPoint).Div(maxPoint - minPoint);
|
||||
}
|
||||
Vector2 ToNormalizedLocalSpace(const Vector2 &pt) const;
|
||||
|
||||
|
||||
AABB2D operator+(const Vector2& pt) const
|
||||
{
|
||||
AABB2D a;
|
||||
a.minPoint = minPoint + pt;
|
||||
a.maxPoint = maxPoint + pt;
|
||||
return a;
|
||||
}
|
||||
AABB2D operator+(const Vector2& pt) const;
|
||||
|
||||
AABB2D operator-(const Vector2& pt) const
|
||||
{
|
||||
AABB2D a;
|
||||
a.minPoint = minPoint - pt;
|
||||
a.maxPoint = maxPoint - pt;
|
||||
return a;
|
||||
}
|
||||
AABB2D operator-(const Vector2& pt) const;
|
||||
|
||||
};
|
||||
}
|
@@ -1,21 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "LineSegment.h"
|
||||
#include "Shape.h"
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
using namespace LinearAlgebra;
|
||||
class Capsule
|
||||
// TODO: Move to separate math lib, find duplicates!
|
||||
template <typename T>
|
||||
void Swap(T &a, T &b)
|
||||
{
|
||||
T temp = std::move(a);
|
||||
a = std::move(b);
|
||||
b = std::move(temp);
|
||||
}
|
||||
|
||||
using namespace LinearAlgebra;
|
||||
class Capsule : public Shape
|
||||
{
|
||||
public:
|
||||
// Specifies the two inner points of this capsule
|
||||
LineSegment l;
|
||||
// Specifies the radius of this capsule
|
||||
float r;
|
||||
|
||||
public:
|
||||
Capsule();
|
||||
Capsule(const LineSegment& endPoints, float radius);
|
||||
Capsule(const Vector3& bottomPt, const Vector3& topPt, float radius);
|
||||
|
||||
/// Quickly returns an arbitrary point inside this Capsule. Used in GJK intersection test.
|
||||
inline Vector3 AnyPointFast() const { return l.A; }
|
||||
|
||||
/// Computes the extreme point of this Capsule in the given direction.
|
||||
/** An extreme point is a farthest point of this Capsule in the given direction. Given a direction,
|
||||
this point is not necessarily unique.
|
||||
@param direction The direction vector of the direction to find the extreme point. This vector may
|
||||
be unnormalized, but may not be null.
|
||||
@return The extreme point of this Capsule in the given direction. */
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
bool IsDegenerate() const;
|
||||
float Height() const;
|
||||
float Diameter() const;
|
||||
@@ -23,5 +48,32 @@ namespace Geometry
|
||||
Vector3 Center() const;
|
||||
Vector3 Centroid() const;
|
||||
Vector3 ExtremePoint(const Vector3& direction);
|
||||
AABB MinimalEnclosingAABB() const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
bool Intersects(const Plane &plane) const;
|
||||
|
||||
bool Intersects(const Ray &ray) const;
|
||||
|
||||
bool Intersects(const Line &line) const;
|
||||
|
||||
bool Intersects(const LineSegment &lineSegment) const;
|
||||
|
||||
bool Intersects(const AABB &aabb) const;
|
||||
|
||||
bool Intersects(const OBB &obb) const;
|
||||
|
||||
bool Intersects(const Sphere &sphere) const;
|
||||
|
||||
bool Intersects(const Capsule &capsule) const;
|
||||
|
||||
bool Intersects(const Triangle &triangle) const;
|
||||
|
||||
bool Intersects(const Polygon &polygon) const;
|
||||
|
||||
bool Intersects(const Frustum &frustum) const;
|
||||
|
||||
bool Intersects(const Polyhedron &polyhedron) const;
|
||||
};
|
||||
}
|
31
include/J3ML/Geometry/Common.h
Normal file
31
include/J3ML/Geometry/Common.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
// Forward declarations for classes that include each other
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class Shape;
|
||||
class AABB2D;
|
||||
class AABB;
|
||||
class Capsule;
|
||||
class Frustum;
|
||||
class LineSegment;
|
||||
class Line;
|
||||
class OBB;
|
||||
class Plane;
|
||||
class Polygon;
|
||||
class Polyhedron;
|
||||
template<typename T> class QuadTree;
|
||||
class Ray;
|
||||
class Shape;
|
||||
class Sphere;
|
||||
class Triangle;
|
||||
class Triangle2D;
|
||||
class TriangleMesh;
|
||||
}
|
||||
|
||||
// Methods required by Geometry types
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
}
|
@@ -3,20 +3,24 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include "Plane.h"
|
||||
#include <J3ML/LinearAlgebra/CoordinateFrame.h>
|
||||
#include "Shape.h"
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
/// A frustum can be set to one of the two common different forms.
|
||||
enum class FrustumType
|
||||
{
|
||||
Invalid,
|
||||
Invalid = 0,
|
||||
|
||||
/// Set the Frustum type to this value to define the orthographic projection formula. In orthographic projection,
|
||||
/// 3D images are projected onto a 2D plane essentially by flattening the object along one direction (the plane normal).
|
||||
/// The size of the projected images appear the same independent of their distance to the camera, and distant objects will
|
||||
/// not appear smaller. The shape of the Frustum is identical to an oriented bounding box (OBB).
|
||||
Orthographic,
|
||||
|
||||
/// Set the Frustum type to this value to use the perspective projection formula. With perspective projection, the 2D
|
||||
/// image is formed by projecting 3D points towards a single point (the eye point/tip) of the Frustum, and computing the
|
||||
/// point of intersection of the line of the projection and the near plane of the Frustum.
|
||||
@@ -25,16 +29,215 @@ namespace Geometry
|
||||
Perspective
|
||||
};
|
||||
|
||||
class Frustum {
|
||||
public:
|
||||
Plane TopFace;
|
||||
Plane BottomFace;
|
||||
Plane RightFace;
|
||||
Plane LeftFace;
|
||||
Plane FarFace;
|
||||
Plane NearFace;
|
||||
static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar);
|
||||
/// The Frustum class offers choosing between the two common conventions for the value ranges in
|
||||
/// post-projective space. If you are using either the OpenGL or Direct3D API, you must feed the API data that matches
|
||||
/// the correct convention.
|
||||
enum class FrustumProjectiveSpace
|
||||
{
|
||||
Invalid = 0,
|
||||
/// If this option is chosen, the post-projective unit cube of the Frustum
|
||||
/// is modelled after the OpenGL API convention, meaning that in projected space,
|
||||
/// points inside the Frustum have the X and Y range in [-1, 1] and Z ranges in [-1, 1],
|
||||
/// where the near plane maps to Z=-1 and the far plane maps to Z=1.
|
||||
/// @note If you are submitting projection matrices to GPU hardware using the OpenGL API,
|
||||
/// you **must** use this convention. (or otherwise more than half of the precision of the GL depth buffer is wasted)
|
||||
GL,
|
||||
|
||||
/// If this option is chosen, the post-projective unit cube is modelled after the
|
||||
/// Direct3D API convention, which differs from the GL convention that Z ranges in [0, 1] instead.
|
||||
/// Near plane maps to Z=0, and far plane maps to Z=1. The X and Y ranges in [-1, 1] as is with GL.
|
||||
/// @note If you are submitting projection matrices to GPU hardware using the Direct3D API,
|
||||
/// you **must** use this convention.
|
||||
D3D,
|
||||
};
|
||||
|
||||
/// The handedness rule in J3ML bundles together two different conventions related to the camera:
|
||||
/// * the chirality of the world and view spaces,
|
||||
/// * the fixed local front direction of the Frustum.
|
||||
/// @note The world and view spaces are always assumed to the same chirality, meaning that Frustum::ViewMatrix()
|
||||
/// (and hence Frustum::WorldMatrix()) always returns a matrix with a positive determinant, i.e. it does not mirror.
|
||||
/// If FrustumRightHanded is chosen, then Frustum::ProjectionMatrix() is a mirroring matrix, since the post-projective space
|
||||
/// is always left-handed.
|
||||
/// @note Even though in the local space of the camera +Y is always up, in the world space one can use any 'world up' direction
|
||||
/// as one pleases, by orienting the camera via the Frustum::up vector.
|
||||
enum class FrustumHandedness
|
||||
{
|
||||
Invalid = 0,
|
||||
|
||||
/// If a Frustum is left-handed, then in the local space of the Frustum (the view space), the camera looks towards +Z,
|
||||
/// while +Y goes towards up, and +X goes towards right.
|
||||
/// @note The fixed-pipeline D3D9 API traditionally used the FrustumLeftHanded convention.
|
||||
Left,
|
||||
|
||||
/// If a Frustum is right-handed, then the camera looks towards -Z, +Y is up, and +X is right.
|
||||
/// @note The fixed-pipeline OpenGL API traditionally used the FrustumRightHanded convention.
|
||||
Right
|
||||
};
|
||||
|
||||
/// Represents either an orthographic or a perspective viewing frustum.
|
||||
class Frustum : public Shape {
|
||||
public: /// Members
|
||||
|
||||
|
||||
/// Specifies whether this frustum is a perspective or an orthographic frustum.
|
||||
FrustumType type;
|
||||
/// Specifies whether the [-1, 1] or [0, 1] range is used for the post-projective depth range.
|
||||
FrustumProjectiveSpace projectiveSpace;
|
||||
/// Specifies the chirality of world and view spaces
|
||||
FrustumHandedness handedness;
|
||||
/// The eye point of this frustum
|
||||
Vector3 pos;
|
||||
/// The normalized direction this frustum is watching towards.
|
||||
Vector3 front;
|
||||
/// The normalized up direction for this frustum.
|
||||
/// This vector is specified in world (global) space. This vector is always normalized.
|
||||
/// @note The vectors front and up must always be perpendicular to each other. This means that this up vector is not
|
||||
/// a static/constant up vector, e.g. (0, 1, 0), but changes according to when the camera pitches up and down to
|
||||
/// preserve the condition that front and up are always perpendicular
|
||||
/// @note In the _local_ space of the Frustum, the direction +y is _always_ the up direction and cannot be changed. This
|
||||
/// coincides to how Direct3D and OpenGL view and projection matrices are constructed
|
||||
Vector3 up;
|
||||
/// Distance from the eye point to the front plane
|
||||
/// This parameter must be positive. If perspective projection is used, this parameter must be strictly positive
|
||||
/// (0 is not allowed). If orthographic projection is used, 0 is possible (but uncommon, and not recommended).
|
||||
/// When using the Frustum class to derive perspective projection matrices for a GPU, it should be noted that too
|
||||
/// small values cause poor resolution of Z values near the back plane in post-perspective space, if non-linear
|
||||
/// depth is used (which is common). The larger this value is, the more resolution there is for the Z values across the
|
||||
/// depth range. Too large values cause clipping of geometry when they come very near the camera. */
|
||||
float nearPlaneDistance;
|
||||
/// Distance from the eye point to the back plane of the projection matrix.
|
||||
/// This parameter must be strictly greater than nearPlaneDistance. The range [nearPlaneDistance, farPlaneDistance]
|
||||
// specifies the visible range of objects inside the Frustum. When using the Frustum class for deriving perspective
|
||||
// projection matrix for GPU rendering, it should be remembered that any geometry farther from the camera (in Z value)
|
||||
// than this distance will be clipped from the view, and not rendered.
|
||||
float farPlaneDistance;
|
||||
|
||||
union {
|
||||
/// Horizontal field-of-view, in radians. This field is only valid if type == PerspectiveFrustum.
|
||||
/** @see type. */
|
||||
float horizontalFov;
|
||||
/// The width of the orthographic frustum. This field is only valid if type == OrthographicFrustum.
|
||||
/** @see type. */
|
||||
float orthographicWidth;
|
||||
};
|
||||
|
||||
union {
|
||||
/// Vertical field-of-view, in radians. This field is only valid if type == PerspectiveFrustum.
|
||||
/** @see type. */
|
||||
float verticalFov;
|
||||
/// The height of the orthographic frustum. This field is only valid if type == OrthographicFrustum.
|
||||
/** @see type. */
|
||||
float orthographicHeight;
|
||||
};
|
||||
|
||||
void WorldMatrixChanged();
|
||||
void ProjectionMatrixChanged();
|
||||
|
||||
/// Frustums are typically used in batch culling operations.
|
||||
/// Therefore the matrices associated with a frustum are cached for immediate access.
|
||||
Matrix4x4 worldMatrix;
|
||||
Matrix4x4 projectionMatrix;
|
||||
Matrix4x4 viewProjectionMatrix;
|
||||
public: /// Methods
|
||||
Frustum()
|
||||
: type(FrustumType::Invalid),
|
||||
pos(Vector3::NaN),
|
||||
front(Vector3::NaN),
|
||||
up(Vector3::NaN),
|
||||
nearPlaneDistance(NAN),
|
||||
farPlaneDistance(NAN),
|
||||
worldMatrix(Matrix4x4::NaN),
|
||||
viewProjectionMatrix(Matrix4x4::NaN)
|
||||
{
|
||||
// For conveniency, allow automatic initialization of the graphics API and handedness in use.
|
||||
// If neither of the #defines are set, user must specify per-instance.
|
||||
}
|
||||
|
||||
|
||||
/// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test.
|
||||
inline Vector3 AnyPointFast() const { return CornerPoint(0); }
|
||||
|
||||
static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar);
|
||||
AABB MinimalEnclosingAABB() const;
|
||||
OBB MinimalEnclosingOBB() const;
|
||||
void SetKind(FrustumProjectiveSpace projectiveSpace, FrustumHandedness handedness);
|
||||
void SetViewPlaneDistances(float nearPlaneDistance, float farPlaneDistance);
|
||||
void SetFrame(const Vector3& pos, const Vector3& front, const Vector3& up);
|
||||
void SetPos(const Vector3& pos);
|
||||
void SetFront(const Vector3& front);
|
||||
void SetUp(const Vector3& up);
|
||||
void SetPerspective(float horizontalFov, float verticalFov);
|
||||
void SetOrthographic(float orthographicWidth, float orthographicHeight);
|
||||
FrustumHandedness Handedness() const { return handedness; }
|
||||
FrustumType Type() const { return type; }
|
||||
FrustumProjectiveSpace ProjectiveSpace() const { return projectiveSpace;}
|
||||
const Vector3 &Pos() const {return pos;}
|
||||
const Vector3 &Front() const { return front; }
|
||||
const Vector3 &Up() const { return up; }
|
||||
float NearPlaneDistance() const { return nearPlaneDistance; }
|
||||
float FarPlaneDistance() const { return farPlaneDistance;}
|
||||
float HorizontalFov() const { return horizontalFov;}
|
||||
float VerticalFov() const { return verticalFov;}
|
||||
float OrthographicWidth() const { return orthographicWidth; }
|
||||
float OrthograhpicHeight() const { return orthographicHeight; }
|
||||
int NumEdges() const { return 12; }
|
||||
float AspectRatio() const;
|
||||
|
||||
void SetHorizontalFovAndAspectRatio(float horizontalFov, float aspectRatio);
|
||||
|
||||
|
||||
Vector3 CornerPoint(int cornerIndex) const;
|
||||
|
||||
Vector3 NearPlanePos(float x, float y) const;
|
||||
Vector3 FarPlanePos(float x, float y) const;
|
||||
|
||||
Vector3 WorldRight() const
|
||||
{
|
||||
if (handedness == FrustumHandedness::Right)
|
||||
return Vector3::Cross(front, up);
|
||||
else
|
||||
return Vector3::Cross(up, front);
|
||||
}
|
||||
|
||||
Plane TopPlane() const;
|
||||
Plane BottomPlane() const;
|
||||
Plane RightPlane() const;
|
||||
Plane LeftPlane() const;
|
||||
Plane FarPlane() const;
|
||||
Plane NearPlane() const;
|
||||
float NearPlaneWidth() const;
|
||||
float NearPlaneHeight() const;
|
||||
|
||||
|
||||
void Translate(const Vector3& offset);
|
||||
void Transform(const Matrix3x3& transform);
|
||||
void Transform(const Matrix4x4& transform);
|
||||
void Transform(const Quaternion& transform);
|
||||
|
||||
|
||||
Polyhedron ToPolyhedron() const;
|
||||
|
||||
bool Intersects(const Ray& ray) const;
|
||||
//bool Intersects(const Line& line) const;
|
||||
bool Intersects(const LineSegment& lineSegment) const;
|
||||
bool Intersects(const AABB& aabb) const;
|
||||
bool Intersects(const OBB& obb) const;
|
||||
bool Intersects(const Plane& plane) const;
|
||||
bool Intersects(const Triangle& triangle) const;
|
||||
bool Intersects(const Polygon& lineSegment) const;
|
||||
bool Intersects(const Sphere& aabb) const;
|
||||
bool Intersects(const Capsule& obb) const;
|
||||
bool Intersects(const Frustum& plane) const;
|
||||
bool Intersects(const Polyhedron& triangle) const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
void GetCornerPoints(Vector3 *outPointArray) const;
|
||||
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
LineSegment Edge(int edgeIndex) const;
|
||||
|
||||
bool Intersects(const Line &line) const;
|
||||
};
|
||||
}
|
24
include/J3ML/Geometry/Line.h
Normal file
24
include/J3ML/Geometry/Line.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "LineSegment.h"
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class Line
|
||||
{
|
||||
public:
|
||||
Vector3 Position; /// Specifies the origin of this line.
|
||||
Vector3 Direction; /// The normalized direction vector of this ray.
|
||||
public:
|
||||
static void
|
||||
ClosestPointLineLine(const Vector3 &v0, const Vector3 &v10, const Vector3 &v2, const Vector3 &v32, float &d,
|
||||
float &d2);
|
||||
|
||||
Vector3 ClosestPoint(const J3ML::Geometry::LineSegment &other, float &d, float &d2) const;
|
||||
|
||||
Vector3 GetPoint(float d) const;
|
||||
|
||||
Vector3 ClosestPoint(const Vector3 &targetPoint, float &d) const;
|
||||
};
|
||||
}
|
@@ -1,16 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include "Plane.h"
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
/// Clamps the given input value to the range [min, max].
|
||||
/** @see Clamp01(), Min(), Max(). */
|
||||
template<typename T>
|
||||
T Clamp(const T &val, const T &floor, const T &ceil)
|
||||
{
|
||||
assert(floor <= ceil);
|
||||
return val <= ceil ? (val >= floor ? val : floor) : ceil;
|
||||
}
|
||||
|
||||
/// Clamps the given input value to the range [0, 1].
|
||||
/** @see Clamp(), Min(), Max(). */
|
||||
template<typename T>
|
||||
T Clamp01(const T &val) { return Clamp(val, T(0), T(1)); }
|
||||
|
||||
using LinearAlgebra::Vector3;
|
||||
class LineSegment
|
||||
{
|
||||
public:
|
||||
LineSegment();
|
||||
LineSegment(const Vector3& a, const Vector3& b);
|
||||
Vector3 A;
|
||||
Vector3 B;
|
||||
public:
|
||||
LineSegment();
|
||||
LineSegment(const Vector3& a, const Vector3& b);
|
||||
|
||||
/// Computes the closest point on this line segment to the given object.
|
||||
/** @param d [out] If specified, this parameter receives the normalized distance along
|
||||
this line segment which specifies the closest point on this line segment to
|
||||
the specified point.
|
||||
@return The closest point on this line segment to the given object.
|
||||
@see Contains(), Distance(), Intersects(). */
|
||||
Vector3 ClosestPoint(const Vector3 &point) const;
|
||||
bool Contains(const Vector3& point, float distanceThreshold = 1e-3f) const;
|
||||
|
||||
|
||||
/// Quickly returns an arbitrary point inside this LineSegment. Used in GJK intersection test.
|
||||
inline Vector3 AnyPointFast() const { return A; }
|
||||
|
||||
/// Computes an extreme point of this LineSegment in the given direction.
|
||||
/** An extreme point is a farthest point along this LineSegment in the given direction. Given a direction,
|
||||
this point is not necessarily unique.
|
||||
@param direction The direction vector of the direction to find the extreme point. This vector may
|
||||
be unnormalized, but may not be null.
|
||||
@return An extreme point of this LineSegment in the given direction. The returned point is always
|
||||
either a or b.
|
||||
@see a, b.*/
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
Vector3 GetPoint(float d) const;
|
||||
Vector3 ClosestPoint(const Vector3 &point, float &d) const;
|
||||
Vector3 ClosestPoint(const Ray &other, float &d, float &d2) const;
|
||||
float Distance(const Vector3 &point) const;
|
||||
float DistanceSq(const Vector3& point) const;
|
||||
float Distance(const Vector3 &point, float &d) const;
|
||||
float Distance(const Ray &other) const;
|
||||
float Distance(const Ray &other, float &d) const;
|
||||
float Distance(const Ray &other, float &d, float &d2) const;
|
||||
float Distance(const Line &other) const;
|
||||
float Distance(const Line &other, float &d) const;
|
||||
float Distance(const Line &other, float &d, float &d2) const;
|
||||
float Distance(const LineSegment &other) const;
|
||||
float Distance(const LineSegment &other, float &d) const;
|
||||
float Distance(const LineSegment &other, float &d, float &d2) const;
|
||||
float Distance(const Plane& other) const;
|
||||
|
||||
Vector3 Dir() const;
|
||||
|
||||
float Length() const;
|
||||
|
||||
float LengthSq() const;
|
||||
|
||||
float DistanceSq(const LineSegment &other) const;
|
||||
|
||||
Vector3 ClosestPoint(const LineSegment &other, float &d, float &d2) const;
|
||||
|
||||
Vector3 ClosestPoint(const Line &other, float &d, float &d2) const;
|
||||
|
||||
bool Intersects(const LineSegment &segment) const;
|
||||
};
|
||||
|
||||
LineSegment operator *(const Matrix4x4 &transform, const LineSegment &l);
|
||||
}
|
@@ -1,31 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/Geometry.h>
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
|
||||
namespace Geometry {
|
||||
class OBB
|
||||
|
||||
namespace J3ML::Geometry {
|
||||
/// A 3D arbitrarily oriented bounding box
|
||||
// This data structure represents a box in 3D space. The local axes of this box can be arbitrarily oriented/rotated
|
||||
// with respect to the global world coordinate system. This allows OBBs to more tightly bound objects than AABBs do,
|
||||
// which always align with the world space axes. This flexibility has the drawback that the geometry tests and operations
|
||||
// involving OBBs are more costly, and representing an OBB in memory takes more space (15 floats vs 6 floats)
|
||||
class OBB : public Shape
|
||||
{
|
||||
public:
|
||||
// The center position of this OBB
|
||||
Vector3 pos;
|
||||
// Stores half-sizes to x, y, and z directions in the local space of this OBB.
|
||||
Vector3 r;
|
||||
// Specifies normalized direc tion vectors for the local axes
|
||||
// Specifies normalized direction vectors for the local axes
|
||||
Vector3 axis[3];
|
||||
|
||||
/// Default constructor that does not initialize any member values.
|
||||
OBB() {}
|
||||
// Constructs an OBB by explicitly initializing all member values
|
||||
OBB(const Vector3& pos, const Vector3& radii, const Vector3& axis0, const Vector3& axis1, const Vector3& axis2);
|
||||
OBB(const Geometry::AABB& aabb);
|
||||
OBB(const AABB& aabb);
|
||||
inline static int NumFaces() { return 6; }
|
||||
inline static int NumEdges() { return 12; }
|
||||
inline static int NumVertices() { return 8; }
|
||||
|
||||
Polyhedron ToPolyhedron() const;
|
||||
//PBVolume<6> ToPBVolume() const;
|
||||
AABB MinimalEnclosingAABB() const
|
||||
{
|
||||
AABB aabb;
|
||||
aabb.SetFrom(*this);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
Geometry::AABB MinimalEnclosingAABB() const;
|
||||
bool Intersects(const LineSegment &lineSegment) const;
|
||||
|
||||
Sphere MinimalEnclosingSphere() const;
|
||||
Sphere MaximalContainedSphere() const;
|
||||
@@ -33,16 +47,60 @@ namespace Geometry {
|
||||
Vector3 HalfSize() const;
|
||||
Vector3 Diagonal() const;
|
||||
Vector3 HalfDiagonal() const;
|
||||
void Transform(const Matrix3x3& transform);
|
||||
void Transform(const Matrix4x4& transform);
|
||||
void Transform(const Quaternion& transform);
|
||||
bool IsFinite() const;
|
||||
bool IsDegenerate() const;
|
||||
Vector3 CenterPoint() const;
|
||||
Vector3 Centroid() const;
|
||||
|
||||
Vector3 AnyPointFast() const;
|
||||
Vector3 AnyPointFast() const { return pos; }
|
||||
|
||||
float Volume();
|
||||
float SurfaceArea();
|
||||
float Volume() const;
|
||||
float SurfaceArea() const;
|
||||
Geometry::LineSegment Edge(int edgeIndex) const;
|
||||
Vector3 CornerPoint(int cornerIndex) const;
|
||||
|
||||
Vector3 PointInside(float x, float y, float z) const;
|
||||
Vector3 PointInside(const Vector3& pt) const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
int UniqueFaceNormals(Vector3 *out) const;
|
||||
|
||||
int UniqueEdgeDirections(Vector3 *out) const;
|
||||
|
||||
Vector3 PointOnEdge(int edgeIndex, float u) const;
|
||||
|
||||
Vector3 FaceCenterPoint(int faceIndex) const;
|
||||
|
||||
void GetCornerPoints(Vector3 *outPointArray) const;
|
||||
|
||||
void GetFacePlanes(Plane *outPlaneArray) const;
|
||||
|
||||
Plane FacePlane(int faceIndex) const;
|
||||
|
||||
void ExtremePointsAlongDirection(const Vector3 &dir, const Vector3 *pointArray, int numPoints, int &idxSmallest,
|
||||
int &idxLargest, float &smallestD, float &largestD);
|
||||
|
||||
Vector3 FacePoint(int faceIndex, float u, float v) const;
|
||||
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
|
||||
|
||||
void SetFrom(const AABB &aabb, const Matrix3x3 &transform);
|
||||
|
||||
void SetFrom(const AABB &aabb, const Matrix4x4 &transform);
|
||||
|
||||
void SetFrom(const AABB &aabb, const Quaternion &transform);
|
||||
|
||||
bool Intersects(const Plane& plane) const;
|
||||
|
||||
Matrix4x4 LocalToWorld() const;
|
||||
|
||||
Matrix4x4 WorldToLocal() const;
|
||||
};
|
||||
}
|
@@ -1,13 +1,86 @@
|
||||
#pragma once
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include "Shape.h"
|
||||
#include "Ray.h"
|
||||
|
||||
using namespace LinearAlgebra;
|
||||
|
||||
class Plane
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
public:
|
||||
Vector3 Position;
|
||||
Vector3 Normal;
|
||||
float distance = 0.f;
|
||||
using J3ML::LinearAlgebra::Vector3;
|
||||
|
||||
};
|
||||
class Plane : public Shape
|
||||
{
|
||||
public:
|
||||
Vector3 Position;
|
||||
Vector3 Normal;
|
||||
float distance = 0.f;
|
||||
public:
|
||||
Plane() : Shape() {}
|
||||
Plane(const Vector3& v1, const Vector3 &v2, const Vector3& v3);
|
||||
Plane(const Vector3& pos, const Vector3& norm);
|
||||
Plane(const Line &line, const Vector3 &normal);
|
||||
|
||||
void Set(const Vector3& v1, const Vector3& v2, const Vector3& v3);
|
||||
|
||||
void Set(const Vector3 &point, const Vector3 &normal_);
|
||||
|
||||
bool Intersects(J3ML::Geometry::Ray ray, float *dist) const;
|
||||
|
||||
/// Tests if the given point lies on the positive side of this plane.
|
||||
/** A plane divides the space in three sets: the negative halfspace, the plane itself, and the positive halfspace.
|
||||
The normal vector of the plane points towards the positive halfspace.
|
||||
@return This function returns true if the given point lies either on this plane itself, or in the positive
|
||||
halfspace of this plane.
|
||||
@see IsInPositiveDirection, AreOnSameSide(), Distance(), SignedDistance(). */
|
||||
bool IsOnPositiveSide(const Vector3 &point) const;
|
||||
|
||||
float SignedDistance(const Vector3 &point) const;
|
||||
float SignedDistance(const AABB &aabb) const;
|
||||
float SignedDistance(const OBB &obb) const;
|
||||
float SignedDistance(const Capsule &capsule) const;
|
||||
//float Plane::SignedDistance(const Circle &circle) const { return Plane_SignedDistance(*this, circle); }
|
||||
float SignedDistance(const Frustum &frustum) const;
|
||||
//float SignedDistance(const Line &line) const { return Plane_SignedDistance(*this, line); }
|
||||
float SignedDistance(const LineSegment &lineSegment) const;
|
||||
float SignedDistance(const Ray &ray) const;
|
||||
//float Plane::SignedDistance(const Plane &plane) const { return Plane_SignedDistance(*this, plane); }
|
||||
float SignedDistance(const Polygon &polygon) const;
|
||||
float SignedDistance(const Polyhedron &polyhedron) const;
|
||||
float SignedDistance(const Sphere &sphere) const;
|
||||
float SignedDistance(const Triangle &triangle) const;
|
||||
|
||||
static bool
|
||||
IntersectLinePlane(const Vector3 &planeNormal, float planeD, const Vector3 &linePos, const Vector3 &lineDir,
|
||||
float &t);
|
||||
|
||||
float Distance(const Vector3 &) const;
|
||||
|
||||
float Distance(const LineSegment &lineSegment) const;
|
||||
|
||||
float Distance(const Sphere &sphere) const;
|
||||
|
||||
float Distance(const Capsule &capsule) const;
|
||||
|
||||
bool Intersects(const Line &line, float *dist) const;
|
||||
|
||||
bool Intersects(const LineSegment &lineSegment, float *dist) const;
|
||||
|
||||
bool Intersects(const Sphere &sphere) const;
|
||||
|
||||
bool Intersects(const Capsule &capsule) const;
|
||||
|
||||
bool Intersects(const AABB &aabb) const;
|
||||
|
||||
bool Intersects(const OBB &obb) const;
|
||||
|
||||
bool Intersects(const Triangle &triangle) const;
|
||||
|
||||
bool Intersects(const Frustum &frustum) const;
|
||||
|
||||
bool Intersects(const Polyhedron &polyhedron) const;
|
||||
|
||||
LineSegment Project(const LineSegment &segment);
|
||||
|
||||
Vector3 Project(const Vector3 &point) const;
|
||||
};
|
||||
}
|
@@ -1,7 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
namespace Geometry {
|
||||
class Polygon {
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include <vector>
|
||||
#include "Shape.h"
|
||||
#include "J3ML/LinearAlgebra.h"
|
||||
|
||||
namespace J3ML::Geometry {
|
||||
|
||||
class Polygon : public Shape
|
||||
{
|
||||
public:
|
||||
std::vector<Vector3> vertices;
|
||||
|
||||
/// Quickly returns an arbitrary point inside this AABB. Used in GJK intersection test.
|
||||
Vector3 AnyPointFast() const { return !vertices.empty() ? vertices[0] : Vector3::NaN; }
|
||||
|
||||
AABB MinimalEnclosingAABB() const;
|
||||
int NumVertices() const;
|
||||
Vector3 Vertex(int vertexIndex) const;
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
bool Intersects(const Capsule &capsule) const;
|
||||
bool Intersects(const Line &line) const;
|
||||
bool Intersects(const Ray &ray) const;
|
||||
bool Intersects(const LineSegment &lineSegment) const;
|
||||
bool Intersects(const Plane &plane) const;
|
||||
bool Intersects(const AABB &aabb) const;
|
||||
bool Intersects(const OBB &obb) const;
|
||||
bool Intersects(const Triangle &triangle, float polygonThickness = 1e-3f) const;
|
||||
bool Intersects(const Polygon &polygon, float polygonThickness = 1e-3f) const;
|
||||
bool Intersects(const Frustum &frustum) const;
|
||||
bool Intersects(const Polyhedron &polyhedron) const;
|
||||
bool Intersects(const Sphere &sphere) const;
|
||||
std::vector<Triangle> Triangulate() const;
|
||||
/// Tests if this polygon is planar.
|
||||
/** A polygon is planar if all its vertices lie on the same plane.
|
||||
@note Almost all functions in this class require that the polygon is planar. While you can store vertices of
|
||||
non-planar polygons in this class, they are better avoided. Read the member function documentation carefully
|
||||
to avoid calling for non-planar polygons any functions which assume planarity.
|
||||
@see IsConvex(), IsSimple(), IsNull(), IsFinite(), IsDegenerate(). */
|
||||
bool IsPlanar(float epsilonSq = 1e-4f) const;
|
||||
|
||||
Vector2 MapTo2D(int i) const;
|
||||
|
||||
Vector2 MapTo2D(const Vector3 &point) const;
|
||||
|
||||
Vector3 BasisU() const;
|
||||
|
||||
Vector3 BasisV() const;
|
||||
|
||||
Plane PlaneCCW() const;
|
||||
|
||||
bool Contains(const Polygon &worldSpacePolygon, float polygonThickness) const;
|
||||
|
||||
bool Contains(const Vector3 &worldSpacePoint, float polygonThicknessSq = 1e-2f) const;
|
||||
|
||||
bool Contains(const LineSegment &worldSpaceLineSegment, float polygonThickness) const;
|
||||
|
||||
bool Contains(const Triangle &worldSpaceTriangle, float polygonThickness) const;
|
||||
|
||||
LineSegment Edge(int i) const;
|
||||
|
||||
bool ConvexIntersects(const AABB &aabb) const;
|
||||
|
||||
bool ConvexIntersects(const OBB &obb) const;
|
||||
|
||||
bool ConvexIntersects(const Frustum &frustum) const;
|
||||
|
||||
Vector3 MapFrom2D(const Vector2 &point) const;
|
||||
|
||||
bool Intersects2D(const LineSegment &segment) const;
|
||||
|
||||
Vector3 ClosestPoint(const LineSegment &lineSegment) const;
|
||||
|
||||
Vector3 ClosestPoint(const LineSegment &lineSegment, Vector3 *lineSegmentPt) const;
|
||||
|
||||
Vector3 ClosestPoint(const Vector3 &point) const;
|
||||
protected:
|
||||
|
||||
|
||||
};
|
||||
}
|
@@ -1,8 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
namespace Geometry
|
||||
{
|
||||
class Polyhedron {
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include <J3ML/Geometry/Shape.h>
|
||||
#include <vector>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
using namespace J3ML::LinearAlgebra;
|
||||
|
||||
// Represents a three-dimensional closed geometric solid defined by flat polygonal faces.
|
||||
class Polyhedron : public Shape
|
||||
{
|
||||
public:
|
||||
// Stores a list of indices of a single face of a Polygon
|
||||
struct Face
|
||||
{
|
||||
// Specifies the indices of the corner vertices of the polyhedron.
|
||||
// Indices point to the polyhedron vertex array.
|
||||
// The face vertices should all lie on the same plane.
|
||||
// The positive direction of the plane (the direction the face outwards normal points)
|
||||
// is the one where the vertices are wound in counter-clockwise order.
|
||||
std::vector<int> v;
|
||||
|
||||
// Reverses the winding order of this face. This has the effect of reversing the direction
|
||||
// the normal of this face points to.
|
||||
void FlipWindingOrder();
|
||||
|
||||
};
|
||||
|
||||
// Specifies the vertices of this polyhedron.
|
||||
std::vector<Vector3> v;
|
||||
std::vector<Face> f;
|
||||
|
||||
int NumVertices() const {return (int)v.size();}
|
||||
int NumFaces() const { return (int)f.size();}
|
||||
AABB MinimalEnclosingAABB() const;
|
||||
|
||||
Vector3 Vertex(int vertexIndex) const;
|
||||
|
||||
|
||||
|
||||
bool Contains(const Vector3&) const;
|
||||
bool Contains(const LineSegment&) const;
|
||||
bool Contains(const Triangle&) const;
|
||||
bool Contains(const Polygon&) const;
|
||||
bool Contains(const AABB&) const;
|
||||
bool Contains(const OBB&) const;
|
||||
bool Contains(const Frustum&) const;
|
||||
bool Contains(const Polyhedron&) const;
|
||||
|
||||
bool ContainsConvex(const Vector3&, float epsilon = 1e-4f) const;
|
||||
bool ContainsConvex(const LineSegment&) const;
|
||||
bool ContainsConvex(const Triangle&) const;
|
||||
|
||||
bool Intersects(const Line&) const;
|
||||
bool Intersects(const LineSegment&) const;
|
||||
bool Intersects(const Ray&) const;
|
||||
bool Intersects(const Plane&) const;
|
||||
bool Intersects(const Polyhedron&) const;
|
||||
bool Intersects(const AABB&) const;
|
||||
bool Intersects(const OBB&) const;
|
||||
bool Intersects(const Triangle&) const;
|
||||
bool Intersects(const Polygon&) const;
|
||||
bool Intersects(const Frustum&) const;
|
||||
bool Intersects(const Sphere&) const;
|
||||
bool Intersects(const Capsule& capsule) const;
|
||||
|
||||
bool IsClosed() const;
|
||||
|
||||
Plane FacePlane(int faceIndex) const;
|
||||
|
||||
std::vector<Polygon> Faces() const;
|
||||
|
||||
int NumEdges() const;
|
||||
|
||||
LineSegment Edge(int edgeIndex) const;
|
||||
|
||||
std::vector<std::pair<int, int>> EdgeIndices() const;
|
||||
|
||||
std::vector<LineSegment> Edges() const;
|
||||
|
||||
Polygon FacePolygon(int faceIndex) const;
|
||||
|
||||
Vector3 FaceNormal(int faceIndex) const;
|
||||
|
||||
bool IsConvex() const;
|
||||
|
||||
Vector3 ApproximateConvexCentroid() const;
|
||||
|
||||
int ExtremeVertex(const Vector3 &direction) const;
|
||||
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
|
||||
/// Tests if the given face of this Polyhedron contains the given point.
|
||||
bool FaceContains(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-3f) const;
|
||||
|
||||
/// A helper for Contains() and FaceContains() tests: Returns a positive value if the given point is contained in the given face,
|
||||
/// and a negative value if the given point is outside the face. The magnitude of the return value reports a pseudo-distance
|
||||
/// from the point to the nearest edge of the face polygon. This is used as a robustness/stability criterion to estimate how
|
||||
/// numerically believable the result is.
|
||||
float FaceContainmentDistance2D(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-5f) const;
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
Vector3 ClosestPoint(const LineSegment& lineSegment, Vector3 *lineSegmentPt) const;
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
@@ -5,7 +5,7 @@
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include "AABB2D.h"
|
||||
|
||||
namespace Geometry {
|
||||
namespace J3ML::Geometry {
|
||||
|
||||
|
||||
using LinearAlgebra::Vector2;
|
||||
|
@@ -5,13 +5,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <vector>
|
||||
#include "TriangleMesh.h"
|
||||
#include "Frustum.h"
|
||||
#include "OBB.h"
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
using LinearAlgebra::Vector3;
|
||||
using J3ML::LinearAlgebra::Vector3;
|
||||
|
||||
|
||||
// RaycastResult structure containing the first object the ray collides with,
|
||||
// the surface intersection point,
|
||||
// and the surface normal at the point of intersection.
|
||||
struct RaycastResult
|
||||
{
|
||||
Vector3 Intersection;
|
||||
Vector3 SurfaceNormal;
|
||||
bool Hit;
|
||||
Shape* Target;
|
||||
static RaycastResult NoHit() { return {Vector3::NaN, Vector3::NaN, false, nullptr};}
|
||||
};
|
||||
|
||||
// A ray in 3D space is a line that starts from an origin point and extends to infinity in one direction
|
||||
class Ray
|
||||
{
|
||||
public:
|
||||
// The position of this ray.
|
||||
Vector3 Origin;
|
||||
// The normalized direction vector of this ray.
|
||||
// @note: For proper functionality, this direction vector needs to always be normalized
|
||||
Vector3 Direction;
|
||||
Ray() {}
|
||||
Ray(const Vector3& pos, const Vector3& dir);
|
||||
//explicit Ray(const Line& line);
|
||||
explicit Ray(const LineSegment& lineSegment);
|
||||
bool IsFinite() const;
|
||||
Vector3 GetPoint(float distance) const;
|
||||
|
||||
RaycastResult Cast(const Triangle& target, float maxDistance = 99999999);
|
||||
RaycastResult Cast(const Plane& target, float maxDistance = 99999999);
|
||||
RaycastResult Cast(const AABB& target, float maxDistance = 99999999);
|
||||
// https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_sphere.html
|
||||
RaycastResult Cast(const Sphere& target, float maxDistance = 99999999);
|
||||
RaycastResult Cast(const OBB& target, float maxDistance = 99999999);
|
||||
RaycastResult Cast(const Capsule& target, float maxDistance = 99999999);
|
||||
RaycastResult Cast(const Frustum& target, float maxDistance = 99999999);
|
||||
RaycastResult Cast(const TriangleMesh& target, float maxDistance = 9999999);
|
||||
|
||||
// Returns a RaycastResult structure containing the first object the ray collides with,
|
||||
// the surface intersection point,
|
||||
// and the surface normal at the point of intersection.
|
||||
RaycastResult Cast(std::vector<Shape> shapes, float maxDistance = 99999999);
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
|
||||
Vector3 ClosestPoint(const LineSegment&, float &d, float &d2) const;
|
||||
|
||||
Vector3 ClosestPoint(const Vector3 &targetPoint, float &d) const;
|
||||
};
|
||||
}
|
26
include/J3ML/Geometry/Shape.h
Normal file
26
include/J3ML/Geometry/Shape.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class GeometricPrimitive
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
class Shape
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
class Shape2D
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
@@ -1,15 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "J3ML/Geometry.h"
|
||||
#include <J3ML/Geometry.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/TriangleMesh.h>
|
||||
#include <J3ML/Geometry/Shape.h>
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class Sphere
|
||||
using J3ML::LinearAlgebra::Matrix3x3;
|
||||
using J3ML::LinearAlgebra::Matrix4x4;
|
||||
|
||||
// A mathematical representation of a 3-dimensional sphere
|
||||
class Sphere : public Shape
|
||||
{
|
||||
public:
|
||||
Sphere(const Vector3& pos, float radius)
|
||||
{
|
||||
Vector3 Position;
|
||||
float Radius;
|
||||
public:
|
||||
Sphere() {}
|
||||
Sphere(const Vector3& pos, float radius) : Position(pos), Radius(radius) {}
|
||||
|
||||
/// Quickly returns an arbitrary point inside this Sphere. Used in GJK intersection test.
|
||||
Vector3 AnyPointFast() const { return Position; }
|
||||
/// Computes the extreme point of this Sphere in the given direction.
|
||||
/** An extreme point is a farthest point of this Sphere in the given direction. For
|
||||
a Sphere, this point is unique.
|
||||
@param direction The direction vector of the direction to find the extreme point. This vector may
|
||||
be unnormalized, but may not be null.
|
||||
@return The extreme point of this Sphere in the given direction. */
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
|
||||
void Translate(const Vector3& offset)
|
||||
{
|
||||
Position = Position + offset;
|
||||
}
|
||||
void Transform(const Matrix3x3& transform)
|
||||
{
|
||||
Position = transform * Position;
|
||||
}
|
||||
void Transform(const Matrix4x4& transform)
|
||||
{
|
||||
Position = transform * Position;
|
||||
}
|
||||
inline float Cube(float inp) const
|
||||
{
|
||||
return inp*inp*inp;
|
||||
}
|
||||
float Volume() const
|
||||
{
|
||||
return 4.f * M_PI * Cube(Radius) / 3.f;
|
||||
}
|
||||
float SurfaceArea() const
|
||||
{
|
||||
return 4.f * M_PI * Cube(Radius) / 3.f;
|
||||
}
|
||||
bool IsFinite() const
|
||||
{
|
||||
return Position.IsFinite() && std::isfinite(Radius);
|
||||
}
|
||||
bool IsDegenerate()
|
||||
{
|
||||
return !(Radius > 0.f) || !Position.IsFinite();
|
||||
}
|
||||
bool Contains(const Vector3& point) const
|
||||
{
|
||||
return Position.DistanceSquared(point) <= Radius*Radius;
|
||||
}
|
||||
|
||||
bool Contains(const Vector3& point, float epsilon) const
|
||||
{
|
||||
return Position.DistanceSquared(point) <= Radius*Radius + epsilon;
|
||||
}
|
||||
bool Contains(const LineSegment& lineseg) const;
|
||||
TriangleMesh GenerateUVSphere() const {}
|
||||
TriangleMesh GenerateIcososphere() const {}
|
||||
|
||||
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
|
||||
};
|
||||
}
|
@@ -1,9 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
namespace Geometry
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <cfloat>
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class Triangle
|
||||
{
|
||||
public:
|
||||
Vector3 V0;
|
||||
Vector3 V1;
|
||||
Vector3 V2;
|
||||
public:
|
||||
|
||||
float DistanceSq(const Vector3 &point) const;
|
||||
|
||||
bool Intersects(const AABB& aabb) const;
|
||||
bool Intersects(const Capsule& capsule) const;
|
||||
AABB BoundingAABB() const;
|
||||
|
||||
/// Tests if the given object is fully contained inside this triangle.
|
||||
/** @param triangleThickness An epsilon threshold value to use for this test. triangleThicknessSq is the squared version of this parameter.
|
||||
This specifies the maximum distance the given object can lie from the plane defined by this triangle.
|
||||
@see Distance(), Intersects(), ClosestPoint().
|
||||
@todo Add Triangle::Contains(Circle) and Triangle::Contains(Disc). */
|
||||
bool Contains(const Vector3& point, float triangleThicknessSq = 1e-5f) const;
|
||||
bool Contains(const LineSegment& lineSeg, float triangleThickness = 1e-3f) const;
|
||||
bool Contains(const Triangle& triangle, float triangleThickness = 1e-3f) const;
|
||||
|
||||
void ProjectToAxis(const Vector3 &axis, float &dMin, float &dMax) const;
|
||||
|
||||
/// Quickly returns an arbitrary point inside this Triangle. Used in GJK intersection test.
|
||||
inline Vector3 AnyPointFast() const { return V0; }
|
||||
|
||||
/// Computes an extreme point of this Triangle in the given direction.
|
||||
/** An extreme point is a farthest point of this Triangle in the given direction. Given a direction,
|
||||
this point is not necessarily unique.
|
||||
@param direction The direction vector of the direction to find the extreme point. This vector may
|
||||
be unnormalized, but may not be null.
|
||||
@return An extreme point of this Triangle in the given direction. The returned point is always a
|
||||
vertex of this Triangle.
|
||||
@see Vertex(). */
|
||||
Vector3 ExtremePoint(const Vector3 &direction) const;
|
||||
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
|
||||
|
||||
|
||||
static float IntersectLineTri(const Vector3 &linePos, const Vector3 &lineDir, const Vector3 &v0, const Vector3 &v1,
|
||||
const Vector3 &v2, float &u, float &v);
|
||||
|
||||
/// Computes the closest point on the edge of this triangle to the given object.
|
||||
/** @param outU [out] If specified, receives the barycentric U coordinate of the returned point (in the UV convention).
|
||||
This pointer may be null.
|
||||
@param outV [out] If specified, receives the barycentric V coordinate of the returned point (in the UV convention).
|
||||
This pointer may be null.
|
||||
@param outD [out] If specified, receives the distance along the line of the closest point on the line to the edge of this triangle.
|
||||
@return The closest point on the edge of this triangle to the given object.
|
||||
@todo Add ClosestPointToTriangleEdge(Point/Ray/Triangle/Plane/Polygon/Circle/Disk/AABB/OBB/Sphere/Capsule/Frustum/Polyhedron).
|
||||
@see Distance(), Contains(), Intersects(), ClosestPointToTriangleEdge(), Line::GetPoint. */
|
||||
Vector3 ClosestPointToTriangleEdge(const Line &line, float *outU, float *outV, float *outD) const;
|
||||
Vector3 ClosestPointToTriangleEdge(const LineSegment &lineSegment, float *outU, float *outV, float *outD) const;
|
||||
|
||||
Vector3 ClosestPoint(const LineSegment &lineSegment, Vector3 *otherPt = 0) const;
|
||||
/// Returns the point at the given barycentric coordinates.
|
||||
/** This function computes the vector space point at the given barycentric coordinates.
|
||||
@param uvw The barycentric UVW coordinate triplet. The condition u+v+w == 1 should hold for the input coordinate.
|
||||
If 0 <= u,v,w <= 1, the returned point lies inside this triangle.
|
||||
@return u*a + v*b + w*c. */
|
||||
Vector3 Point(const Vector3 &uvw) const;
|
||||
Vector3 Point(float u, float v, float w) const;
|
||||
/** These functions are an alternate form of Point(u,v,w) for the case when the barycentric coordinates are
|
||||
represented as a (u,v) pair and not as a (u,v,w) triplet. This function is provided for convenience
|
||||
and effectively just computes Point(1-u-v, u, v).
|
||||
@param uv The barycentric UV coordinates. If 0 <= u,v <= 1 and u+v <= 1, then the returned point lies inside
|
||||
this triangle.
|
||||
@return a + (b-a)*u + (c-a)*v.
|
||||
@see BarycentricUV(), BarycentricUVW(), BarycentricInsideTriangle(). */
|
||||
Vector3 Point(const Vector2 &uv) const;
|
||||
Vector3 Point(float u, float v) const;
|
||||
|
||||
/// Expresses the given point in barycentric (u,v,w) coordinates.
|
||||
/** @note There are two different conventions for representing barycentric coordinates. One uses
|
||||
a (u,v,w) triplet with the equation pt == u*a + v*b + w*c, and the other uses a (u,v) pair
|
||||
with the equation pt == a + u*(b-a) + v*(c-a). These two are equivalent. Use the mappings
|
||||
(u,v) -> (1-u-v, u, v) and (u,v,w)->(v,w) to convert between these two representations.
|
||||
@param point The point of the vector space to express in barycentric coordinates. This point should
|
||||
lie in the plane formed by this triangle.
|
||||
@return The factors (u,v,w) that satisfy the weighted sum equation point == u*a + v*b + w*c.
|
||||
@see BarycentricUV(), BarycentricInsideTriangle(), Point(), http://mathworld.wolfram.com/BarycentricCoordinates.html */
|
||||
Vector3 BarycentricUVW(const Vector3 &point) const;
|
||||
|
||||
/// Expresses the given point in barycentric (u,v) coordinates.
|
||||
/** @note There are two different conventions for representing barycentric coordinates. One uses
|
||||
a (u,v,w) triplet with the equation pt == u*a + v*b + w*c, and the other uses a (u,v) pair
|
||||
with the equation pt == a + u*(b-a) + v*(c-a). These two are equivalent. Use the mappings
|
||||
(u,v) -> (1-u-v, u, v) and (u,v,w)->(v,w) to convert between these two representations.
|
||||
@param point The point to express in barycentric coordinates. This point should lie in the plane
|
||||
formed by this triangle.
|
||||
@return The factors (u,v) that satisfy the weighted sum equation point == a + u*(b-a) + v*(c-a).
|
||||
@see BarycentricUVW(), BarycentricInsideTriangle(), Point(). */
|
||||
Vector2 BarycentricUV(const Vector3 &point) const;
|
||||
|
||||
|
||||
Vector3 ClosestPoint(const Vector3 &p) const;
|
||||
|
||||
Plane PlaneCCW() const;
|
||||
|
||||
Plane PlaneCW() const;
|
||||
|
||||
Vector3 Vertex(int i) const;
|
||||
|
||||
LineSegment Edge(int i) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
@@ -1,8 +1,9 @@
|
||||
//
|
||||
// Created by dawsh on 1/25/24.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef J3ML_TRIANGLE2D_H
|
||||
#define J3ML_TRIANGLE2D_H
|
||||
|
||||
#endif //J3ML_TRIANGLE2D_H
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class Triangle2D {
|
||||
public:
|
||||
};
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
class TriangleMesh
|
||||
{
|
||||
|
@@ -1,17 +1,242 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Created by josh on 12/25/2023.
|
||||
//
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <stdfloat>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
namespace J3ML
|
||||
namespace J3ML::SizedIntegralTypes
|
||||
{
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using u128 = __uint128_t;
|
||||
|
||||
using s8 = int8_t;
|
||||
using s16 = int16_t;
|
||||
using s32 = int32_t;
|
||||
using s64 = int64_t;
|
||||
using s128 = __int128_t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace J3ML::SizedFloatTypes
|
||||
{
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
using f128 = long double;
|
||||
|
||||
}
|
||||
|
||||
using namespace J3ML::SizedIntegralTypes;
|
||||
using namespace J3ML::SizedFloatTypes;
|
||||
|
||||
namespace J3ML::Math
|
||||
{
|
||||
|
||||
bool EqualAbs(float a, float b, float epsilon = 1e-3f);
|
||||
float RecipFast(float x);
|
||||
// Coming soon: Units Namespace
|
||||
// For Dimensional Analysis
|
||||
/*
|
||||
namespace Units
|
||||
{
|
||||
|
||||
struct Unit {};
|
||||
|
||||
struct Meters : public Unit { };
|
||||
struct ImperialInches : public Unit {};
|
||||
struct Time : public Unit {};
|
||||
struct Grams : public Unit {};
|
||||
|
||||
|
||||
struct MetersPerSecond : public Unit {};
|
||||
|
||||
|
||||
template <typename TUnit>
|
||||
struct Quantity
|
||||
{
|
||||
public:
|
||||
|
||||
float Value;
|
||||
};
|
||||
|
||||
struct Mass : public Quantity<Grams> {};
|
||||
struct Length : public Quantity<Meters> { };
|
||||
|
||||
struct Velocity : public Quantity<MetersPerSecond>{ };
|
||||
|
||||
class MetrixPrefix
|
||||
{
|
||||
public:
|
||||
std::string Prefix;
|
||||
std::string Symbol;
|
||||
int Power;
|
||||
float InverseMultiply(float input) const
|
||||
{
|
||||
return std::pow(input, -Power);
|
||||
}
|
||||
float Multiply(float input) const
|
||||
{
|
||||
return std::pow(input, Power);
|
||||
}
|
||||
|
||||
};
|
||||
namespace Prefixes
|
||||
{
|
||||
static constexpr MetrixPrefix Tera {"tera", "T", 12};
|
||||
static constexpr MetrixPrefix Giga {"giga", "G", 9};
|
||||
static constexpr MetrixPrefix Mega {"mega", "M", 6};
|
||||
static constexpr MetrixPrefix Kilo {"kilo", "k", 3};
|
||||
static constexpr MetrixPrefix Hecto {"hecto", "h", 2};
|
||||
static constexpr MetrixPrefix Deca {"deca", "da", 1};
|
||||
static constexpr MetrixPrefix None {"", "", 0};
|
||||
static constexpr MetrixPrefix Deci {"", "", 0};
|
||||
static constexpr MetrixPrefix Centi {"", "", 0};
|
||||
static constexpr MetrixPrefix Milli {"", "", 0};
|
||||
static constexpr MetrixPrefix Micro {"", "", 0};
|
||||
static constexpr MetrixPrefix Nano {"", "", 0};
|
||||
static constexpr MetrixPrefix Pico {"", "", 0};
|
||||
}
|
||||
|
||||
Length operator ""_meters(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
Length operator ""_m(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
constexpr Length operator ""_kilometers(long double value)
|
||||
{
|
||||
return Length {(float)value};
|
||||
}
|
||||
Length operator ""_km(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
Length operator ""_centimeters(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
Length operator ""_cm(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
|
||||
Length operator ""_millimeters(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
Length operator ""_mm(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
|
||||
Velocity operator ""_mps(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
|
||||
Velocity operator ""_meters_per_second(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
|
||||
Velocity operator ""_kmps(long double value)
|
||||
{
|
||||
return {(float)value};
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
#pragma region Constants
|
||||
static const float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f;
|
||||
static const float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364f;
|
||||
static const float GoldenRatio = 1.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375f;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Math Functions
|
||||
|
||||
inline float Radians(float degrees);
|
||||
inline float Degrees(float radians);
|
||||
|
||||
|
||||
struct NumberRange
|
||||
{
|
||||
float LowerBound;
|
||||
float UpperBound;
|
||||
};
|
||||
|
||||
|
||||
float NormalizeToRange(float input, float fromLower, float fromUpper, float toLower, float toUpper);
|
||||
float NormalizeToRange(float input, const NumberRange& from, const NumberRange& to);
|
||||
// auto rotation_normalized = NormalizeToRange(inp, {0, 360}, {-1, 1});
|
||||
|
||||
inline float Lerp(float a, float b, float t);
|
||||
/// Linearly interpolates from a to b, under the modulus mod.
|
||||
/// This function takes evaluates a and b in the range [0, mod] and takes the shorter path to reach from a to b.
|
||||
inline float LerpMod(float a, float b, float mod, float t);
|
||||
/// Computes the lerp factor a and b have to be Lerp()ed to get x.
|
||||
inline float InverseLerp(float a, float b, float x);
|
||||
/// See http://msdn.microsoft.com/en-us/library/bb509665(v=VS.85).aspx
|
||||
inline float Step(float y, float x);
|
||||
/// See http://msdn.microsoft.com/en-us/library/bb509658(v=vs.85).aspx
|
||||
inline float Ramp(float min, float max, float x);
|
||||
|
||||
inline float PingPongMod(float x, float mod);
|
||||
|
||||
inline float Sqrt(float x);
|
||||
inline float FastSqrt(float x);
|
||||
/// Returns 1/Sqrt(x). (The reciprocal of the square root of x)
|
||||
inline float RSqrt(float x);
|
||||
|
||||
inline float FastRSqrt(float x);
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
namespace BitTwiddling
|
||||
{
|
||||
/// Parses a string of form "011101010" to a u32
|
||||
u32 BinaryStringToValue(const char* s);
|
||||
|
||||
/// Returns the number of 1's set in the given value.
|
||||
inline int CountBitsSet(u32 value);
|
||||
}
|
||||
|
||||
namespace Interp
|
||||
{
|
||||
inline float SmoothStart(float t);
|
||||
}
|
||||
|
||||
struct Rotation
|
||||
{
|
||||
public:
|
||||
Rotation();
|
||||
Rotation(float value);
|
||||
float valueInRadians;
|
||||
float ValueInRadians() const;
|
||||
float ValueInDegrees() const;
|
||||
Rotation operator+(const Rotation& rhs);
|
||||
};
|
||||
|
||||
|
||||
Rotation operator ""_rad(long double rads);
|
||||
|
||||
Rotation operator ""_radians(long double rads);
|
||||
|
||||
Rotation operator ""_deg(long double rads);
|
||||
|
||||
Rotation operator ""_degrees(long double rads);
|
||||
|
||||
}
|
@@ -1,76 +1,30 @@
|
||||
//// Dawsh Linear Algebra Library - Everything you need for 3D math
|
||||
/// @file LinearAlgebra.h
|
||||
/// @description Includes all LinearAlgebra classes and functions
|
||||
/// @author Josh O'Leary, William Tomasine II
|
||||
/// @copyright 2024 Redacted Software
|
||||
/// @license Unlicense - Public Domain
|
||||
/// @revision 1.3
|
||||
/// @edited 2024-02-26
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace Math
|
||||
{
|
||||
const float Pi = M_PI;
|
||||
inline float Radians(float degrees) { return degrees * (Pi/180.f); }
|
||||
inline float Degrees(float radians) { return radians * (180.f/Pi); }
|
||||
|
||||
|
||||
struct NumberRange
|
||||
{
|
||||
float LowerBound;
|
||||
float UpperBound;
|
||||
};
|
||||
|
||||
|
||||
float NormalizeToRange(float input, float fromLower, float fromUpper, float toLower, float toUpper);
|
||||
float NormalizeToRange(float input, const NumberRange& from, const NumberRange& to);
|
||||
// auto rotation_normalized = NormalizeToRange(inp, {0, 360}, {-1, 1});
|
||||
inline float Lerp(float a, float b, float t);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Dawsh Linear Algebra Library - Everything you need for 3D math
|
||||
namespace LinearAlgebra {
|
||||
class Vector2; // A type representing a position in a 2-dimensional coordinate space.
|
||||
class Vector3; // A type representing a position in a 3-dimensional coordinate space.
|
||||
class Vector4; // A type representing a position in a 4-dimensional coordinate space.
|
||||
class Angle2D; // Uses x,y components to represent a 2D rotation.
|
||||
class EulerAngle; // Uses pitch,yaw,roll components to represent a 3D orientation.
|
||||
class AxisAngle; //
|
||||
class CoordinateFrame; //
|
||||
class Matrix2x2;
|
||||
class Matrix3x3;
|
||||
class Matrix4x4;
|
||||
class Transform2D;
|
||||
class Transform3D;
|
||||
class Quaternion;
|
||||
|
||||
|
||||
using Position = Vector3;
|
||||
}
|
||||
|
||||
// TODO: Enforce Style Consistency (Function Names use MicroSoft Case)
|
||||
|
||||
// Note: Josh Linear Algebra Types are designed as Immutable Data Types
|
||||
// x, y, z, w, etc. values should not and can not be set directly.
|
||||
// rather, you just construct a new type and assign it.
|
||||
// This might sound ass-backwards for many object types.
|
||||
// But mathematically, a vector or matrix is defined by it's size, and values.
|
||||
// Changing the value of one axis fundamentally changes the definition of the vector/matrix.
|
||||
// So we enforce this conceptually at code level...
|
||||
// If you're wondering how it remains performant, it only heap-allocates a tiny space (4*n bytes for vectors) (4*n*m bytes for matrices)
|
||||
// Just Trust Me Bro - Josjh
|
||||
#define MUTABLE true // Toggle This For: Ugly math, ugly code, and an ugly genital infection!
|
||||
|
||||
#if MUTABLE
|
||||
#define IMMUTABLE !MUTABLE
|
||||
#endif
|
||||
|
||||
namespace LinearAlgebra
|
||||
{
|
||||
// TODO: Implement Templated Linear Algebra
|
||||
|
||||
|
||||
// Library Code //
|
||||
|
||||
#include "J3ML/LinearAlgebra/Vector2.h"
|
||||
#include "J3ML/LinearAlgebra/Vector3.h"
|
||||
#include "J3ML/LinearAlgebra/Vector4.h"
|
||||
#include "J3ML/LinearAlgebra/Quaternion.h"
|
||||
#include "J3ML/LinearAlgebra/AxisAngle.h"
|
||||
#include "J3ML/LinearAlgebra/EulerAngle.h"
|
||||
#include "J3ML/LinearAlgebra/Matrix2x2.h"
|
||||
#include "J3ML/LinearAlgebra/Matrix3x3.h"
|
||||
#include "J3ML/LinearAlgebra/Matrix4x4.h"
|
||||
#include "J3ML/LinearAlgebra/Transform2D.h"
|
||||
#include "J3ML/LinearAlgebra/CoordinateFrame.h"
|
||||
|
||||
|
||||
|
||||
}
|
||||
using namespace J3ML::LinearAlgebra;
|
||||
|
@@ -1,14 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
class Angle2D {
|
||||
public:
|
||||
float x;
|
||||
float y;
|
||||
Angle2D(Math::Rotation rads);
|
||||
Angle2D(float X, float Y)
|
||||
{
|
||||
x = X;
|
||||
y = Y;
|
||||
}
|
||||
|
||||
bool operator==(const Angle2D& rhs) const {
|
||||
return (this->x==rhs.x && this->y==rhs.y);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/LinearAlgebra/EulerAngle.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
namespace LinearAlgebra
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
|
||||
/// Transitional datatype, not useful for internal representation of rotation
|
||||
|
28
include/J3ML/LinearAlgebra/Common.h
Normal file
28
include/J3ML/LinearAlgebra/Common.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// Forward Declarations for classes that include each other
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
class Vector2; // A type representing a position in a 2-dimensional coordinate space.
|
||||
class Vector3; // A type representing a position in a 3-dimensional coordinate space.
|
||||
class Vector4; // A type representing a position in a 4-dimensional coordinate space.
|
||||
class Angle2D; // Uses x,y components to represent a 2D rotation.
|
||||
class EulerAngle; // Uses pitch,yaw,roll components to represent a 3D orientation.
|
||||
class AxisAngle; //
|
||||
class CoordinateFrame; //
|
||||
class Matrix2x2;
|
||||
class Matrix3x3;
|
||||
class Matrix4x4;
|
||||
class Transform2D;
|
||||
class Transform3D;
|
||||
class Quaternion;
|
||||
|
||||
|
||||
using Position = Vector3;
|
||||
}
|
||||
|
||||
// Methods required by LinearAlgebra types
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
namespace LinearAlgebra
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
/// The CFrame is fundamentally 4 vectors (position, forward, right, up vector)
|
||||
class CoordinateFrame
|
||||
|
@@ -1,8 +1,12 @@
|
||||
#pragma once
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.h>
|
||||
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
class AxisAngle;
|
||||
|
||||
// Essential Reading:
|
||||
// http://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf
|
||||
|
66
include/J3ML/LinearAlgebra/Matrix.h
Normal file
66
include/J3ML/LinearAlgebra/Matrix.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include "Vector.h"
|
||||
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
|
||||
|
||||
template <uint ROWS, uint COLS, typename T>
|
||||
class Matrix
|
||||
{
|
||||
static constexpr uint Diag = std::min(ROWS, COLS);
|
||||
|
||||
using RowVector = Vector<ROWS, T>;
|
||||
using ColVector = Vector<COLS, T>;
|
||||
using DiagVector = Vector<Diag, T>;
|
||||
|
||||
enum { Rows = ROWS };
|
||||
enum { Cols = COLS };
|
||||
|
||||
void AssertRowSize(uint rows)
|
||||
{
|
||||
assert(rows < Rows && "");
|
||||
}
|
||||
void AssertColumnSize(uint cols)
|
||||
{
|
||||
assert(cols < Cols && "");
|
||||
}
|
||||
|
||||
RowVector GetRow(uint index) const;
|
||||
ColVector GetColumn(uint index) const;
|
||||
void SetRow(uint index, RowVector);
|
||||
void SetColumn(uint index, ColVector);
|
||||
|
||||
RowVector &Row(uint index) const;
|
||||
ColVector &Column(uint index) const;
|
||||
|
||||
const T At(uint row, uint col) const
|
||||
{
|
||||
AssertRowSize(row);
|
||||
AssertColumnSize(col);
|
||||
|
||||
return elems[row][col];
|
||||
}
|
||||
|
||||
T &At(uint row, uint col)
|
||||
{
|
||||
AssertRowSize(row);
|
||||
AssertColumnSize(col);
|
||||
|
||||
return elems[row][col];
|
||||
}
|
||||
|
||||
float* ptr();
|
||||
const float* ptr() const;
|
||||
|
||||
float operator[](uint index) const;
|
||||
float& operator[](uint index);
|
||||
|
||||
private:
|
||||
T elems[ROWS][COLS];
|
||||
};
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class Matrix2x2 {
|
||||
public:
|
||||
enum { Rows = 3 };
|
||||
@@ -16,9 +16,17 @@ namespace LinearAlgebra {
|
||||
Matrix2x2(float val);
|
||||
Matrix2x2(float m00, float m01, float m10, float m11);
|
||||
Matrix2x2(const Vector2& r1, const Vector2& r2);
|
||||
explicit Matrix2x2(const float *data);
|
||||
|
||||
Vector2 GetRow(int index) const;
|
||||
Vector2 GetColumn(int index) const;
|
||||
|
||||
void SetRow(int i, const Vector2& row);
|
||||
void SetColumn(int i, const Vector2& col);
|
||||
void SetAt(int x, int y, float value);
|
||||
|
||||
float At(int x, int y) const;
|
||||
float &At(int x, int y);
|
||||
|
||||
float Determinant() const;
|
||||
Matrix2x2 Inverse() const;
|
||||
|
@@ -1,11 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/LinearAlgebra/Common.h>
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
|
||||
class Quaternion;
|
||||
|
||||
/// A 3-by-3 matrix for linear transformations of 3D geometry.
|
||||
/* This can represent any kind of linear transformations of 3D geometry, which include
|
||||
* rotation, scale, shear, mirroring, and orthographic projection.
|
||||
@@ -22,6 +26,7 @@ namespace LinearAlgebra {
|
||||
* vectors in the form M * v. This means that "Matrix3x3 M, M1, M2; M = M1 * M2;" gives a transformation M
|
||||
* that applies M2 first, followed by M1 second
|
||||
*/
|
||||
|
||||
class Matrix3x3 {
|
||||
public:
|
||||
enum { Rows = 3 };
|
||||
@@ -36,6 +41,8 @@ namespace LinearAlgebra {
|
||||
Matrix3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22);
|
||||
Matrix3x3(const Vector3& r1, const Vector3& r2, const Vector3& r3);
|
||||
explicit Matrix3x3(const Quaternion& orientation);
|
||||
explicit Matrix3x3(const float *data);
|
||||
|
||||
|
||||
static Matrix3x3 RotateX(float radians);
|
||||
static Matrix3x3 RotateY(float radians);
|
||||
@@ -44,6 +51,10 @@ namespace LinearAlgebra {
|
||||
Vector3 GetRow(int index) const;
|
||||
Vector3 GetColumn(int index) const;
|
||||
|
||||
Vector3 GetRow3(int index) const;
|
||||
|
||||
Vector3 GetColumn3(int index) const;
|
||||
|
||||
float &At(int row, int col);
|
||||
float At(int x, int y) const;
|
||||
|
||||
@@ -52,25 +63,14 @@ namespace LinearAlgebra {
|
||||
/// Creates a new M3x3 that rotates about the given axis by the given angle
|
||||
static Matrix3x3 RotateAxisAngle(const Vector3& axis, float angleRadians);
|
||||
|
||||
static Matrix3x3 RotateFromTo(const Vector3& source, const Vector3& direction)
|
||||
{
|
||||
|
||||
}
|
||||
// TODO: Implement
|
||||
static Matrix3x3 RotateFromTo(const Vector3& source, const Vector3& direction);
|
||||
|
||||
void SetRow(int i, const Vector3 &vector3);
|
||||
void SetColumn(int i, const Vector3& vector);
|
||||
void SetAt(int x, int y, float value);
|
||||
|
||||
void Orthonormalize(int c0, int c1, int c2)
|
||||
{
|
||||
Vector3 v0 = GetColumn(c0);
|
||||
Vector3 v1 = GetColumn(c1);
|
||||
Vector3 v2 = GetColumn(c2);
|
||||
Vector3::Orthonormalize(v0, v1, v2);
|
||||
SetColumn(c0, v0);
|
||||
SetColumn(c1, v1);
|
||||
SetColumn(c2, v2);
|
||||
}
|
||||
void Orthonormalize(int c0, int c1, int c2);
|
||||
|
||||
static Matrix3x3 LookAt(const Vector3& forward, const Vector3& target, const Vector3& localUp, const Vector3& worldUp);
|
||||
|
||||
@@ -90,8 +90,8 @@ namespace LinearAlgebra {
|
||||
|
||||
/// Creates a new transformation matrix that scales by the given factors.
|
||||
// This matrix scales with respect to origin.
|
||||
static Matrix3x3 Scale(float sx, float sy, float sz);
|
||||
static Matrix3x3 Scale(const Vector3& scale);
|
||||
static Matrix3x3 FromScale(float sx, float sy, float sz);
|
||||
static Matrix3x3 FromScale(const Vector3& scale);
|
||||
|
||||
/// Returns the main diagonal.
|
||||
Vector3 Diagonal() const;
|
||||
@@ -124,13 +124,35 @@ namespace LinearAlgebra {
|
||||
Vector2 Transform(const Vector2& rhs) const;
|
||||
Vector3 Transform(const Vector3& rhs) const;
|
||||
|
||||
Vector3 operator[](int row) const
|
||||
{
|
||||
return Vector3{elems[row][0], elems[row][1], elems[row][2]};
|
||||
}
|
||||
|
||||
Matrix3x3 ScaleBy(const Vector3& rhs);
|
||||
Vector3 GetScale() const;
|
||||
|
||||
Vector3 operator[](int row) const;
|
||||
|
||||
Vector2 operator * (const Vector2& rhs) const;
|
||||
Vector3 operator * (const Vector3& rhs) const;
|
||||
Matrix3x3 operator * (const Matrix3x3& rhs) const;
|
||||
Matrix4x4 operator * (const Matrix4x4& rhs) const;
|
||||
Matrix3x3 Mul(const Matrix3x3& rhs) const;
|
||||
Matrix4x4 Mul(const Matrix4x4& rhs) const;
|
||||
Vector2 Mul(const Vector2& rhs) const;
|
||||
Vector3 Mul(const Vector3& rhs) const;
|
||||
Vector4 Mul(const Vector4& rhs) const;
|
||||
Quaternion Mul(const Quaternion& rhs) const;
|
||||
|
||||
// Returns true if the column vectors of this matrix are all perpendicular to each other.
|
||||
bool IsColOrthogonal(float epsilon = 1e-3f) const;
|
||||
// Returns true if the row vectors of this matrix are all perpendicular to each other.
|
||||
bool IsRowOrthogonal(float epsilon = 1e-3f) const;
|
||||
|
||||
|
||||
|
||||
bool HasUniformScale(float epsilon = 1e-3f) const;
|
||||
|
||||
Vector3 ExtractScale() const {
|
||||
return {GetColumn(0).Length(), GetColumn(1).Length(), GetColumn(2).Length()};
|
||||
}
|
||||
|
||||
protected:
|
||||
float elems[3][3];
|
||||
|
@@ -1,9 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/LinearAlgebra/Common.h>
|
||||
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector4.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
/// A 4-by-4 matrix for affine transformations and perspective projections of 3D geometry.
|
||||
/* This matrix can represent the most generic form of transformations for 3D objects,
|
||||
@@ -40,6 +46,8 @@ namespace LinearAlgebra {
|
||||
/// Constructs this float4x4 to represent the same transformation as the given float3x3.
|
||||
/** This function expands the last row and column of this matrix with the elements from the identity matrix. */
|
||||
Matrix4x4(const Matrix3x3&);
|
||||
explicit Matrix4x4(const float* data);
|
||||
|
||||
/// Constructs a new float4x4 by explicitly specifying all the matrix elements.
|
||||
/// The elements are specified in row-major format, i.e. the first row first followed by the second and third row.
|
||||
/// E.g. The element _10 denotes the scalar at second (index 1) row, first (index 0) column.
|
||||
@@ -113,10 +121,25 @@ namespace LinearAlgebra {
|
||||
void SetRow(int row, const Vector4& rowVector);
|
||||
void SetRow(int row, float m_r0, float m_r1, float m_r2, float m_r3);
|
||||
|
||||
void SetCol(int col, const Vector3& colVector, float m_c3);
|
||||
void SetCol(int col, const Vector4& colVector);
|
||||
void SetCol(int col, float m_c0, float m_c1, float m_c2, float m_c3);
|
||||
void SetCol(int col, const float *data);
|
||||
|
||||
Vector4 GetRow(int index) const;
|
||||
Vector4 GetColumn(int index) const;
|
||||
Vector3 GetRow3(int index) const;
|
||||
Vector3 GetColumn3(int index) const;
|
||||
Vector4 Col(int i) const;
|
||||
Vector4 Row(int i) const;
|
||||
Vector4 Col3(int i) const;
|
||||
Vector4 Row3(int i) const;
|
||||
|
||||
Vector3 GetScale() const
|
||||
{
|
||||
|
||||
}
|
||||
Matrix4x4 Scale(const Vector3&);
|
||||
|
||||
float &At(int row, int col);
|
||||
float At(int x, int y) const;
|
||||
@@ -204,10 +227,10 @@ namespace LinearAlgebra {
|
||||
static Matrix4x4 D3DPerspProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
|
||||
static Matrix4x4 D3DPerspProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
|
||||
|
||||
static Matrix4x4 OpenGLOrthoProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
|
||||
static Matrix4x4 OpenGLOrthoProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
|
||||
static Matrix4x4 OpenGLPerspProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
|
||||
static Matrix4x4 OpenGLPerspProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
|
||||
static Matrix4x4 OpenGLOrthoProjLH(float n, float f, float h, float v);
|
||||
static Matrix4x4 OpenGLOrthoProjRH(float n, float f, float h, float v);
|
||||
static Matrix4x4 OpenGLPerspProjLH(float n, float f, float h, float v);
|
||||
static Matrix4x4 OpenGLPerspProjRH(float n, float f, float h, float v);
|
||||
|
||||
Vector4 operator[](int row);
|
||||
|
||||
@@ -218,12 +241,16 @@ namespace LinearAlgebra {
|
||||
Matrix4x4 operator *(float scalar) const;
|
||||
Matrix4x4 operator /(float scalar) const;
|
||||
|
||||
|
||||
Vector4 operator[](int row) const;
|
||||
|
||||
Vector2 operator * (const Vector2& rhs) const;
|
||||
Vector3 operator * (const Vector3& rhs) const;
|
||||
Vector4 operator * (const Vector4& rhs) const;
|
||||
|
||||
Vector2 Mul(const Vector2& rhs) const;
|
||||
Vector3 Mul(const Vector3& rhs) const;
|
||||
Vector4 Mul(const Vector4& rhs) const;
|
||||
|
||||
Matrix4x4 operator * (const Matrix3x3 &rhs) const;
|
||||
|
||||
Matrix4x4 operator +() const;
|
||||
@@ -234,9 +261,25 @@ namespace LinearAlgebra {
|
||||
Matrix4x4 &operator = (const Quaternion& rhs);
|
||||
Matrix4x4 &operator = (const Matrix4x4& rhs) = default;
|
||||
|
||||
|
||||
Vector3 ExtractScale() const;
|
||||
|
||||
bool HasUniformScale(float epsilon = 1e-3f) const;
|
||||
bool IsColOrthogonal3(float epsilon = 1e-3f) const;
|
||||
bool IsRowOrthogonal3(float epsilon = 1e-3f) const;
|
||||
|
||||
bool IsColOrthogonal(float epsilon = 1e-3f) const;
|
||||
bool IsRowOrthogonal(float epsilon = 1e-3f) const;
|
||||
/// Returns true if this matrix is seen to contain a "projective" part,
|
||||
/// i.e. whether the last row of this matrix differs from [0 0 0 1]
|
||||
bool ContainsProjection(float epsilon = 1e-3f) const;
|
||||
|
||||
void InverseOrthonormal();
|
||||
|
||||
protected:
|
||||
float elems[4][4];
|
||||
|
||||
|
||||
Vector3 TransformDir(float tx, float ty, float tz) const;
|
||||
};
|
||||
}
|
@@ -1,13 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
|
||||
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
#include <J3ML/LinearAlgebra/Vector4.h>
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
//#include <J3ML/LinearAlgebra/AxisAngle.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace LinearAlgebra
|
||||
|
||||
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
|
||||
class Matrix3x3;
|
||||
|
||||
class Quaternion : public Vector4 {
|
||||
public:
|
||||
Quaternion();
|
||||
@@ -23,13 +32,9 @@ namespace LinearAlgebra
|
||||
|
||||
// Constructs this quaternion by specifying a rotation axis and the amount of rotation to be performed about that axis
|
||||
// @param rotationAxis The normalized rotation axis to rotate about. If using Vector4 version of the constructor, the w component of this vector must be 0.
|
||||
Quaternion(const Vector3 &rotationAxis, float rotationAngleBetween) {
|
||||
SetFromAxisAngle(rotationAxis, rotationAngleBetween);
|
||||
}
|
||||
Quaternion(const Vector3 &rotationAxis, float rotationAngleBetween);
|
||||
|
||||
Quaternion(const Vector4 &rotationAxis, float rotationAngleBetween) {
|
||||
SetFromAxisAngle(rotationAxis, rotationAngleBetween);
|
||||
}
|
||||
Quaternion(const Vector4 &rotationAxis, float rotationAngleBetween);
|
||||
//void Inverse();
|
||||
|
||||
explicit Quaternion(Vector4 vector4);
|
||||
@@ -49,15 +54,9 @@ namespace LinearAlgebra
|
||||
|
||||
Vector3 GetWorldZ() const;
|
||||
|
||||
Vector3 GetAxis() const {
|
||||
float rcpSinAngle = 1 - (std::sqrt(1 - w * w));
|
||||
Vector3 GetAxis() const;
|
||||
|
||||
return Vector3(x, y, z) * rcpSinAngle;
|
||||
}
|
||||
|
||||
float GetAngle() const {
|
||||
return std::acos(w) * 2.f;
|
||||
}
|
||||
float GetAngle() const;
|
||||
|
||||
|
||||
Matrix3x3 ToMatrix3x3() const;
|
||||
@@ -102,7 +101,7 @@ namespace LinearAlgebra
|
||||
Quaternion operator - () const;
|
||||
float Dot(const Quaternion &quaternion) const;
|
||||
|
||||
float Angle() const { return std::acos(w) * 2.f;}
|
||||
float Angle() const { return acos(w) * 2.f;}
|
||||
|
||||
float AngleBetween(const Quaternion& target) const;
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class Transform2D {
|
||||
protected:
|
||||
Matrix3x3 transformation;
|
||||
|
@@ -1,13 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
template <typename T, int Dims>
|
||||
class templated_vector
|
||||
#include <cstdlib>
|
||||
|
||||
namespace J3ML::LinearAlgebra
|
||||
{
|
||||
template <uint DIMS, typename T>
|
||||
class Vector {
|
||||
public:
|
||||
enum { Dimensions = DIMS};
|
||||
T elems[DIMS];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
using v2f = templated_vector<float, 2>;
|
||||
using v3f = templated_vector<float, 3>;
|
||||
using v4f = templated_vector<float, 4>;
|
||||
}
|
||||
|
@@ -1,21 +1,28 @@
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "modernize-use-nodiscard"
|
||||
#pragma once
|
||||
#include <J3ML/J3ML.h>
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <cstddef>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
using namespace J3ML;
|
||||
|
||||
|
||||
|
||||
|
||||
/// A 2D (x, y) ordered pair.
|
||||
class Vector2 {
|
||||
public:
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
public:
|
||||
enum {Dimensions = 2};
|
||||
public:
|
||||
/// Default Constructor - Initializes values to zero
|
||||
Vector2();
|
||||
/// Constructs a new Vector2 with the value (X, Y)
|
||||
Vector2(float X, float Y);
|
||||
/// Constructs this float2 from a C array, to the value (data[0], data[1]).
|
||||
explicit Vector2(const float* data);
|
||||
// Constructs a new Vector2 with the value {scalar, scalar}
|
||||
explicit Vector2(float scalar);
|
||||
Vector2(const Vector2& rhs); // Copy Constructor
|
||||
//Vector2(Vector2&&) = default; // Move Constructor
|
||||
|
||||
@@ -25,19 +32,43 @@ namespace LinearAlgebra {
|
||||
static const Vector2 Down;
|
||||
static const Vector2 Right;
|
||||
static const Vector2 NaN;
|
||||
static const Vector2 Infinity;
|
||||
|
||||
float GetX() const;
|
||||
float GetY() const;
|
||||
void SetX(float newX);
|
||||
void SetY(float newY);
|
||||
|
||||
/// Casts this float2 to a C array.
|
||||
/** This function does not allocate new memory or make a copy of this float2. This function simply
|
||||
returns a C pointer view to this data structure. Use ptr()[0] to access the x component of this float2
|
||||
and ptr()[1] to access the y component.
|
||||
@note Since the returned pointer points to this class, do not dereference the pointer after this
|
||||
float2 has been deleted. You should never store a copy of the returned pointer.
|
||||
@note This function is provided for compatibility with other APIs which require raw C pointer access
|
||||
to vectors. Avoid using this function in general, and instead always use the operator []
|
||||
or the At() function to access the elements of this vector by index.
|
||||
@return A pointer to the first float element of this class. The data is contiguous in memory.
|
||||
@see operator [](), At(). */
|
||||
float* ptr();
|
||||
const float *ptr() const;
|
||||
|
||||
float operator[](std::size_t index) const;
|
||||
float &operator[](std::size_t index);
|
||||
|
||||
const float At(std::size_t index) const;
|
||||
|
||||
float &At(std::size_t index);
|
||||
|
||||
Vector2 Abs() const;
|
||||
|
||||
bool IsWithinMarginOfError(const Vector2& rhs, float margin=0.001f) const;
|
||||
|
||||
bool IsNormalized(float epsilonSq = 1e-5f) const;
|
||||
bool IsZero(float epsilonSq = 1e-6f) const;
|
||||
bool IsPerpendicular(const Vector2& other, float epsilonSq=1e-5f) const;
|
||||
|
||||
float operator[](std::size_t index);
|
||||
|
||||
bool operator == (const Vector2& rhs) const;
|
||||
bool operator != (const Vector2& rhs) const;
|
||||
|
||||
@@ -72,10 +103,6 @@ namespace LinearAlgebra {
|
||||
static float Magnitude(const Vector2& of);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsFinite() const;
|
||||
static bool IsFinite(const Vector2& v);
|
||||
|
||||
@@ -143,14 +170,20 @@ namespace LinearAlgebra {
|
||||
Vector2 operator +() const; // TODO: Implement
|
||||
Vector2 operator -() const;
|
||||
/// Assigns a vector to another
|
||||
Vector2& operator+=(const Vector2& rhs); // Adds a vector to this vector, in-place.
|
||||
Vector2& operator-=(const Vector2& rhs); // Subtracts a vector from this vector, in-place
|
||||
Vector2 &operator =(const Vector2 &rhs);
|
||||
Vector2& operator+=(const Vector2& rhs);
|
||||
Vector2& operator-=(const Vector2& rhs);
|
||||
Vector2& operator*=(float scalar);
|
||||
Vector2& operator/=(float scalar);
|
||||
|
||||
public:
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
|
||||
/// Tests if the triangle a->b->c is oriented counter-clockwise.
|
||||
/** Returns true if the triangle a->b->c is oriented counter-clockwise, when viewed in the XY-plane
|
||||
where x spans to the right and y spans up.
|
||||
Another way to think of this is that this function returns true, if the point C lies to the left
|
||||
of the directed line AB. */
|
||||
static bool OrientedCCW(const Vector2 &, const Vector2 &, const Vector2 &);
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -158,4 +191,5 @@ namespace LinearAlgebra {
|
||||
{
|
||||
return rhs * lhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
@@ -1,26 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <J3ML/LinearAlgebra/Angle2D.h>
|
||||
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
// A 3D (x, y, z) ordered pair.
|
||||
class Vector3 {
|
||||
public:
|
||||
|
||||
// Default Constructor - Initializes to zero
|
||||
Vector3();
|
||||
// Constructs a new Vector3 with the value (X, Y, Z)
|
||||
Vector3(float X, float Y, float Z);
|
||||
Vector3(const Vector3& rhs); // Copy Constructor
|
||||
Vector3(Vector3&&) = default; // Move Constructor
|
||||
Vector3& operator=(const Vector3& rhs);
|
||||
|
||||
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
public:
|
||||
enum {Dimensions = 3};
|
||||
public:
|
||||
static const Vector3 Zero;
|
||||
static const Vector3 Up;
|
||||
static const Vector3 Down;
|
||||
@@ -29,38 +27,70 @@ public:
|
||||
static const Vector3 Forward;
|
||||
static const Vector3 Backward;
|
||||
static const Vector3 NaN;
|
||||
static const Vector3 Infinity;
|
||||
static const Vector3 NegativeInfinity;
|
||||
public:
|
||||
|
||||
static void Orthonormalize(Vector3& a, Vector3& b)
|
||||
{
|
||||
a = a.Normalize();
|
||||
b = b - b.ProjectToNorm(a);
|
||||
b = b.Normalize();
|
||||
}
|
||||
/// The default constructor does not initialize any members of this class.
|
||||
/** This means that the values of the members x, y and z are all undefined after creating a new Vector3 using
|
||||
this default constructor. Remember to assign to them before use.
|
||||
@see x, y, z. */
|
||||
Vector3();
|
||||
// Constructs a new Vector3 with the value (X, Y, Z)
|
||||
Vector3(float X, float Y, float Z);
|
||||
Vector3(const Vector2& XY, float Z);
|
||||
Vector3(const Vector3& rhs); // Copy Constructor
|
||||
Vector3(Vector3&&) = default; // Move Constructor
|
||||
/// Constructs this float3 from a C array, to the value (data[0], data[1], data[2]).
|
||||
/** @param data An array containing three elements for x, y and z. This pointer may not be null. */
|
||||
explicit Vector3(const float* data);
|
||||
/// Constructs a new Vector3 with the value (scalar, scalar, scalar).
|
||||
explicit Vector3(float scalar);
|
||||
|
||||
|
||||
/// Casts this float3 to a C array.
|
||||
/** This function does not allocate new memory or make a copy of this Vector3. This function simply
|
||||
returns a C pointer view to this data structure. Use ptr()[0] to access the x component of this float3,
|
||||
ptr()[1] to access y, and ptr()[2] to access the z component of this Vector3.
|
||||
@note Since the returned pointer points to this class, do not dereference the pointer after this
|
||||
float3 has been deleted. You should never store a copy of the returned pointer.
|
||||
@note This function is provided for compatibility with other APIs which require raw C pointer access
|
||||
to vectors. Avoid using this function in general, and instead always use the operator [] or
|
||||
the At() function to access the elements of this vector by index.
|
||||
@return A pointer to the first float element of this class. The data is contiguous in memory.
|
||||
@see operator [](), At(). */
|
||||
float* ptr();
|
||||
[[nodiscard]] const float *ptr() const { return &x;}
|
||||
|
||||
/// Accesses an element of this vector using array notation.
|
||||
/** @param index The element to get. Pass in 0 for x, 1 for y and 2 for z.
|
||||
@note If you have a non-const instance of this class, you can use this notation to set the elements of
|
||||
this vector as well, e.g. vec[1] = 10.f; would set the y-component of this vector.
|
||||
@see ptr(), At(). */
|
||||
float operator[](std::size_t index) const;
|
||||
float &operator[](std::size_t index);
|
||||
|
||||
/// Accesses an element of this vector.
|
||||
/** @param index The element to get. Pass in 0 for x, 1 for y, and 2 for z.
|
||||
@note If you have a non-const instance of this class, you can use this notation to set the elements of
|
||||
this vector as well, e.g. vec.At(1) = 10.f; would set the y-component of this vector.
|
||||
@see ptr(), operator [](). */
|
||||
float At(int index) const;
|
||||
float &At(int index);
|
||||
|
||||
static void Orthonormalize(Vector3& a, Vector3& b);
|
||||
|
||||
Vector3 Abs() const;
|
||||
static Vector3 Abs(const Vector3& rhs);
|
||||
|
||||
/// Returns the DirectionVector for a given angle.
|
||||
static Vector3 Direction(const Vector3 &rhs) ;
|
||||
|
||||
static void Orthonormalize(Vector3& a, Vector3& b, Vector3& c);
|
||||
|
||||
static void Orthonormalize(Vector3& a, Vector3& b, Vector3& c)
|
||||
{
|
||||
a = a.Normalize();
|
||||
b = b - b.ProjectToNorm(a);
|
||||
b = b.Normalize();
|
||||
c = c - c.ProjectToNorm(a);
|
||||
c = c - c.ProjectToNorm(b);
|
||||
c = c.Normalize();
|
||||
}
|
||||
bool AreOrthonormal(const Vector3& a, const Vector3& b, float epsilon);
|
||||
|
||||
bool AreOrthonormal(const Vector3& a, const Vector3& b, float epsilon)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Vector3 ProjectToNorm(const Vector3& direction)
|
||||
{
|
||||
return direction * this->Dot(direction);
|
||||
}
|
||||
Vector3 ProjectToNorm(const Vector3& direction) const;
|
||||
|
||||
float GetX() const;
|
||||
float GetY() const;
|
||||
@@ -74,19 +104,36 @@ public:
|
||||
bool IsZero(float epsilonSq = 1e-6f) const;
|
||||
bool IsPerpendicular(const Vector3& other, float epsilonSq=1e-5f) const;
|
||||
|
||||
float operator[](std::size_t index) const;
|
||||
|
||||
bool operator == (const Vector3& rhs) const;
|
||||
bool operator != (const Vector3& rhs) const;
|
||||
|
||||
bool IsFinite() const
|
||||
{
|
||||
return std::isfinite(x) && std::isfinite(y) && std::isfinite(z);
|
||||
}
|
||||
bool IsFinite() const;
|
||||
float MinElement() const;
|
||||
static float MinElement(const Vector3& of);
|
||||
|
||||
// Normalizes this Vector3.
|
||||
/** In the case of failure, this vector is set to (1, 0, 0), so calling this function will never result in an
|
||||
unnormalized vector.
|
||||
@note If this function fails to normalize the vector, no error message is printed, the vector is set to (1,0,0) and
|
||||
an error code 0 is returned. This is different than the behavior of the Normalized() function, which prints an
|
||||
error if normalization fails.
|
||||
@note This function operates in-place.
|
||||
@return The old length of this vector, or 0 if normalization failed.
|
||||
@see Normalized(). */
|
||||
float TryNormalize();
|
||||
|
||||
/// Computes a new normalized direction vector that is perpendicular to this vector and the specified hint vector.
|
||||
/** If this vector points toward the hint vector, the vector hint2 is returned instead.
|
||||
@see AnotherPerpendicular(), Cross(). */
|
||||
Vector3 Perpendicular(const Vector3 &hint = Vector3(0,1,0), const Vector3 &hint2 = Vector3(0,0,1)) const;
|
||||
|
||||
Vector3 Min(const Vector3& min) const;
|
||||
static Vector3 Min(const Vector3& a, const Vector3& b, const Vector3& c);
|
||||
static Vector3 Min(const Vector3& lhs, const Vector3& rhs);
|
||||
|
||||
Vector3 Max(const Vector3& max) const;
|
||||
static Vector3 Max(const Vector3& a, const Vector3& b, const Vector3& c);
|
||||
static Vector3 Max(const Vector3& lhs, const Vector3& rhs);
|
||||
|
||||
Vector3 Clamp(const Vector3& min, const Vector3& max) const;
|
||||
@@ -95,6 +142,15 @@ public:
|
||||
/// Returns the magnitude between the two vectors.
|
||||
float Distance(const Vector3& to) const;
|
||||
static float Distance(const Vector3& from, const Vector3& to);
|
||||
//float Distance(const Ray&) const;
|
||||
//float Distance(const LineSegment&) const;
|
||||
//float Distance(const Plane&) const;
|
||||
//float DIstance(const Triangle&) const;
|
||||
|
||||
float DistanceSquared(const Vector3& to) const;
|
||||
// Function Alias for DistanceSquared
|
||||
float DistanceSq(const Vector3& to) const { return DistanceSquared(to); }
|
||||
static float DistanceSquared(const Vector3& from, const Vector3& to);
|
||||
|
||||
float Length() const;
|
||||
static float Length(const Vector3& of);
|
||||
@@ -132,15 +188,21 @@ public:
|
||||
Vector3 Lerp(const Vector3& goal, float alpha) const;
|
||||
static Vector3 Lerp(const Vector3& lhs, const Vector3& rhs, float alpha);
|
||||
|
||||
|
||||
/// Returns the angle between this vector and the specified vector, in radians.
|
||||
/** @note This function takes into account that this vector or the other vector can be unnormalized, and normalizes the computations.
|
||||
If you are computing the angle between two normalized vectors, it is better to use AngleBetweenNorm().
|
||||
@see AngleBetweenNorm(). */
|
||||
Angle2D AngleBetween(const Vector3& rhs) const;
|
||||
static Angle2D AngleBetween(const Vector3& lhs, const Vector3& rhs);
|
||||
|
||||
// Adds two vectors
|
||||
/// Adds two vectors
|
||||
Vector3 operator+(const Vector3& rhs) const;
|
||||
Vector3 Add(const Vector3& rhs) const;
|
||||
static Vector3 Add(const Vector3& lhs, const Vector3& rhs);
|
||||
|
||||
/// Adds the vector(s, s, s) to this vector
|
||||
Vector3 Add(float s) const;
|
||||
|
||||
/// Subtracts two vectors
|
||||
Vector3 operator-(const Vector3& rhs) const;
|
||||
Vector3 Sub(const Vector3& rhs) const;
|
||||
@@ -154,29 +216,37 @@ public:
|
||||
/// Multiplies this vector by a vector, element-wise
|
||||
/// @note Mathematically, the multiplication of two vectors is not defined in linear space structures,
|
||||
/// but this function is provided here for syntactical convenience.
|
||||
Vector3 Mul(const Vector3& rhs) const
|
||||
{
|
||||
|
||||
}
|
||||
Vector3 Mul(const Vector3& rhs) const;
|
||||
|
||||
/// Divides this vector by a scalar
|
||||
Vector3 operator/(float rhs) const;
|
||||
Vector3 Div(float scalar) const;
|
||||
static Vector3 Div(const Vector3& lhs, float rhs);
|
||||
|
||||
|
||||
/// Divides this vector by a vector, element-wise
|
||||
/// @note Mathematically, the multiplication of two vectors is not defined in linear space structures,
|
||||
/// but this function is provided here for syntactical convenience
|
||||
Vector2 Div(const Vector2& v) const;
|
||||
Vector3 Div(const Vector3& v) const;
|
||||
|
||||
/// Unary + operator
|
||||
Vector3 operator+() const; // TODO: Implement
|
||||
/// Unary - operator (Negation)
|
||||
Vector3 operator-() const;
|
||||
public:
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
|
||||
bool Equals(const Vector3& rhs, float epsilon = 1e-3f) const;
|
||||
bool Equals(float _x, float _y, float _z, float epsilon = 1e-3f) const;
|
||||
|
||||
Vector3 &operator =(const Vector3& rhs);
|
||||
Vector3& operator+=(const Vector3& rhs);
|
||||
Vector3& operator-=(const Vector3& rhs);
|
||||
Vector3& operator*=(float scalar);
|
||||
Vector3& operator/=(float scalar);
|
||||
|
||||
void Set(float d, float d1, float d2);
|
||||
};
|
||||
|
||||
static Vector3 operator*(float lhs, const Vector3& rhs)
|
||||
{
|
||||
return rhs * lhs;
|
||||
}
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
class Vector4 {
|
||||
public:
|
||||
// Default Constructor
|
||||
@@ -16,10 +16,19 @@ namespace LinearAlgebra {
|
||||
Vector4(Vector4&& move) = default;
|
||||
Vector4& operator=(const Vector4& rhs);
|
||||
|
||||
float GetX() const;
|
||||
float GetY() const;
|
||||
float GetZ() const;
|
||||
float GetW() const;
|
||||
float* ptr()
|
||||
{
|
||||
return &x;
|
||||
}
|
||||
Vector3 XYZ() const
|
||||
{
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
float GetX() const { return x; }
|
||||
float GetY() const { return y; }
|
||||
float GetZ() const { return z; }
|
||||
float GetW() const { return w; }
|
||||
#if MUTABLE
|
||||
void SetX(float newX) { x = newX;}
|
||||
void SetY(float newY) { y = newY;}
|
||||
@@ -30,16 +39,39 @@ namespace LinearAlgebra {
|
||||
static const Vector4 NaN;
|
||||
|
||||
float operator[](std::size_t index) const;
|
||||
float &operator[](std::size_t index);
|
||||
|
||||
bool IsWithinMarginOfError(const Vector4& rhs, float margin=0.0001f) const;
|
||||
bool IsNormalized(float epsilonSq = 1e-5f) const;
|
||||
|
||||
float LengthSqXYZ() const;
|
||||
|
||||
bool IsNormalized3(float epsilonSq = 1e-5f) const
|
||||
{
|
||||
return std::abs(LengthSqXYZ()-1.f) <= epsilonSq;
|
||||
}
|
||||
bool IsNormalized4(float epsilonSq = 1e-5f) const
|
||||
{
|
||||
return std::abs(LengthSquared()-1.f) <= epsilonSq;
|
||||
}
|
||||
bool IsNormalized(float epsilonSq = 1e-5f) const { return IsNormalized4(epsilonSq); }
|
||||
bool IsZero(float epsilonSq = 1e-6f) const;
|
||||
bool IsFinite() const;
|
||||
bool IsPerpendicular(const Vector4& other, float epsilonSq=1e-5f) const;
|
||||
bool IsPerpendicular(const Vector4& other, float epsilonSq=1e-5f) const
|
||||
{
|
||||
float dot = Dot(other);
|
||||
return dot*dot <= epsilonSq * LengthSquared() * other.LengthSquared();
|
||||
}
|
||||
bool IsPerpendicular3(const Vector4& other, float epsilonSq = 1e-5f) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool operator==(const Vector4& rhs) const;
|
||||
bool operator!=(const Vector4& rhs) const;
|
||||
|
||||
bool Equals(const Vector4& rhs, float epsilon = 1e-3f) const;
|
||||
bool Equals(float _x, float _y, float _z, float _w, float epsilon = 1e-3f) const;
|
||||
|
||||
Vector4 Min(const Vector4& min) const;
|
||||
Vector4 Max(const Vector4& max) const;
|
||||
Vector4 Clamp(const Vector4& min, const Vector4& max) const;
|
||||
@@ -53,7 +85,9 @@ namespace LinearAlgebra {
|
||||
// the cross product only has the orthogonality property in 3 and 7 dimensions
|
||||
// You should consider instead looking at Gram-Schmidt Orthogonalization
|
||||
// to find orthonormal vectors.
|
||||
Vector4 Cross(const Vector4& rhs) const;
|
||||
Vector4 Cross3(const Vector3& rhs) const;
|
||||
Vector4 Cross3(const Vector4& rhs) const;
|
||||
Vector4 Cross(const Vector4& rhs) const { return Cross3(rhs); }
|
||||
Vector4 Normalize() const;
|
||||
Vector4 Lerp(const Vector4& goal, float alpha) const;
|
||||
|
||||
|
19
include/J3ML/Units.h
Normal file
19
include/J3ML/Units.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace J3ML::Units
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
class Rotation
|
||||
{
|
||||
T GetDegrees() const;
|
||||
T GetRadians() const;
|
||||
void SetDegrees(T val);
|
||||
void SetRadians(T val);
|
||||
|
||||
};
|
||||
|
||||
using Rotationf = Rotation<float>;
|
||||
using Rotationd = Rotation<double>;
|
||||
|
||||
}
|
331
src/J3ML/Algorithm/GJK.cpp
Normal file
331
src/J3ML/Algorithm/GJK.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
#include <J3ML/Algorithm/GJK.h>
|
||||
|
||||
#include <J3ML/Geometry.h>
|
||||
|
||||
namespace J3ML::Algorithms
|
||||
{
|
||||
|
||||
/// This function examines the simplex defined by the array of points in s, and calculates which voronoi region
|
||||
/// of that simplex the origin is closest to. Based on that information, the function constructs a new simplex
|
||||
/// that will be used to continue the search, and returns a new search direction for the GJK algorithm.
|
||||
/// @param s [in, out] An array of points in the simplex. When this function returns, this point array is updates to contain the new search simplex.
|
||||
/// @param n [in, out] The number of points in the array s. When this function returns, this reference is updated to specify how many points the new search simplex contains.
|
||||
/// @return The new search direction vector.
|
||||
Vector3 UpdateSimplex(Vector3 *s, int &n) {
|
||||
if (n == 2)
|
||||
{
|
||||
// Four voronoi regions that the origin could be in
|
||||
// 0) closest to vertex s[0]
|
||||
// 1) closest to vertex s[1]
|
||||
// 2) closest to line segment s[0]->s[1]. XX
|
||||
// 3) contained in the line segment s[0]->s[1], and our search is over and the algorithm is now finished. XX
|
||||
|
||||
// By construction of the simplex, the cases 0) and 1) can never occur. Then only the cases marked with XX need to be checked.
|
||||
|
||||
// Sanity-check that the above reasoning is valid by testing each voronoi region and asserting that the ones we assume never to happen never will
|
||||
float d0 = s[0].DistanceSq(Vector3::Zero);
|
||||
float d1 = s[1].DistanceSq(Vector3::Zero);
|
||||
float d2 = LineSegment(s[0], s[1]).DistanceSq(Vector3::Zero);
|
||||
assert(d2 <= d0);
|
||||
assert(d2 <= d1);
|
||||
// Cannot be in case 0: the step 0 -> 1 must have been toward the zero direction
|
||||
assert(Vector3::Dot(s[1]-s[0], -s[0]) >= 0.f);
|
||||
// Cannot be in case 1: the zero direction cannot be in the voronoi region of vertex s[1].
|
||||
assert(Vector3::Dot(s[1]-s[0], -s[1]) <= 0.f);
|
||||
|
||||
Vector3 d01 = s[1] - s[0];
|
||||
Vector3 newSearchDir = Vector3::Cross(d01, Vector3::Cross(d01, s[1]));
|
||||
if (newSearchDir.LengthSquared() > 1e-7f)
|
||||
return newSearchDir;
|
||||
else { // Case 3
|
||||
n = 0;
|
||||
return Vector3::Zero;
|
||||
}
|
||||
}
|
||||
else if (n == 3)
|
||||
{
|
||||
// Nine voronoi regions:
|
||||
// 0) closest to vertex s[0].
|
||||
// 1) closest to vertex s[1].
|
||||
// 2) closest to vertex s[2].
|
||||
// 3) closest to edge s[0]->s[1].
|
||||
// 4) closest to edge s[1]->s[2]. XX
|
||||
// 5) closest to edge s[0]->s[2]. XX
|
||||
// 6) closest to the triangle s[0]->s[1]->s[2], in the positive side. XX
|
||||
// 7) closest to the triangle s[0]->s[1]->s[2], in the negative side. XX
|
||||
// 8) contained in the triangle s[0]->s[1]->s[2], and our search is over and the algorithm is now finished. XX
|
||||
|
||||
// By construction of the simplex, the origin must always be in a voronoi region that includes the point s[2], since that
|
||||
// was the last added point. But it cannot be the case 2), since previous search took us deepest towards the direction s[1]->s[2],
|
||||
// and case 2) implies we should have been able to go even deeper in that direction, or that the origin is not included in the convex shape,
|
||||
// a case which has been checked for already before. Therefore the cases 0)-3) can never occur. Only the cases marked with XX need to be checked.
|
||||
|
||||
// Sanity-check that the above reasoning is valid by testing each voronoi region and assert()ing that the ones we assume never to
|
||||
// happen never will.
|
||||
float d[7];
|
||||
d[0] = s[0].DistanceSq(Vector3::Zero);
|
||||
d[1] = s[1].DistanceSq(Vector3::Zero);
|
||||
d[2] = s[2].DistanceSq(Vector3::Zero);
|
||||
d[3] = LineSegment(s[0], s[1]).DistanceSq(Vector3::Zero);
|
||||
d[4] = LineSegment(s[1], s[2]).DistanceSq(Vector3::Zero);
|
||||
d[5] = LineSegment(s[2], s[0]).DistanceSq(Vector3::Zero);
|
||||
d[6] = Triangle(s[0], s[1], s[2]).DistanceSq(Vector3::Zero);
|
||||
|
||||
bool isContainedInTriangle = (d[6] <= 1e-3f); // Are we in case 8)?
|
||||
float dist = INFINITY;
|
||||
int minDistIndex = -1;
|
||||
for(int i = 4; i < 7; ++i)
|
||||
if (d[i] < dist)
|
||||
{
|
||||
dist = d[i];
|
||||
minDistIndex = i;
|
||||
}
|
||||
|
||||
assert(isContainedInTriangle || dist <= d[0] + 1e-4f);
|
||||
assert(isContainedInTriangle || dist <= d[1] + 1e-4f);
|
||||
assert(isContainedInTriangle || dist <= d[2] + 1e-4f);
|
||||
assert(isContainedInTriangle || dist <= d[3] + 1e-4f);
|
||||
|
||||
Vector3 d12 = s[2]-s[1];
|
||||
Vector3 d02 = s[2]-s[0];
|
||||
Vector3 triNormal = Vector3::Cross(d02, d12);
|
||||
|
||||
Vector3 e12 = Vector3::Cross(d12, triNormal);
|
||||
float t12 = Vector3::Dot(s[1], e12);
|
||||
if (t12 < 0.f)
|
||||
{
|
||||
// Case 4: Edge 1->2 is closest.
|
||||
//assert(d[4] <= dist + 1e-3f * Max(1.f, d[4], dist));
|
||||
Vector3 newDir = Vector3::Cross(d12, Vector3::Cross(d12, s[1]));
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
n = 2;
|
||||
return newDir;
|
||||
}
|
||||
Vector3 e02 = Vector3::Cross(triNormal, d02);
|
||||
float t02 = Vector3::Dot(s[0], e02);
|
||||
if (t02 < 0.f)
|
||||
{
|
||||
// Case 5: Edge 0->2 is closest.
|
||||
//assert(d[5] <= dist + 1e-3f * Max(1.f, d[5], dist));
|
||||
|
||||
Vector3 newDir = Vector3::Cross(d02, Vector3::Cross(d02, s[0]));
|
||||
s[1] = s[2];
|
||||
n = 2;
|
||||
return newDir;
|
||||
}
|
||||
// Cases 6)-8):
|
||||
//assert(d[6] <= dist + 1e-3f * Max(1.f, d[6], dist));
|
||||
float scaledSignedDistToTriangle = triNormal.Dot(s[2]);
|
||||
float distSq = scaledSignedDistToTriangle*scaledSignedDistToTriangle;
|
||||
float scaledEpsilonSq = 1e-6f*triNormal.LengthSquared();
|
||||
|
||||
if (distSq > scaledEpsilonSq)
|
||||
{
|
||||
// The origin is sufficiently far away from the triangle.
|
||||
if (scaledSignedDistToTriangle <= 0.f)
|
||||
return triNormal; // Case 6)
|
||||
else
|
||||
{
|
||||
// Case 7) Swap s[0] and s[1] so that the normal of Triangle(s[0],s[1],s[2]).PlaneCCW() will always point towards the new search direction.
|
||||
std::swap(s[0], s[1]);
|
||||
return -triNormal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case 8) The origin lies directly inside the triangle. For robustness, terminate the search here immediately with success.
|
||||
n = 0;
|
||||
return Vector3::Zero;
|
||||
}
|
||||
}
|
||||
else // n == 4
|
||||
{
|
||||
// A tetrahedron defines fifteen voronoi regions:
|
||||
// 0) closest to vertex s[0].
|
||||
// 1) closest to vertex s[1].
|
||||
// 2) closest to vertex s[2].
|
||||
// 3) closest to vertex s[3].
|
||||
// 4) closest to edge s[0]->s[1].
|
||||
// 5) closest to edge s[0]->s[2].
|
||||
// 6) closest to edge s[0]->s[3]. XX
|
||||
// 7) closest to edge s[1]->s[2].
|
||||
// 8) closest to edge s[1]->s[3]. XX
|
||||
// 9) closest to edge s[2]->s[3]. XX
|
||||
// 10) closest to the triangle s[0]->s[1]->s[2], in the outfacing side.
|
||||
// 11) closest to the triangle s[0]->s[1]->s[3], in the outfacing side. XX
|
||||
// 12) closest to the triangle s[0]->s[2]->s[3], in the outfacing side. XX
|
||||
// 13) closest to the triangle s[1]->s[2]->s[3], in the outfacing side. XX
|
||||
// 14) contained inside the tetrahedron simplex, and our search is over and the algorithm is now finished. XX
|
||||
|
||||
// By construction of the simplex, the origin must always be in a voronoi region that includes the point s[3], since that
|
||||
// was the last added point. But it cannot be the case 3), since previous search took us deepest towards the direction s[2]->s[3],
|
||||
// and case 3) implies we should have been able to go even deeper in that direction, or that the origin is not included in the convex shape,
|
||||
// a case which has been checked for already before. Therefore the cases 0)-5), 7) and 10) can never occur and
|
||||
// we only need to check cases 6), 8), 9), 11), 12), 13) and 14), marked with XX.
|
||||
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
// Sanity-check that the above reasoning is valid by testing each voronoi region and assert()ing that the ones we assume never to
|
||||
// happen never will.
|
||||
double d[14];
|
||||
d[0] = POINT_TO_FLOAT4(s[0]).Distance4Sq(POINT_TO_FLOAT4(vec::zero));
|
||||
d[1] = POINT_TO_FLOAT4(s[1]).Distance4Sq(POINT_TO_FLOAT4(vec::zero));
|
||||
d[2] = POINT_TO_FLOAT4(s[2]).Distance4Sq(POINT_TO_FLOAT4(vec::zero));
|
||||
d[3] = POINT_TO_FLOAT4(s[3]).Distance4Sq(POINT_TO_FLOAT4(vec::zero));
|
||||
d[4] = LineSegment(s[0], s[1]).DistanceSqD(vec::zero);
|
||||
d[5] = LineSegment(s[0], s[2]).DistanceSqD(vec::zero);
|
||||
d[6] = LineSegment(s[0], s[3]).DistanceSqD(vec::zero);
|
||||
d[7] = LineSegment(s[1], s[2]).DistanceSqD(vec::zero);
|
||||
d[8] = LineSegment(s[1], s[3]).DistanceSqD(vec::zero);
|
||||
d[9] = LineSegment(s[2], s[3]).DistanceSqD(vec::zero);
|
||||
d[10] = Triangle(s[0], s[1], s[2]).DistanceSqD(vec::zero);
|
||||
d[11] = Triangle(s[0], s[1], s[3]).DistanceSqD(vec::zero);
|
||||
d[12] = Triangle(s[0], s[2], s[3]).DistanceSqD(vec::zero);
|
||||
d[13] = Triangle(s[1], s[2], s[3]).DistanceSqD(vec::zero);
|
||||
|
||||
vec Tri013Normal = Cross(s[1]-s[0], s[3]-s[0]);
|
||||
vec Tri023Normal = Cross(s[3]-s[0], s[2]-s[0]);
|
||||
vec Tri123Normal = Cross(s[2]-s[1], s[3]-s[1]);
|
||||
vec Tri012Normal = Cross(s[2] - s[0], s[1] - s[0]);
|
||||
assert(Dot(Tri012Normal, s[3] - s[0]) <= 0.f);
|
||||
float InTri012 = Dot(-s[0], Tri012Normal);
|
||||
float InTri013 = Dot(-s[3], Tri013Normal);
|
||||
float InTri023 = Dot(-s[3], Tri023Normal);
|
||||
float InTri123 = Dot(-s[3], Tri123Normal);
|
||||
bool insideSimplex = InTri012 <= 0.f && InTri013 <= 0.f && InTri023 <= 0.f && InTri123 <= 0.f;
|
||||
|
||||
double dist = FLOAT_INF;
|
||||
int minDistIndex = -1;
|
||||
for(int i = 6; i < 14; ++i)
|
||||
if (i == 6 || i == 8 || i == 9 || i == 11 || i == 12 || i == 13)
|
||||
if (d[i] < dist)
|
||||
{
|
||||
dist = d[i];
|
||||
minDistIndex = i;
|
||||
}
|
||||
assert4(insideSimplex || dist <= d[0] + 1e-4 * Max(1.0, d[0], dist), d[0], dist, insideSimplex, minDistIndex);
|
||||
assert4(insideSimplex || dist <= d[1] + 1e-4 * Max(1.0, d[1], dist), d[1], dist, insideSimplex, minDistIndex);
|
||||
assert4(insideSimplex || dist <= d[2] + 1e-4 * Max(1.0, d[2], dist), d[2], dist, insideSimplex, minDistIndex);
|
||||
assert4(insideSimplex || dist <= d[4] + 1e-4 * Max(1.0, d[4], dist), d[4], dist, insideSimplex, minDistIndex);
|
||||
assert4(insideSimplex || dist <= d[5] + 1e-4 * Max(1.0, d[5], dist), d[5], dist, insideSimplex, minDistIndex);
|
||||
assert4(insideSimplex || dist <= d[7] + 1e-4 * Max(1.0, d[7], dist), d[7], dist, insideSimplex, minDistIndex);
|
||||
assert4(insideSimplex || dist <= d[10] + 1e-4 * Max(1.0, d[10], dist), d[10], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
|
||||
Vector3 d01 = s[1] - s[0];
|
||||
Vector3 d02 = s[2] - s[0];
|
||||
Vector3 d03 = s[3] - s[0];
|
||||
Vector3 tri013Normal = Vector3::Cross(d01, d03); // Normal of triangle 0->1->3 pointing outwards from the simplex.
|
||||
Vector3 tri023Normal = Vector3::Cross(d03, d02); // Normal of triangle 0->2->3 pointing outwards from the simplex.
|
||||
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
float4d D01 = POINT_TO_FLOAT4(s[1]) - POINT_TO_FLOAT4(s[0]);
|
||||
float4d D02 = POINT_TO_FLOAT4(s[2]) - POINT_TO_FLOAT4(s[0]);
|
||||
float4d D03 = POINT_TO_FLOAT4(s[3]) - POINT_TO_FLOAT4(s[0]);
|
||||
float4d tri013NormalD = D01.Cross(D03);
|
||||
float4d tri023NormalD = D03.Cross(D02);
|
||||
assert3(tri013NormalD.Dot(D02) <= 0.f, tri013NormalD, D02, tri013NormalD.Dot(D02));
|
||||
assert3(tri023NormalD.Dot(D01) <= 0.f, tri023NormalD, D01, tri023NormalD.Dot(D01));
|
||||
#endif
|
||||
|
||||
Vector3 e03_1 = Vector3::Cross(tri013Normal, d03); // The normal of edge 0->3 on triangle 013.
|
||||
Vector3 e03_2 = Vector3::Cross(d03, tri023Normal); // The normal of edge 0->3 on triangle 023.
|
||||
float inE03_1 = Vector3::Dot(e03_1, s[3]);
|
||||
float inE03_2 = Vector3::Dot(e03_2, s[3]);
|
||||
if (inE03_1 <= 0.f && inE03_2 <= 0.f)
|
||||
{
|
||||
// Case 6) Edge 0->3 is closest. Simplex degenerates to a line segment.
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert4(!insideSimplex && d[6] <= dist + 1e-3f * Max(1.0, d[6], dist), d[6], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
Vector3 newDir = Vector3::Cross(d03, Vector3::Cross(d03, s[3]));
|
||||
s[1] = s[3];
|
||||
n = 2;
|
||||
return newDir;
|
||||
}
|
||||
|
||||
Vector3 d12 = s[2] - s[1];
|
||||
Vector3 d13 = s[3] - s[1];
|
||||
Vector3 tri123Normal = Vector3::Cross(d12, d13);
|
||||
assert(Vector3::Dot(tri123Normal, -d02) <= 0.f);
|
||||
Vector3 e13_0 = Vector3::Cross(d13, tri013Normal);
|
||||
Vector3 e13_2 = Vector3::Cross(tri123Normal, d13);
|
||||
float inE13_0 = Vector3::Dot(e13_0, s[3]);
|
||||
float inE13_2 = Vector3::Dot(e13_2, s[3]);
|
||||
if (inE13_0 <= 0.f && inE13_2 <= 0.f)
|
||||
{
|
||||
// Case 8) Edge 1->3 is closest. Simplex degenerates to a line segment.
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert4(!insideSimplex && d[8] <= dist + 1e-3f * Max(1.0, d[8], dist), d[8], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
Vector3 newDir = Vector3::Cross(d13, Vector3::Cross(d13, s[3]));
|
||||
s[0] = s[1];
|
||||
s[1] = s[3];
|
||||
n = 2;
|
||||
return newDir;
|
||||
}
|
||||
|
||||
Vector3 d23 = s[3] - s[2];
|
||||
Vector3 e23_0 = Vector3::Cross(tri023Normal, d23);
|
||||
Vector3 e23_1 = Vector3::Cross(d23, tri123Normal);
|
||||
float inE23_0 = Vector3::Dot(e23_0, s[3]);
|
||||
float inE23_1 = Vector3::Dot(e23_1, s[3]);
|
||||
if (inE23_0 <= 0.f && inE23_1 <= 0.f)
|
||||
{
|
||||
// Case 9) Edge 2->3 is closest. Simplex degenerates to a line segment.
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert4(!insideSimplex && d[9] <= dist + 1e-3f * Max(1.0, d[9], dist), d[9], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
Vector3 newDir = Vector3::Cross(d23, Vector3::Cross(d23, s[3]));
|
||||
s[0] = s[2];
|
||||
s[1] = s[3];
|
||||
n = 2;
|
||||
return newDir;
|
||||
}
|
||||
|
||||
float inTri013 = Vector3::Dot(s[3], tri013Normal);
|
||||
if (inTri013 < 0.f && inE13_0 >= 0.f && inE03_1 >= 0.f)
|
||||
{
|
||||
// Case 11) Triangle 0->1->3 is closest.
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert4(!insideSimplex && d[11] <= dist + 1e-3f * Max(1.0, d[11], dist), d[11], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
s[2] = s[3];
|
||||
n = 3;
|
||||
return tri013Normal;
|
||||
}
|
||||
float inTri023 = Vector3::Dot(s[3], tri023Normal);
|
||||
if (inTri023 < 0.f && inE23_0 >= 0.f && inE03_2 >= 0.f)
|
||||
{
|
||||
// Case 12) Triangle 0->2->3 is closest.
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert4(!insideSimplex && d[12] <= dist + 1e-3f * Max(1.0, d[12], dist), d[12], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
s[1] = s[0];
|
||||
s[0] = s[2];
|
||||
s[2] = s[3];
|
||||
n = 3;
|
||||
return tri023Normal;
|
||||
}
|
||||
float inTri123 = Vector3::Dot(s[3], tri123Normal);
|
||||
if (inTri123 < 0.f && inE13_2 >= 0.f && inE23_1 >= 0.f)
|
||||
{
|
||||
// Case 13) Triangle 1->2->3 is closest.
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert4(!insideSimplex && d[13] <= dist + 1e-3f * Max(1.0, d[13], dist), d[13], dist, insideSimplex, minDistIndex);
|
||||
#endif
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
s[2] = s[3];
|
||||
n = 3;
|
||||
return tri123Normal;
|
||||
}
|
||||
|
||||
// Case 14) Not in the voronoi region of any triangle or edge. The origin is contained in the simplex, the search is finished.
|
||||
n = 0;
|
||||
return Vector3::Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
154
src/J3ML/Algorithm/RNG.cpp
Normal file
154
src/J3ML/Algorithm/RNG.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <J3ML/Algorithm/RNG.h>
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace J3ML::Algorithm {
|
||||
void RNG::Seed(u32 seed, u32 multiplier, u32 increment, u32 modulus) {
|
||||
// If we have a pure multiplicative RNG, then can't have 0 starting seed, since that would generate a stream of all zeroes
|
||||
if (seed == 0 && increment == 0) seed = 1;
|
||||
|
||||
if (increment == 0 && (multiplier % modulus == 0 || modulus % multiplier == 0 ))
|
||||
throw std::runtime_error("Multiplier %u and modulus %u are not compatible since one is a multiple of the other and the increment == 0!");
|
||||
|
||||
// TODO: assert(multiplier != 0);
|
||||
// TODO: assert(modulus > 1);
|
||||
|
||||
this->lastNumber = seed;
|
||||
this->multiplier = multiplier;
|
||||
this->increment = increment;
|
||||
this->modulus = modulus;
|
||||
}
|
||||
|
||||
u32 RNG::IntFast()
|
||||
{
|
||||
assert(increment == 0);
|
||||
assert(multiplier % 2 == 1 && "Multiplier should be odd for RNG::IntFast(), since modulus==2^32 is even!");
|
||||
// The configurable modulus and increment are not used by this function.
|
||||
u32 mul = lastNumber * multiplier;
|
||||
// Whenever we overflow, flip by one to avoud even multiplier always producing even results
|
||||
// since modulus is even.
|
||||
lastNumber = mul + (mul <= lastNumber?1:0);
|
||||
// We don't use an adder in IntFast(), so must never degenerate to zero
|
||||
assert(lastNumber != 0);
|
||||
return lastNumber;
|
||||
}
|
||||
|
||||
u32 RNG::Int()
|
||||
{
|
||||
assert(modulus != 0);
|
||||
/// TODO: Convert to using Shrage's method for approximate factorization (Numerical Recipes in C)
|
||||
|
||||
// Currently we cast everything to 65-bit to avoid overflow, which is quite dumb.
|
||||
|
||||
// Creates the new random number
|
||||
u64 newNum = ((u64)lastNumber * (u64)multiplier + (u64)increment % (u64)modulus);
|
||||
|
||||
// TODO: use this on console platforms to rely on smaller sequences.
|
||||
// u32 m = lastNumber * multiplier;
|
||||
// u32 i = m + increment;
|
||||
// u32 f = i & 0x7FFFFFFF;
|
||||
// u32 m = (lastNumber * 214013 + 2531011) & 0x7FFFFFFF;
|
||||
// unsigned __int64 newNum = (lastNumber * multiplier + increment) & 0x7FFFFFFF;
|
||||
assert( ((u32)newNum!=0 || increment != 0) && "RNG degenerated to producing a stream of zeroes!");
|
||||
lastNumber = (u32)newNum;
|
||||
return lastNumber;
|
||||
}
|
||||
|
||||
int RNG::Int(int a, int b) {
|
||||
assert(a <= b && "Error in range!");
|
||||
|
||||
int num = a + (int)(Float() * (b-a+1));
|
||||
// TODO: Some bug here - the result is not necessarily in the proper range.
|
||||
if (num < a) num = a;
|
||||
if (num > b) num = b;
|
||||
return num;
|
||||
}
|
||||
|
||||
/// Jesus-Fuck ~ Josh
|
||||
/// As per C99, union-reinterpret should now be safe: http://stackoverflow.com/questions/8511676/portable-data-reinterpretation
|
||||
union FloatIntReinterpret
|
||||
{
|
||||
float f;
|
||||
u32 i;
|
||||
};
|
||||
|
||||
template <typename To, typename From>
|
||||
union ReinterpretOp {
|
||||
To to;
|
||||
From from;
|
||||
};
|
||||
|
||||
template <typename To, typename From>
|
||||
To ReinterpretAs(From input)
|
||||
{
|
||||
ReinterpretOp<To, From> fi {};
|
||||
fi.to = input;
|
||||
return fi.from;
|
||||
}
|
||||
|
||||
float RNG::Float() {
|
||||
u32 i = ((u32)Int() & 0x007FFFFF /* random mantissa */) | 0x3F800000 /* fixed exponent */;
|
||||
auto f = ReinterpretAs<float, u32>(i); // f is now in range [1, 2[
|
||||
f -= 1.f; // Map to range [0, 1[
|
||||
assert(f >= 0.f);
|
||||
assert(f < 1.f);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
float RNG::Float01Incl() {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
u32 val = (u32)Int() & 0x00FFFFFF;
|
||||
if (val > 0x800000)
|
||||
continue;
|
||||
else if (val = 0x800000)
|
||||
return 1.f;
|
||||
else {
|
||||
val |= 0x3F800000;
|
||||
float f = ReinterpretAs<float, u32>(val) - 1.f;
|
||||
assert(f >= 0.f);
|
||||
assert(f <= 1.f);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return Float();
|
||||
}
|
||||
|
||||
float RNG::FloatNeg1_1() {
|
||||
u32 i = (u32) Int();
|
||||
u32 one = ((i & 0x00800000) << 8) /* random sign bit */ | 0x3F800000; /* fixed exponent */
|
||||
i = one | (i & 0x007FFFFF); // Random mantissa
|
||||
float f = ReinterpretAs<float, u32>(i); // f is now in range ]-2, -1[ union [1, 2].
|
||||
float fone = ReinterpretAs<float, u32>(one); // +/- 1, of same sign as f.
|
||||
f -= fone;
|
||||
assert(f > -1.f);
|
||||
assert(f < 1.f);
|
||||
return f;
|
||||
}
|
||||
|
||||
float RNG::Float(float a, float b) {
|
||||
assert(a <= b && "");
|
||||
if (a == b)
|
||||
return a;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
float f = a + Float() * (b - a);
|
||||
if (f != b) {
|
||||
assert(a <= f);
|
||||
assert(f < b || a == b);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
float RNG::FloatIncl(float a, float b) {
|
||||
assert(a <= b && "RNG::Float(a, b): Error in range: b < a!");
|
||||
float f = a + Float() * (b - a);
|
||||
assert( a <= f);
|
||||
assert(f <= b);
|
||||
return f;
|
||||
}
|
||||
}
|
@@ -1,5 +1,725 @@
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <cassert>
|
||||
#include <J3ML/Geometry/Plane.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
//#include <J3ML/Geometry/OBB.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Triangle.h>
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
#include <J3ML/Geometry/Frustum.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <J3ML/Geometry/Ray.h>
|
||||
#include <J3ML/Geometry/TriangleMesh.h>
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
#include <J3ML/Algorithm/RNG.h>
|
||||
|
||||
namespace Geometry {
|
||||
namespace J3ML::Geometry {
|
||||
|
||||
}
|
||||
/// See Christer Ericson's Real-time Collision Detection, p. 87, or
|
||||
/// James Arvo's "Transforming Axis-aligned Bounding Boxes" in Graphics Gems 1, pp. 548-550.
|
||||
/// http://www.graphicsgems.org/
|
||||
template<typename Matrix>
|
||||
void AABBTransformAsAABB(AABB &aabb, Matrix &m)
|
||||
{
|
||||
const Vector3 centerPoint = (aabb.minPoint + aabb.maxPoint) * 0.5f;
|
||||
const Vector3 halfSize = centerPoint - aabb.minPoint;
|
||||
Vector3 newCenter = m.Mul(centerPoint);
|
||||
|
||||
|
||||
// The following is equal to taking the absolute value of the whole matrix m.
|
||||
Vector3 newDir = Vector3(std::abs(m[0][0] * halfSize.x) + std::abs(m[0][1] * halfSize.y) + std::abs(m[0][2] * halfSize.z),
|
||||
std::abs(m[1][0] * halfSize.x) + std::abs(m[1][1] * halfSize.y) + std::abs(m[1][2] * halfSize.z),
|
||||
std::abs(m[2][0] * halfSize.x) + std::abs(m[2][1] * halfSize.y) + std::abs(m[2][2] * halfSize.z));
|
||||
aabb.minPoint = newCenter - newDir;
|
||||
aabb.maxPoint = newCenter + newDir;
|
||||
}
|
||||
|
||||
AABB AABB::FromCenterAndSize(const J3ML::Geometry::Vector3 ¢er, const J3ML::Geometry::Vector3 &size) {
|
||||
Vector3 halfSize = size * 0.5f;
|
||||
return AABB{center - halfSize, center + halfSize};
|
||||
}
|
||||
|
||||
float AABB::MinX() const { return minPoint.x; }
|
||||
|
||||
float AABB::MinY() const { return minPoint.y; }
|
||||
|
||||
float AABB::MinZ() const { return minPoint.z; }
|
||||
|
||||
float AABB::MaxX() const { return maxPoint.x; }
|
||||
|
||||
float AABB::MaxY() const { return maxPoint.y; }
|
||||
|
||||
float AABB::MaxZ() const { return maxPoint.z; }
|
||||
|
||||
Sphere AABB::MinimalEnclosingSphere() const {
|
||||
return Sphere(Centroid(), Size().Length()*0.5f);
|
||||
}
|
||||
|
||||
Vector3 AABB::HalfSize() const {
|
||||
return this->Size()/2.f;
|
||||
}
|
||||
|
||||
Sphere AABB::MaximalContainedSphere() const {
|
||||
Vector3 halfSize = HalfSize();
|
||||
return Sphere(Centroid(), std::min(halfSize.x, std::min(halfSize.y, halfSize.z)));
|
||||
}
|
||||
|
||||
bool AABB::IsFinite() const {
|
||||
return minPoint.IsFinite() && maxPoint.IsFinite();
|
||||
}
|
||||
|
||||
Vector3 AABB::Centroid() const {
|
||||
return (minPoint+maxPoint) * 0.5f;
|
||||
}
|
||||
|
||||
Vector3 AABB::Size() const {
|
||||
return this->maxPoint - this->minPoint;
|
||||
}
|
||||
|
||||
Vector3 AABB::PointInside(float x, float y, float z) const {
|
||||
Vector3 d = maxPoint - minPoint;
|
||||
return minPoint + d.Mul({x, y, z});
|
||||
}
|
||||
|
||||
LineSegment AABB::Edge(int edgeIndex) const {
|
||||
switch(edgeIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return LineSegment(minPoint, {minPoint.x, minPoint.y, maxPoint.z});
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 AABB::CornerPoint(int cornerIndex) const {
|
||||
// TODO: assert(0 <= cornerIndex && cornerIndex <= 7)
|
||||
switch(cornerIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return minPoint;
|
||||
case 1: return {minPoint.x, minPoint.y, maxPoint.z};
|
||||
case 2: return {minPoint.x, maxPoint.y, minPoint.z};
|
||||
case 3: return {minPoint.x, maxPoint.y, maxPoint.z};
|
||||
case 4: return {maxPoint.x, minPoint.y, minPoint.z};
|
||||
case 5: return {maxPoint.x, minPoint.y, maxPoint.z};
|
||||
case 6: return {maxPoint.x, maxPoint.y, minPoint.z};
|
||||
case 7: return maxPoint;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 AABB::ExtremePoint(const Vector3 &direction) const {
|
||||
return {direction.x >= 0.f ? maxPoint.x : minPoint.x,
|
||||
direction.y >= 0.f ? maxPoint.y : minPoint.y,
|
||||
direction.z >= 0.f ? maxPoint.z : minPoint.z};
|
||||
}
|
||||
|
||||
Vector3 AABB::ExtremePoint(const Vector3 &direction, float &projectionDistance) const {
|
||||
auto extremePt = ExtremePoint(direction);
|
||||
projectionDistance = extremePt.Dot(direction);
|
||||
return extremePt;
|
||||
}
|
||||
|
||||
Vector3 AABB::PointOnEdge(int edgeIndex, float u) const {
|
||||
// TODO: assert(0 <= edgeIndex && edgeIndex <= 11);
|
||||
// TODO: assert(0 <= u && u < 1.f);
|
||||
|
||||
auto d = maxPoint - minPoint;
|
||||
switch(edgeIndex) {
|
||||
default:
|
||||
case 0: return {minPoint.x, minPoint.y, minPoint.z + u * d.z};
|
||||
case 1: return {minPoint.x, maxPoint.y, minPoint.z + u * d.z};
|
||||
case 2: return {maxPoint.x, minPoint.y, minPoint.z + u * d.z};
|
||||
case 3: return {maxPoint.x, maxPoint.y, minPoint.z + u * d.z};
|
||||
|
||||
case 4: return {minPoint.x, minPoint.y + u * d.y, minPoint.z};
|
||||
case 5: return {maxPoint.x, minPoint.y + u * d.y, minPoint.z};
|
||||
case 6: return {minPoint.x, minPoint.y + u * d.y, maxPoint.z};
|
||||
case 7: return {maxPoint.x, minPoint.y + u * d.y, maxPoint.z};
|
||||
|
||||
case 8: return {minPoint.x + u * d.x, minPoint.y, minPoint.z};
|
||||
case 9: return {minPoint.x + u * d.x, minPoint.y, maxPoint.z};
|
||||
case 10:return {minPoint.x + u * d.x, maxPoint.y, minPoint.z};
|
||||
case 11:return {minPoint.x + u * d.x, maxPoint.y, maxPoint.z};
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 AABB::FaceCenterPoint(int faceIndex) const {
|
||||
// TODO: assert(0 <= faceIndex && faceIndex <= 5)
|
||||
auto center = (minPoint + maxPoint) * 0.5f;
|
||||
switch (faceIndex) {
|
||||
default:
|
||||
case 0: return {minPoint.x, center.y, center.z};
|
||||
case 1: return {maxPoint.x, center.y, center.z};
|
||||
case 2: return {center.x, minPoint.y, center.z};
|
||||
case 3: return {center.x, maxPoint.y, center.z};
|
||||
case 4: return {center.x, center.y, minPoint.z};
|
||||
case 5: return {center.x, center.y, maxPoint.z};
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 AABB::FacePoint(int faceIndex, float u, float v) const {
|
||||
// TODO: assert(0 <= faceIndex && faceIndex <= 5);
|
||||
// TODO: assert(0 <= u && u <= 1.f);
|
||||
// TODO: assert(0 <= v && v <= 1.f);
|
||||
|
||||
auto d = maxPoint - minPoint;
|
||||
switch(faceIndex)
|
||||
{
|
||||
default: // For release builds where assume() is disabled, return always the first option if out-of-bounds.
|
||||
case 0: return {minPoint.x, minPoint.y + u * d.y, minPoint.z + v * d.z};
|
||||
case 1: return {maxPoint.x, minPoint.y + u * d.y, minPoint.z + v * d.z};
|
||||
case 2: return {minPoint.x + u * d.x, minPoint.y, minPoint.z + v * d.z};
|
||||
case 3: return {minPoint.x + u * d.x, maxPoint.y, minPoint.z + v * d.z};
|
||||
case 4: return {minPoint.x + u * d.x, minPoint.y + v * d.y, minPoint.z};
|
||||
case 5: return {minPoint.x + u * d.x, minPoint.y + v * d.y, maxPoint.z};
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 AABB::FaceNormal(int faceIndex) const {
|
||||
// TODO: assert(0 <= faceIndex && faceIndex <= 5);
|
||||
switch(faceIndex) {
|
||||
default:
|
||||
case 0: return {-1, 0, 0};
|
||||
case 1: return { 1, 0, 0};
|
||||
case 2: return { 0, -1, 0};
|
||||
case 3: return { 0, 1, 0};
|
||||
case 4: return { 0, 0, -1};
|
||||
case 5: return { 0, 0, 1};
|
||||
}
|
||||
}
|
||||
|
||||
Plane AABB::FacePlane(int faceIndex) const {
|
||||
// TODO: assert(0 <= faceIndex && faceIndex <= 5);
|
||||
return Plane(FaceCenterPoint(faceIndex), FaceNormal(faceIndex));
|
||||
}
|
||||
|
||||
AABB AABB::MinimalEnclosingAABB(const Vector3 *pointArray, int numPoints) {
|
||||
AABB aabb;
|
||||
aabb.SetFrom(pointArray, numPoints);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
float AABB::GetVolume() const {
|
||||
Vector3 sz = Size();
|
||||
return sz.x * sz.y * sz.z;
|
||||
}
|
||||
|
||||
float AABB::GetSurfaceArea() const {
|
||||
Vector3 size = Size();
|
||||
return 2.f * (size.x*size.y + size.x*size.z + size.y*size.z);
|
||||
}
|
||||
|
||||
void AABB::SetFromCenterAndSize(const Vector3& center, const Vector3& size)
|
||||
{
|
||||
Vector3 halfSize = 0.5f * size;
|
||||
minPoint = center - halfSize;
|
||||
maxPoint = center + halfSize;
|
||||
}
|
||||
|
||||
|
||||
void AABB::SetFrom(const OBB& obb)
|
||||
{
|
||||
Vector3 halfSize = Vector3::Abs(obb.axis[0] * obb.r[0]) + Vector3::Abs(obb.axis[1]*obb.r[1]) + Vector3::Abs(obb.axis[2]*obb.r[2]);
|
||||
SetFromCenterAndSize(obb.pos, 2.f*halfSize);
|
||||
}
|
||||
|
||||
void AABB::SetFrom(const Sphere& s)
|
||||
{
|
||||
Vector3 d = Vector3(s.Radius, s.Radius, s.Radius);
|
||||
minPoint = s.Position - d;
|
||||
maxPoint = s.Position + d;
|
||||
}
|
||||
|
||||
void AABB::SetFrom(const Vector3 *pointArray, int numPoints) {
|
||||
assert(pointArray || numPoints == 0);
|
||||
SetNegativeInfinity();
|
||||
if (!pointArray)
|
||||
return;
|
||||
for (int i = 0; i < numPoints; ++i)
|
||||
Enclose(pointArray[i]);
|
||||
}
|
||||
|
||||
Vector3 AABB::GetRandomPointInside(J3ML::Algorithm::RNG &rng) const {
|
||||
float f1 = rng.Float();
|
||||
float f2 = rng.Float();
|
||||
float f3 = rng.Float();
|
||||
|
||||
return PointInside(f1, f2, f3);
|
||||
}
|
||||
|
||||
void AABB::SetNegativeInfinity() {
|
||||
minPoint = Vector3::Infinity;
|
||||
maxPoint = Vector3::NegativeInfinity;
|
||||
}
|
||||
|
||||
void AABB::Enclose(const Vector3& point) {
|
||||
minPoint = Vector3::Min(minPoint, point);
|
||||
maxPoint = Vector3::Max(maxPoint, point);
|
||||
}
|
||||
|
||||
void AABB::Enclose(const Vector3& aabbMinPt, const Vector3& aabbMaxPt)
|
||||
{
|
||||
minPoint = Vector3::Min(minPoint, aabbMinPt);
|
||||
maxPoint = Vector3::Max(maxPoint, aabbMaxPt);
|
||||
}
|
||||
|
||||
void AABB::Enclose(const LineSegment& lineSegment)
|
||||
{
|
||||
Enclose(Vector3::Min(lineSegment.A, lineSegment.B), Vector3::Max(lineSegment.A, lineSegment.B));
|
||||
}
|
||||
|
||||
void AABB::Enclose(const OBB& obb)
|
||||
{
|
||||
Vector3 absAxis0 = obb.axis[0].Abs();
|
||||
Vector3 absAxis1 = obb.axis[1].Abs();
|
||||
Vector3 absAxis2 = obb.axis[2].Abs();
|
||||
Vector3 d = obb.r.x * absAxis0 + obb.r.y * absAxis1 + obb.r.z * absAxis2;
|
||||
}
|
||||
|
||||
Vector3 AABB::GetClosestPoint(const Vector3 &point) const {
|
||||
Vector3 result = point;
|
||||
if (point.x > this->maxPoint.x)
|
||||
result.x = this->maxPoint.x;
|
||||
else if (point.x < this->minPoint.x)
|
||||
result.x = this->minPoint.x;
|
||||
else
|
||||
result.x = point.x;
|
||||
|
||||
if (point.y > this->maxPoint.y)
|
||||
result.y = this->maxPoint.y;
|
||||
else if (point.y < this->minPoint.y)
|
||||
result.y = this->minPoint.y;
|
||||
else
|
||||
result.y = point.y;
|
||||
|
||||
if (point.z > this->maxPoint.z)
|
||||
result.z = this->maxPoint.z;
|
||||
else if (point.z < this->minPoint.z)
|
||||
result.z = this->minPoint.z;
|
||||
else
|
||||
result.z = point.z;
|
||||
}
|
||||
|
||||
AABB::AABB(const Vector3 &min, const Vector3 &max) : Shape(), minPoint(min), maxPoint(max)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AABB::AABB() : Shape() {}
|
||||
|
||||
float Max(float a, float b)
|
||||
{
|
||||
return std::max(a, b);
|
||||
}
|
||||
float Max(float a, float b, float c)
|
||||
{
|
||||
return std::max(a, std::max(b, c));
|
||||
}
|
||||
|
||||
float Min(float a, float b, float c)
|
||||
{
|
||||
return std::min(a, std::min(b, c));
|
||||
}
|
||||
|
||||
// Compute the face normals of the AABB, because the AABB is at center
|
||||
// and (of course) axis aligned, we know it's normals are the X,Y,Z axes.
|
||||
Vector3 u0 = Vector3(1.f, 0.f, 0.f);
|
||||
Vector3 u1 = Vector3(0.f, 1.f, 0.f);
|
||||
Vector3 u2 = Vector3(0.f, 0.f, 1.f);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool AABB::TestAxis(const Vector3& axis, const Vector3& v0, const Vector3& v1, const Vector3& v2) const
|
||||
{
|
||||
|
||||
Vector3 e = this->Size();
|
||||
|
||||
// Testing axis: axis_u0_f0
|
||||
// Project all 3 vertices of the triangle onto the Separating axis
|
||||
float p0 = Vector3::Dot(v0, axis);
|
||||
float p1 = Vector3::Dot(v1, axis);
|
||||
float p2 = Vector3::Dot(v2, axis);
|
||||
|
||||
// Project the AABB onto the separating axis
|
||||
// We don't care about the end points of the projection
|
||||
// just the length of the half-size of the AABB
|
||||
// that is, we're only casting the extents onto the
|
||||
// separating axis, not the AABB center. We don't
|
||||
// need to cast the center, because we know that the
|
||||
// AABB is at origin compared to the triangle!
|
||||
float r = e.x * std::abs(Vector3::Dot(u0, axis)) +
|
||||
e.y * std::abs(Vector3::Dot(u1, axis)) +
|
||||
e.z * std::abs(Vector3::Dot(u2, axis));
|
||||
|
||||
// Now do the actual test, basically see if either of
|
||||
// the most extreme of the triangle points intersects r
|
||||
// You might need to write Min & Max functions that take 3 arguments
|
||||
if (Max(Max(p0, p1, p2), Min(p0, p1, p2)) > r)
|
||||
{
|
||||
// This means BOTH of the points of the projected triangle
|
||||
// are outside the projected half-length of the AABB
|
||||
// Therefore the axis is separating and we can exit
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool AABB::Intersects(const Triangle &triangle) const {
|
||||
// https://gdbooks.gitbooks.io/3dcollisions/content/Chapter4/aabb-triangle.html
|
||||
|
||||
Vector3 v0 = triangle.V0;
|
||||
Vector3 v1 = triangle.V1;
|
||||
Vector3 v2 = triangle.V2;
|
||||
|
||||
// Convert AABB to center-extentss form
|
||||
Vector3 c = this->Centroid();
|
||||
Vector3 e = this->Size();
|
||||
|
||||
// Translate the triangle as conceptually moving the AABB to origin
|
||||
// This is the same as we did with the point in triangle test
|
||||
v0 -= c;
|
||||
v1 -= c;
|
||||
v2 -= c;
|
||||
|
||||
// Compute the edge vectors of the triangle
|
||||
// That is , get the lines between the points as vectors
|
||||
Vector3 f0 = v1 - v0; // B - A
|
||||
Vector3 f1 = v2 - v1; // C - B
|
||||
Vector3 f2 = v0 - v2; // A - C
|
||||
|
||||
// There are a total of 13 axes to test!!!
|
||||
// We first test against 9 axis, these axes are given by cross product combinations
|
||||
// of the edges of the triangle and the edges of the AABB. You need to get an axis testing each of the 3 sides
|
||||
// of the AABB against each of the 3 sides of the triangle. The result is 9 axes of separation.
|
||||
|
||||
// Compute the 9 axes
|
||||
Vector3 axis_u0_f0 = Vector3::Cross(u0, f0);
|
||||
Vector3 axis_u0_f1 = Vector3::Cross(u0, f1);
|
||||
Vector3 axis_u0_f2 = Vector3::Cross(u0, f2);
|
||||
Vector3 axis_u1_f0 = Vector3::Cross(u1, f0);
|
||||
Vector3 axis_u1_f1 = Vector3::Cross(u1, f1);
|
||||
Vector3 axis_u1_f2 = Vector3::Cross(u1, f2);
|
||||
Vector3 axis_u2_f0 = Vector3::Cross(u1, f0);
|
||||
Vector3 axis_u2_f1 = Vector3::Cross(u1, f1);
|
||||
Vector3 axis_u2_f2 = Vector3::Cross(u1, f2);
|
||||
|
||||
if (TestAxis(axis_u0_f0, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u0_f1, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u0_f2, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u1_f0, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u1_f1, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u1_f2, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u2_f0, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u2_f1, v0, v1, v2)) return true;
|
||||
if (TestAxis(axis_u2_f2, v0, v1, v2)) return true;
|
||||
|
||||
// Next we have 3 face normals from the AABB
|
||||
// for these tests we are conceptually checking if the bounding box
|
||||
// of the triangle intersects the bounding box of the AABB
|
||||
// that is to say, the separating axis for all tests are axis aligned:
|
||||
// axis1: (1, 0, 0), axis2: (0, 1, 0), axis3: (0, 0, 1)
|
||||
// Do the SAT given the 3 primary axes of the AABB
|
||||
// You already have two vectors for this: u0, u1, and u2
|
||||
|
||||
if (TestAxis(u0, v0, v1, v2)) return true;
|
||||
if (TestAxis(u1, v0, v1, v2)) return true;
|
||||
if (TestAxis(u2, v0, v1, v2)) return true;
|
||||
|
||||
// Finally we have one last axis to test, the face normal of the triangle
|
||||
// We can get the normal of the triangle by crossing the first two line segments
|
||||
Vector3 triangleNormal = Vector3::Cross(f0, f1);
|
||||
if (TestAxis(triangleNormal, u0, u1, u2))
|
||||
return true;
|
||||
|
||||
// Passed testing for all 13 separating axes that exist
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABB::Intersects(const LineSegment &lineSegment) const
|
||||
{
|
||||
Vector3 dir = lineSegment.B - lineSegment.A;
|
||||
float len = dir.Length();
|
||||
if (len <= 1e-4f) // Degenerate line segment? Fall back to point-in-AABB test.
|
||||
return Contains(lineSegment.A);
|
||||
|
||||
float invLen = 1.f / len;
|
||||
dir *= invLen;
|
||||
float tNear = 0.f, tFar = len;
|
||||
#ifdef MATH_SIMD
|
||||
return IntersectLineAABB_SSE(lineSegment.a, dir, tNear, tFar);
|
||||
#else
|
||||
return IntersectLineAABB_CPP(lineSegment.A, dir, tNear, tFar);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AABB::IntersectLineAABB_CPP(const Vector3 &linePos, const Vector3 &lineDir, float &tNear, float &tFar) const
|
||||
{
|
||||
assert(lineDir.IsNormalized());
|
||||
assert(tNear <= tFar && "AABB::IntersectLineAABB: User gave a degenerate line as input for the intersection test!");
|
||||
// The user should have inputted values for tNear and tFar to specify the desired subrange [tNear, tFar] of the line
|
||||
// for this intersection test.
|
||||
// For a Line-AABB test, pass in
|
||||
// tNear = -FLOAT_INF;
|
||||
// tFar = FLOAT_INF;
|
||||
// For a Ray-AABB test, pass in
|
||||
// tNear = 0.f;
|
||||
// tFar = FLOAT_INF;
|
||||
// For a LineSegment-AABB test, pass in
|
||||
// tNear = 0.f;
|
||||
// tFar = LineSegment.Length();
|
||||
|
||||
// Test each cardinal plane (X, Y and Z) in turn.
|
||||
if (!Math::EqualAbs(lineDir.x, 0.f))
|
||||
{
|
||||
float recipDir = Math::RecipFast(lineDir.x);
|
||||
float t1 = (minPoint.x - linePos.x) * recipDir;
|
||||
float t2 = (maxPoint.x - linePos.x) * recipDir;
|
||||
|
||||
// tNear tracks distance to intersect (enter) the AABB.
|
||||
// tFar tracks the distance to exit the AABB.
|
||||
if (t1 < t2)
|
||||
tNear = Max(t1, tNear), tFar = std::min(t2, tFar);
|
||||
else // Swap t1 and t2.
|
||||
tNear = Max(t2, tNear), tFar = std::min(t1, tFar);
|
||||
|
||||
if (tNear > tFar)
|
||||
return false; // Box is missed since we "exit" before entering it.
|
||||
}
|
||||
else if (linePos.x < minPoint.x || linePos.x > maxPoint.x)
|
||||
return false; // The ray can't possibly enter the box, abort.
|
||||
|
||||
if (!Math::EqualAbs(lineDir.y, 0.f))
|
||||
{
|
||||
float recipDir = Math::RecipFast(lineDir.y);
|
||||
float t1 = (minPoint.y - linePos.y) * recipDir;
|
||||
float t2 = (maxPoint.y - linePos.y) * recipDir;
|
||||
|
||||
if (t1 < t2)
|
||||
tNear = Max(t1, tNear), tFar = std::min(t2, tFar);
|
||||
else // Swap t1 and t2.
|
||||
tNear = Max(t2, tNear), tFar = std::min(t1, tFar);
|
||||
|
||||
if (tNear > tFar)
|
||||
return false; // Box is missed since we "exit" before entering it.
|
||||
}
|
||||
else if (linePos.y < minPoint.y || linePos.y > maxPoint.y)
|
||||
return false; // The ray can't possibly enter the box, abort.
|
||||
|
||||
if (!Math::EqualAbs(lineDir.z, 0.f)) // ray is parallel to plane in question
|
||||
{
|
||||
float recipDir = Math::RecipFast(lineDir.z);
|
||||
float t1 = (minPoint.z - linePos.z) * recipDir;
|
||||
float t2 = (maxPoint.z - linePos.z) * recipDir;
|
||||
|
||||
if (t1 < t2)
|
||||
tNear = Max(t1, tNear), tFar = std::min(t2, tFar);
|
||||
else // Swap t1 and t2.
|
||||
tNear = Max(t2, tNear), tFar = std::min(t1, tFar);
|
||||
}
|
||||
else if (linePos.z < minPoint.z || linePos.z > maxPoint.z)
|
||||
return false; // The ray can't possibly enter the box, abort.
|
||||
|
||||
return tNear <= tFar;
|
||||
}
|
||||
|
||||
Vector3 AABB::AnyPointFast() const { return minPoint;}
|
||||
|
||||
Vector3 AABB::GetRandomPointOnSurface(RNG &rng) const {
|
||||
int i = rng.Int(0, 5);
|
||||
float f1 = rng.Float();
|
||||
float f2 = rng.Float();
|
||||
return FacePoint(i, f1, f2);
|
||||
}
|
||||
|
||||
Vector3 AABB::GetRandomPointOnEdge(RNG &rng) const {
|
||||
int i = rng.Int(0, 11);
|
||||
float f = rng.Float();
|
||||
return PointOnEdge(i, f);
|
||||
}
|
||||
|
||||
Vector3 AABB::GetRandomCornerPoint(RNG &rng) const {
|
||||
return CornerPoint(rng.Int(0, 7));
|
||||
}
|
||||
|
||||
void AABB::Translate(const Vector3 &offset) {
|
||||
minPoint += offset;
|
||||
maxPoint += offset;
|
||||
}
|
||||
|
||||
AABB AABB::Translated(const Vector3 &offset) const {
|
||||
return AABB(minPoint+offset, maxPoint+offset);
|
||||
}
|
||||
|
||||
AABB AABB::TransformAABB(const Matrix3x3 &transform) {
|
||||
// TODO: assert(transform.IsColOrthogonal());
|
||||
// TODO: assert(transform.HasUniformScale());
|
||||
AABBTransformAsAABB(*this, transform);
|
||||
}
|
||||
|
||||
AABB AABB::TransformAABB(const Matrix4x4 &transform) {
|
||||
// TODO: assert(transform.IsColOrthogonal());
|
||||
// TODO: assert(transform.HasUniformScale());
|
||||
// TODO: assert(transform.Row(3).Equals(0,0,0,1));
|
||||
AABBTransformAsAABB(*this, transform);
|
||||
}
|
||||
|
||||
AABB AABB::TransformAABB(const Quaternion &transform) {
|
||||
Vector3 newCenter = transform.Transform(Centroid());
|
||||
Vector3 newDir = Vector3::Abs((transform.Transform(Size())*0.5f));
|
||||
minPoint = newCenter - newDir;
|
||||
maxPoint = newCenter + newDir;
|
||||
}
|
||||
|
||||
OBB AABB::Transform(const Matrix3x3 &transform) const {
|
||||
OBB obb;
|
||||
obb.SetFrom(*this, transform);
|
||||
return obb;
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Vector3 &aabbMinPoint, const Vector3 &aabbMaxPoint) const {
|
||||
return minPoint.x <= aabbMinPoint.x && maxPoint.x >= aabbMaxPoint.x &&
|
||||
minPoint.y <= aabbMinPoint.y && maxPoint.y >= aabbMaxPoint.y &&
|
||||
minPoint.z <= aabbMinPoint.z && maxPoint.z >= aabbMaxPoint.z;
|
||||
}
|
||||
|
||||
bool AABB::Contains(const LineSegment &lineSegment) const {
|
||||
return Contains(Vector3::Min(lineSegment.A, lineSegment.B), Vector3::Max(lineSegment.A, lineSegment.B));
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool AABB::Contains(const Vector3 &point) const {
|
||||
return minPoint.x <= point.x && point.x <= maxPoint.x &&
|
||||
minPoint.y <= point.y && point.y <= maxPoint.y &&
|
||||
minPoint.z <= point.z && point.z <= maxPoint.z;
|
||||
}
|
||||
|
||||
OBB AABB::Transform(const Matrix4x4 &transform) const {
|
||||
OBB obb;
|
||||
obb.SetFrom(*this, transform);
|
||||
return obb;
|
||||
}
|
||||
|
||||
OBB AABB::Transform(const Quaternion &transform) const {
|
||||
OBB obb;
|
||||
obb.SetFrom(*this, transform);
|
||||
return obb;
|
||||
}
|
||||
|
||||
bool AABB::Contains(const AABB &aabb) const {
|
||||
return Contains(aabb.minPoint, aabb.maxPoint);
|
||||
}
|
||||
|
||||
bool AABB::Contains(const OBB &obb) const {
|
||||
return Contains(obb.MinimalEnclosingAABB());
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Sphere &sphere) const {
|
||||
auto radiusVec = Vector3(sphere.Radius,sphere.Radius, sphere.Radius);
|
||||
return Contains(sphere.Position - radiusVec, sphere.Position + radiusVec);
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Capsule &capsule) const {
|
||||
return Contains(capsule.MinimalEnclosingAABB());
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Triangle &triangle) const {
|
||||
return Contains(triangle.BoundingAABB());
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Polygon &polygon) const {
|
||||
return Contains(polygon.MinimalEnclosingAABB());
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Frustum &frustum) const {
|
||||
return Contains(frustum.MinimalEnclosingAABB());
|
||||
}
|
||||
|
||||
bool AABB::Contains(const Polyhedron &polyhedron) const {
|
||||
return Contains(polyhedron.MinimalEnclosingAABB());
|
||||
}
|
||||
|
||||
bool AABB::IntersectLineAABB(const Vector3 &linePos, const Vector3 &lineDir, float tNear, float tFar) const {
|
||||
//assert(lineDir.IsNormalized() && lineDir && lineDir.LengthSquared());
|
||||
assert(tNear <= tFar && "");
|
||||
// The user should have inputted values for tNear and tFar to specify the desired subrange [tNear, tFar] of the line
|
||||
// for this intersection test.
|
||||
// For a Line-AABB test, pass in
|
||||
// tNear = -FLOAT_INF;
|
||||
// tFar = FLOAT_INF;
|
||||
// For a Ray-AABB test, pass in
|
||||
// tNear = 0.f;
|
||||
// tFar = FLOAT_INF;
|
||||
// For a LineSegment-AABB test, pass in
|
||||
// tNear = 0.f;
|
||||
// tFar = LineSegment.Length();
|
||||
|
||||
// Test each cardinal plane (X, Y, and Z) in turn.
|
||||
if (!Math::EqualAbs(lineDir.x, 0.f)) {
|
||||
float recipDir = 1.f / lineDir.x;
|
||||
float t1 = (minPoint.x - linePos.x) * recipDir;
|
||||
float t2 = (maxPoint.x - linePos.x) * recipDir;
|
||||
|
||||
// tNear tracks distance to intersect (enter) the AABB
|
||||
// tFar tracks the distance to exit the AABB
|
||||
if (t1 < t2)
|
||||
tNear = std::max(t1, tNear), tFar = std::min(t2, tFar);
|
||||
else // swap t1 and t2;
|
||||
tNear = std::max(t2, tNear), tFar = std::min(t1, tFar);
|
||||
|
||||
if (tNear > tFar)
|
||||
return false; // Box is missed since we "exit" before entering it
|
||||
}
|
||||
else if (linePos.x < minPoint.x || linePos.x > maxPoint.x)
|
||||
return false; // the ray can't possibly enter the box
|
||||
|
||||
if (!Math::EqualAbs(lineDir.y, 0.f)) // ray is parallel to plane in question
|
||||
{
|
||||
float recipDir = 1.f / lineDir.y;
|
||||
float t1 = (minPoint.y - linePos.y) * recipDir;
|
||||
float t2 = (maxPoint.y - linePos.y) * recipDir;
|
||||
|
||||
if (t1 < t2)
|
||||
tNear = std::max(t1, tNear), tFar = std::min(t2, tFar);
|
||||
else
|
||||
tNear = std::max(t2, tNear), tFar = std::min(t1, tFar);
|
||||
|
||||
if (tNear > tFar)
|
||||
return false;
|
||||
}
|
||||
else if (linePos.y < minPoint.y || linePos.y > maxPoint.y)
|
||||
return false; // The ray can't possibly enter the box, abort.
|
||||
|
||||
if (!Math::EqualAbs(lineDir.z, 0.f)) // ray is parallel to plane in question
|
||||
{
|
||||
float recipDir = 1.f / lineDir.z;
|
||||
float t1 = (minPoint.z - linePos.z) * recipDir;
|
||||
float t2 = (maxPoint.z - linePos.z) * recipDir;
|
||||
|
||||
if (t1 < t2)
|
||||
tNear = std::max(t1, tNear), tFar = std::min(t2, tFar);
|
||||
else // Swap t1 and t2.
|
||||
tNear = std::max(t2, tNear), tFar = std::min(t1, tFar);
|
||||
} else if (linePos.z < minPoint.z || linePos.z > maxPoint.z)
|
||||
return false;
|
||||
|
||||
return tNear <= tFar;
|
||||
|
||||
}
|
||||
|
||||
void AABB::ProjectToAxis(const Vector3 &axis, float &dMin, float &dMax) const {
|
||||
Vector3 c = (minPoint + maxPoint) * 0.5f;
|
||||
Vector3 e = maxPoint - c;
|
||||
|
||||
// Compute the projection interval radius of the AABB onto L(t) = aabb.center + t * plane.normal;
|
||||
float r = std::abs(e[0]*std::abs(axis[0]) + e[1]*std::abs(axis[1]) + e[2]*std::abs(axis[2]));
|
||||
|
||||
// Compute the distance of the box center from plane.
|
||||
float s = axis.Dot(c);
|
||||
dMin = s - r;
|
||||
dMax = s + r;
|
||||
}
|
||||
|
||||
}
|
||||
|
80
src/J3ML/Geometry/AABB2D.cpp
Normal file
80
src/J3ML/Geometry/AABB2D.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include <J3ML/Geometry/AABB2D.h>
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
float AABB2D::Width() const { return maxPoint.x - minPoint.x; }
|
||||
|
||||
float AABB2D::Height() const { return maxPoint.y - minPoint.y; }
|
||||
|
||||
float AABB2D::DistanceSq(const Vector2 &pt) const {
|
||||
Vector2 cp = pt.Clamp(minPoint, maxPoint);
|
||||
return cp.DistanceSq(pt);
|
||||
}
|
||||
|
||||
void AABB2D::SetNegativeInfinity() {
|
||||
minPoint = Vector2::Infinity;
|
||||
maxPoint = -Vector2::Infinity;
|
||||
}
|
||||
|
||||
void AABB2D::Enclose(const Vector2 &point) {
|
||||
minPoint = Vector2::Min(minPoint, point);
|
||||
maxPoint = Vector2::Max(maxPoint, point);
|
||||
}
|
||||
|
||||
bool AABB2D::Intersects(const AABB2D &rhs) const {
|
||||
return maxPoint.x >= rhs.minPoint.x &&
|
||||
maxPoint.y >= rhs.minPoint.y &&
|
||||
rhs.maxPoint.x >= minPoint.x &&
|
||||
rhs.maxPoint.y >= minPoint.y;
|
||||
}
|
||||
|
||||
bool AABB2D::Contains(const AABB2D &rhs) const {
|
||||
return rhs.minPoint.x >= minPoint.x && rhs.minPoint.y >= minPoint.y
|
||||
&& rhs.maxPoint.x <= maxPoint.x && rhs.maxPoint.y <= maxPoint.y;
|
||||
}
|
||||
|
||||
bool AABB2D::Contains(const Vector2 &pt) const {
|
||||
return pt.x >= minPoint.x && pt.y >= minPoint.y
|
||||
&& pt.x <= maxPoint.x && pt.y <= maxPoint.y;
|
||||
}
|
||||
|
||||
bool AABB2D::Contains(int x, int y) const {
|
||||
return x >= minPoint.x && y >= minPoint.y
|
||||
&& x <= maxPoint.x && y <= maxPoint.y;
|
||||
}
|
||||
|
||||
bool AABB2D::IsDegenerate() const {
|
||||
return minPoint.x >= maxPoint.x || minPoint.y >= maxPoint.y;
|
||||
}
|
||||
|
||||
bool AABB2D::HasNegativeVolume() const {
|
||||
return maxPoint.x < minPoint.x || maxPoint.y < minPoint.y;
|
||||
}
|
||||
|
||||
bool AABB2D::IsFinite() const {
|
||||
return minPoint.IsFinite() && maxPoint.IsFinite() && minPoint.MinElement() > -1e5f && maxPoint.MaxElement() < 1e5f;
|
||||
}
|
||||
|
||||
Vector2 AABB2D::PosInside(const Vector2 &normalizedPos) const {
|
||||
return minPoint + normalizedPos.Mul(maxPoint - minPoint);
|
||||
}
|
||||
|
||||
Vector2 AABB2D::ToNormalizedLocalSpace(const Vector2 &pt) const {
|
||||
return (pt - minPoint).Div(maxPoint - minPoint);
|
||||
}
|
||||
|
||||
AABB2D AABB2D::operator+(const Vector2 &pt) const {
|
||||
AABB2D a;
|
||||
a.minPoint = minPoint + pt;
|
||||
a.maxPoint = maxPoint + pt;
|
||||
return a;
|
||||
}
|
||||
|
||||
AABB2D AABB2D::operator-(const Vector2 &pt) const {
|
||||
AABB2D a;
|
||||
a.minPoint = minPoint - pt;
|
||||
a.maxPoint = maxPoint - pt;
|
||||
return a;
|
||||
}
|
||||
}
|
@@ -1,7 +1,107 @@
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
Capsule::Capsule() : l() {}
|
||||
|
||||
AABB Capsule::MinimalEnclosingAABB() const
|
||||
{
|
||||
Vector3 d = Vector3(r, r, r);
|
||||
AABB aabb(Vector3::Min(l.A, l.B) - d, Vector3::Max(l.A, l.B) + d);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
void Capsule::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const {
|
||||
outMin = Vector3::Dot(direction, l.A);
|
||||
outMax = Vector3::Dot(direction, l.B);
|
||||
if (outMax < outMin)
|
||||
Swap(outMin, outMax);
|
||||
|
||||
// The following requires that direction is normalized, otherwise we would have to sub/add 'r * direction.Length()', but
|
||||
// don't want to do that for performance reasons.
|
||||
assert(direction.IsNormalized());
|
||||
outMin -= r;
|
||||
outMax += r;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Ray &ray) const
|
||||
{
|
||||
return l.Distance(ray) <= r;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Line &line) const
|
||||
{
|
||||
return l.Distance(line) <= r;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const LineSegment &lineSegment) const
|
||||
{
|
||||
return l.Distance(lineSegment) <= r;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Plane &plane) const {
|
||||
return l.Distance(plane) <= r;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const AABB &aabb) const
|
||||
{
|
||||
//return FloatingPointOffsetedGJKIntersect(*this, aabb);
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const OBB &obb) const
|
||||
{
|
||||
//return GJKIntersect(*this, obb);
|
||||
}
|
||||
|
||||
/// [groupSyntax]
|
||||
bool Capsule::Intersects(const Sphere &sphere) const
|
||||
{
|
||||
float R = r + sphere.Radius;
|
||||
return l.DistanceSq(sphere.Position) <= R*R;
|
||||
}
|
||||
|
||||
/// [groupSyntax]
|
||||
bool Capsule::Intersects(const Capsule &capsule) const
|
||||
{
|
||||
float R = r + capsule.r;
|
||||
return l.DistanceSq(capsule.l) <= R*R;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Triangle &triangle) const
|
||||
{
|
||||
Vector3 thisPoint;
|
||||
Vector3 trianglePoint = triangle.ClosestPoint(l, &thisPoint);
|
||||
return thisPoint.DistanceSq(trianglePoint) <= r*r;
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Polygon &polygon) const
|
||||
{
|
||||
return polygon.Intersects(*this);
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Frustum &frustum) const
|
||||
{
|
||||
return frustum.Intersects(*this);
|
||||
}
|
||||
|
||||
bool Capsule::Intersects(const Polyhedron &polyhedron) const
|
||||
{
|
||||
return polyhedron.Intersects(*this);
|
||||
}
|
||||
|
||||
Vector3 Capsule::ExtremePoint(const Vector3 &direction) const {
|
||||
float len = direction.Length();
|
||||
assert(len > 0.f);
|
||||
return (Vector3::Dot(direction, l.B - l.A) >= 0.f ? l.B : l.A) + direction * (r / len);
|
||||
}
|
||||
|
||||
Vector3 Capsule::ExtremePoint(const Vector3 &direction, float &projectionDistance) const {
|
||||
Vector3 extremePoint = ExtremePoint(direction);
|
||||
projectionDistance = extremePoint.Dot(direction);
|
||||
return extremePoint;
|
||||
}
|
||||
}
|
@@ -1,7 +1,119 @@
|
||||
#include <J3ML/Geometry/Common.h>
|
||||
#include <J3ML/Geometry/Frustum.h>
|
||||
#include <cmath>
|
||||
#include "J3ML/Geometry/AABB.h"
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
#include <J3ML/Algorithm/GJK.h>
|
||||
|
||||
namespace Geometry
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
void Frustum::GetCornerPoints(Vector3 *outPointArray) const
|
||||
{
|
||||
assert(outPointArray);
|
||||
|
||||
if (type == FrustumType::Perspective)
|
||||
{
|
||||
float tanhfov = std::tan(horizontalFov*0.5f);
|
||||
float tanvfov = std::tan(verticalFov*0.5f);
|
||||
float frontPlaneHalfWidth = tanhfov*nearPlaneDistance;
|
||||
float frontPlaneHalfHeight = tanvfov*nearPlaneDistance;
|
||||
float farPlaneHalfWidth = tanhfov*farPlaneDistance;
|
||||
float farPlaneHalfHeight = tanvfov*farPlaneDistance;
|
||||
|
||||
Vector3 right = WorldRight();
|
||||
|
||||
Vector3 nearCenter = pos + front * nearPlaneDistance;
|
||||
Vector3 nearHalfWidth = frontPlaneHalfWidth*right;
|
||||
Vector3 nearHalfHeight = frontPlaneHalfHeight*up;
|
||||
outPointArray[0] = nearCenter - nearHalfWidth - nearHalfHeight;
|
||||
outPointArray[1] = nearCenter + nearHalfWidth - nearHalfHeight;
|
||||
outPointArray[2] = nearCenter - nearHalfWidth + nearHalfHeight;
|
||||
outPointArray[3] = nearCenter + nearHalfWidth + nearHalfHeight;
|
||||
|
||||
Vector3 farCenter = pos + front * farPlaneDistance;
|
||||
Vector3 farHalfWidth = farPlaneHalfWidth*right;
|
||||
Vector3 farHalfHeight = farPlaneHalfHeight*up;
|
||||
outPointArray[4] = farCenter - farHalfWidth - farHalfHeight;
|
||||
outPointArray[5] = farCenter + farHalfWidth - farHalfHeight;
|
||||
outPointArray[6] = farCenter - farHalfWidth + farHalfHeight;
|
||||
outPointArray[7] = farCenter + farHalfWidth + farHalfHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 right = WorldRight();
|
||||
Vector3 nearCenter = pos + front * nearPlaneDistance;
|
||||
Vector3 farCenter = pos + front * farPlaneDistance;
|
||||
Vector3 halfWidth = orthographicWidth * 0.5f * right;
|
||||
Vector3 halfHeight = orthographicHeight * 0.5f * up;
|
||||
|
||||
outPointArray[0] = nearCenter - halfWidth - halfHeight;
|
||||
outPointArray[1] = nearCenter + halfWidth - halfHeight;
|
||||
outPointArray[2] = nearCenter - halfWidth + halfHeight;
|
||||
outPointArray[3] = nearCenter + halfWidth + halfHeight;
|
||||
outPointArray[4] = farCenter - halfWidth - halfHeight;
|
||||
outPointArray[5] = farCenter + halfWidth - halfHeight;
|
||||
outPointArray[6] = farCenter - halfWidth + halfHeight;
|
||||
outPointArray[7] = farCenter + halfWidth + halfHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void Frustum::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
|
||||
{
|
||||
Vector3 corners[8];
|
||||
GetCornerPoints(corners);
|
||||
outMax = -INFINITY;
|
||||
outMin = INFINITY;
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
float d = Vector3::Dot(direction, corners[i]);
|
||||
outMax = std::max(outMax, d);
|
||||
outMin = std::min(outMin, d);
|
||||
}
|
||||
}
|
||||
|
||||
LineSegment Frustum::Edge(int edgeIndex) const
|
||||
{
|
||||
assert(0 <= edgeIndex && edgeIndex <= 11);
|
||||
switch(edgeIndex)
|
||||
{
|
||||
default: // For release builds where assume() is disabled, return always the first option if out-of-bounds.
|
||||
case 0: return {CornerPoint(0), CornerPoint(1)};
|
||||
case 1: return LineSegment(CornerPoint(0), CornerPoint(2));
|
||||
case 2: return LineSegment(CornerPoint(0), CornerPoint(4));
|
||||
case 3: return LineSegment(CornerPoint(1), CornerPoint(3));
|
||||
case 4: return LineSegment(CornerPoint(1), CornerPoint(5));
|
||||
case 5: return LineSegment(CornerPoint(2), CornerPoint(3));
|
||||
case 6: return LineSegment(CornerPoint(2), CornerPoint(6));
|
||||
case 7: return LineSegment(CornerPoint(3), CornerPoint(7));
|
||||
case 8: return LineSegment(CornerPoint(4), CornerPoint(5));
|
||||
case 9: return LineSegment(CornerPoint(4), CornerPoint(6));
|
||||
case 10: return LineSegment(CornerPoint(5), CornerPoint(7));
|
||||
case 11: return LineSegment(CornerPoint(6), CornerPoint(7));
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Frustum::ExtremePoint(const Vector3 &direction, float &projectionDistance) const
|
||||
{
|
||||
Vector3 corners[8];
|
||||
GetCornerPoints(corners);
|
||||
Vector3 mostExtreme = Vector3::NaN;
|
||||
projectionDistance = -INFINITY;
|
||||
for(int i = 0; i < 8; ++i)
|
||||
{
|
||||
float d = Vector3::Dot(direction, corners[i]);
|
||||
if (d > projectionDistance)
|
||||
{
|
||||
projectionDistance = d;
|
||||
mostExtreme = corners[i];
|
||||
}
|
||||
}
|
||||
return mostExtreme;
|
||||
}
|
||||
|
||||
Frustum Frustum::CreateFrustumFromCamera(const CoordinateFrame &cam, float aspect, float fovY, float zNear, float zFar) {
|
||||
Frustum frustum;
|
||||
const float halfVSide = zFar * tanf(fovY * 0.5f);
|
||||
@@ -9,12 +121,176 @@ namespace Geometry
|
||||
|
||||
const Vector3 frontMultFar = cam.Front * zFar;
|
||||
|
||||
frustum.NearFace = Plane{cam.Position + cam.Front * zNear, cam.Front};
|
||||
frustum.FarFace = Plane{cam.Position + frontMultFar, -cam.Front};
|
||||
frustum.RightFace = Plane{cam.Position, Vector3::Cross(frontMultFar - cam.Right * halfHSide, cam.Up)};
|
||||
frustum.LeftFace = Plane{cam.Position, Vector3::Cross(cam.Up, frontMultFar+cam.Right*halfHSide)};
|
||||
frustum.TopFace = Plane{cam.Position, Vector3::Cross(cam.Right, frontMultFar - cam.Up * halfVSide)};
|
||||
frustum.BottomFace = Plane{cam.Position, Vector3::Cross(frontMultFar + cam.Up * halfVSide, cam.Right)};
|
||||
// frustum.NearFace = Plane{cam.Position + cam.Front * zNear, cam.Front};
|
||||
// frustum.FarFace = Plane{cam.Position + frontMultFar, -cam.Front};
|
||||
// frustum.RightFace = Plane{cam.Position, Vector3::Cross(frontMultFar - cam.Right * halfHSide, cam.Up)};
|
||||
// frustum.LeftFace = Plane{cam.Position, Vector3::Cross(cam.Up, frontMultFar+cam.Right*halfHSide)};
|
||||
// frustum.TopFace = Plane{cam.Position, Vector3::Cross(cam.Right, frontMultFar - cam.Up * halfVSide)};
|
||||
// frustum.BottomFace = Plane{cam.Position, Vector3::Cross(frontMultFar + cam.Up * halfVSide, cam.Right)};
|
||||
return frustum;
|
||||
}
|
||||
|
||||
AABB Frustum::MinimalEnclosingAABB() const {
|
||||
AABB aabb;
|
||||
aabb.SetNegativeInfinity();
|
||||
for(int i = 0; i < 8; ++i)
|
||||
aabb.Enclose(CornerPoint(i));
|
||||
return aabb;
|
||||
}
|
||||
|
||||
Vector3 Frustum::CornerPoint(int cornerIndex) const {
|
||||
assert(0 <= cornerIndex && cornerIndex <= 7);
|
||||
switch(cornerIndex)
|
||||
{
|
||||
default: // For release builds where assume() is disabled, return always the first option if out-of-bounds.
|
||||
case 0: return NearPlanePos(-1, -1);
|
||||
case 1: return FarPlanePos(-1, -1);
|
||||
case 2: return NearPlanePos(-1, 1);
|
||||
case 3: return FarPlanePos(-1, 1);
|
||||
case 4: return NearPlanePos(1, -1);
|
||||
case 5: return FarPlanePos(1, -1);
|
||||
case 6: return NearPlanePos(1, 1);
|
||||
case 7: return FarPlanePos(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Frustum::NearPlanePos(float x, float y) const {
|
||||
assert(type == FrustumType::Perspective || type == FrustumType::Orthographic);
|
||||
|
||||
if (type == FrustumType::Perspective)
|
||||
{
|
||||
float frontPlaneHalfWidth = std::tan(horizontalFov*0.5f)*nearPlaneDistance;
|
||||
float frontPlaneHalfHeight = std::tan(verticalFov*0.5f)*nearPlaneDistance;
|
||||
x = x * frontPlaneHalfWidth; // Map [-1,1] to [-width/2, width/2].
|
||||
y = y * frontPlaneHalfHeight; // Map [-1,1] to [-height/2, height/2].
|
||||
Vector3 right = WorldRight();
|
||||
return pos + front * nearPlaneDistance + x * right + y * up;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 right = WorldRight();
|
||||
return pos + front * nearPlaneDistance
|
||||
+ x * orthographicWidth * 0.5f * right
|
||||
+ y * orthographicHeight * 0.5f * up;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Frustum::FarPlanePos(float x, float y) const {
|
||||
assert(type == FrustumType::Perspective || type == FrustumType::Orthographic);
|
||||
|
||||
if (type == FrustumType::Perspective)
|
||||
{
|
||||
float farPlaneHalfWidth = std::tan(horizontalFov*0.5f)*farPlaneDistance;
|
||||
float farPlaneHalfHeight = std::tan(verticalFov*0.5f)*farPlaneDistance;
|
||||
|
||||
x = x * farPlaneHalfWidth;
|
||||
y = y * farPlaneHalfHeight;
|
||||
Vector3 right = WorldRight();
|
||||
return pos + front * farPlaneDistance + x * right + y * up;
|
||||
} else {
|
||||
Vector3 right = WorldRight();
|
||||
return pos + front * farPlaneDistance
|
||||
+ x * orthographicWidth * 0.5f * right
|
||||
+ y * orthographicHeight * 0.5f * up;
|
||||
}
|
||||
}
|
||||
|
||||
Polyhedron Frustum::ToPolyhedron() const {
|
||||
// Note to maintainer: this function is an exact copy of AABB::ToPolyhedron() and OBB::ToPolyhedron().
|
||||
|
||||
Polyhedron p;
|
||||
// Populate the corners of this Frustum.
|
||||
// They will be in the order 0: ---, 1: --+, 2: -+-, 3: -++, 4: +--, 5: +-+, 6: ++-, 7: +++.
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
p.v.push_back(CornerPoint(i));
|
||||
}
|
||||
|
||||
// generate the 6 faces of this Frustum. The function Frustum::GetPlane() has a convention of returning
|
||||
// the planes in order near, far, left, right, top, bottom, so follow the same convention here.
|
||||
const int faces[6][4] =
|
||||
{
|
||||
{ 0, 4, 6, 2 }, // Z-: near plane
|
||||
{ 1, 3, 7, 5 }, // Z+: far plane
|
||||
{ 0, 2, 3, 1 }, // X-: left plane
|
||||
{ 4, 5, 7, 6 }, // X+: right plane
|
||||
{ 7, 3, 2, 6 }, // Y+: top plane
|
||||
{ 0, 1, 5, 4 }, // Y-: bottom plane
|
||||
};
|
||||
|
||||
for (int f = 0; f < 6; ++f)
|
||||
{
|
||||
Polyhedron::Face face;
|
||||
if (this->handedness == FrustumHandedness::Left)
|
||||
{
|
||||
for (int v = 0; v < 4; ++v)
|
||||
face.v.push_back(faces[f][3-v]);
|
||||
} else {
|
||||
for (int v = 0; v < 4; ++v)
|
||||
face.v.push_back(faces[f][v]);
|
||||
}
|
||||
p.f.push_back(face);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Ray &ray) const {
|
||||
return this->ToPolyhedron().Intersects(ray);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Line &line) const
|
||||
{
|
||||
///@todo This is a naive test. Implement a faster version.
|
||||
return this->ToPolyhedron().Intersects(line);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const LineSegment &lineSegment) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, lineSegment);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const AABB &aabb) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, aabb);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const OBB &obb) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, obb);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Plane &plane) const
|
||||
{
|
||||
return plane.Intersects(*this);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Triangle &triangle) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, triangle);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Polygon &polygon) const
|
||||
{
|
||||
return polygon.Intersects(*this);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Sphere &sphere) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, sphere);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Capsule &capsule) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, capsule);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Frustum &frustum) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, frustum);
|
||||
}
|
||||
|
||||
bool Frustum::Intersects(const Polyhedron &polyhedron) const
|
||||
{
|
||||
return this->ToPolyhedron().Intersects(polyhedron);
|
||||
}
|
||||
}
|
69
src/J3ML/Geometry/Line.cpp
Normal file
69
src/J3ML/Geometry/Line.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/Geometry/Line.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
/// Computes the closest point pair on two lines.
|
||||
/** The first line is specified by two points start0 and end0. The second line is specified by two points start1 and end1.
|
||||
The implementation of this function follows http://paulbourke.net/geometry/lineline3d/ .
|
||||
@param v0 The starting point of the first line.
|
||||
@param v10 The direction vector of the first line. This can be unnormalized.
|
||||
@param v2 The starting point of the second line.
|
||||
@param v32 The direction vector of the second line. This can be unnormalized.
|
||||
@param d [out] Receives the normalized distance of the closest point along the first line.
|
||||
@param d2 [out] Receives the normalized distance of the closest point along the second line.
|
||||
@return Returns the closest point on line start0<->end0 to the second line.
|
||||
@note This is a low-level utility function. You probably want to use ClosestPoint() or Distance() instead.
|
||||
@see ClosestPoint(), Distance(). */
|
||||
void Line::ClosestPointLineLine(const Vector3 &v0, const Vector3 &v10, const Vector3 &v2, const Vector3 &v32, float &d, float &d2)
|
||||
{
|
||||
assert(!v10.IsZero());
|
||||
assert(!v32.IsZero());
|
||||
Vector3 v02 = v0 - v2;
|
||||
float d0232 = v02.Dot(v32);
|
||||
float d3210 = v32.Dot(v10);
|
||||
float d3232 = v32.Dot(v32);
|
||||
assert(d3232 != 0.f); // Don't call with a zero direction vector.
|
||||
float d0210 = v02.Dot(v10);
|
||||
float d1010 = v10.Dot(v10);
|
||||
float denom = d1010*d3232 - d3210*d3210;
|
||||
if (denom != 0.f)
|
||||
d = (d0232*d3210 - d0210*d3232) / denom;
|
||||
else
|
||||
d = 0.f;
|
||||
d2 = (d0232 + d * d3210) / d3232;
|
||||
}
|
||||
|
||||
Vector3 Line::GetPoint(float d) const
|
||||
{
|
||||
assert(Direction.IsNormalized());
|
||||
return Position + d * Direction;
|
||||
}
|
||||
|
||||
Vector3 Line::ClosestPoint(const Vector3 &targetPoint, float &d) const
|
||||
{
|
||||
d = Vector3::Dot(targetPoint - Position, Direction);
|
||||
return GetPoint(d);
|
||||
}
|
||||
|
||||
Vector3 Line::ClosestPoint(const LineSegment &other, float &d, float &d2) const
|
||||
{
|
||||
ClosestPointLineLine(Position, Direction, other.A, other.B - other.A, d, d2);
|
||||
if (d2 < 0.f)
|
||||
{
|
||||
d2 = 0.f;
|
||||
return ClosestPoint(other.A, d);
|
||||
}
|
||||
else if (d2 > 1.f)
|
||||
{
|
||||
d2 = 1.f;
|
||||
return ClosestPoint(other.B, d);
|
||||
}
|
||||
else
|
||||
return GetPoint(d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include "J3ML/Geometry/Capsule.h"
|
||||
#include <J3ML/Geometry/Line.h>
|
||||
|
||||
namespace Geometry {
|
||||
namespace J3ML::Geometry {
|
||||
|
||||
LineSegment::LineSegment(const Vector3 &a, const Vector3 &b) : A(a), B(b)
|
||||
{
|
||||
@@ -8,4 +10,231 @@ namespace Geometry {
|
||||
}
|
||||
|
||||
LineSegment::LineSegment() {}
|
||||
|
||||
void LineSegment::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
|
||||
{
|
||||
outMin = Vector3::Dot(direction, A);
|
||||
outMax = Vector3::Dot(direction, B);
|
||||
if (outMax < outMin)
|
||||
Swap(outMin, outMax);
|
||||
}
|
||||
|
||||
Vector3 LineSegment::GetPoint(float d) const
|
||||
{
|
||||
return (1.f - d) * A + d * B;
|
||||
}
|
||||
|
||||
float LineSegment::Distance(const Vector3 &point, float &d) const
|
||||
{
|
||||
/// See Christer Ericson's Real-Time Collision Detection, p.130.
|
||||
Vector3 closestPoint = ClosestPoint(point, d);
|
||||
return closestPoint.Distance(point);
|
||||
}
|
||||
|
||||
float LineSegment::DistanceSq(const Vector3 &point) const
|
||||
{
|
||||
float d;
|
||||
/// See Christer Ericson's Real-Time Collision Detection, p.130.
|
||||
Vector3 closestPoint = ClosestPoint(point, d);
|
||||
return closestPoint.DistanceSq(point);
|
||||
}
|
||||
|
||||
float LineSegment::DistanceSq(const LineSegment &other) const
|
||||
{
|
||||
float d, d2;
|
||||
ClosestPoint(other, d, d2);
|
||||
return GetPoint(d).DistanceSq(other.GetPoint(d2));
|
||||
}
|
||||
|
||||
|
||||
float LineSegment::Distance(const Plane &other) const {
|
||||
{
|
||||
float aDist = other.SignedDistance(A);
|
||||
float bDist = other.SignedDistance(B);
|
||||
if (aDist * bDist < 0.f)
|
||||
return 0.f; // There was an intersection, so the distance is zero.
|
||||
return std::min(std::abs(aDist), std::abs(bDist));
|
||||
}
|
||||
}
|
||||
|
||||
float LineSegment::Distance(const LineSegment &other, float &d) const { float d2; return Distance(other, d, d2); }
|
||||
|
||||
float LineSegment::Distance(const LineSegment &other) const { float d, d2; return Distance(other, d, d2); }
|
||||
|
||||
float LineSegment::Distance(const Line &other, float &d) const { float d2; return Distance(other, d, d2); }
|
||||
|
||||
float LineSegment::Distance(const Line &other) const { float d, d2; return Distance(other, d, d2); }
|
||||
|
||||
float LineSegment::Distance(const Ray &other, float &d) const { float d2; return Distance(other, d, d2); }
|
||||
|
||||
float LineSegment::Distance(const Ray &other) const { float d, d2; return Distance(other, d, d2); }
|
||||
|
||||
float LineSegment::Distance(const Vector3 &point) const { float d; return Distance(point, d); }
|
||||
|
||||
Vector3 LineSegment::ClosestPoint(const Vector3 &point, float &d) const {
|
||||
Vector3 dir = B - A;
|
||||
d = Clamp01(Vector3::Dot(point - A, dir) / dir.LengthSquared());
|
||||
return A + d * dir;
|
||||
}
|
||||
|
||||
Vector3 LineSegment::ClosestPoint(const Line &other, float &d, float &d2) const
|
||||
{
|
||||
Line::ClosestPointLineLine(other.Position, other.Direction, A,B - A, d2, d);
|
||||
if (d < 0.f)
|
||||
{
|
||||
d = 0.f;
|
||||
other.ClosestPoint(A, d2);
|
||||
return A;
|
||||
}
|
||||
else if (d > 1.f)
|
||||
{
|
||||
d = 1.f;
|
||||
other.ClosestPoint(B, d2);
|
||||
return B;
|
||||
}
|
||||
else
|
||||
return GetPoint(d);
|
||||
}
|
||||
|
||||
Vector3 LineSegment::ClosestPoint(const Ray &other, float &d, float &d2) const {
|
||||
other.ClosestPoint(*this, d2, d);
|
||||
return GetPoint(d);
|
||||
}
|
||||
|
||||
Vector3 LineSegment::ClosestPoint(const LineSegment &other, float &d, float &d2) const
|
||||
{
|
||||
Vector3 dir = B - A;
|
||||
Line::ClosestPointLineLine(A, B - A, other.A, other.B - other.A, d, d2);
|
||||
if (d >= 0.f && d <= 1.f && d2 >= 0.f && d2 <= 1.f)
|
||||
return A + d * dir;
|
||||
else if (d >= 0.f && d <= 1.f) // Only d2 is out of bounds.
|
||||
{
|
||||
Vector3 p;
|
||||
if (d2 < 0.f)
|
||||
{
|
||||
d2 = 0.f;
|
||||
p = other.A;
|
||||
}
|
||||
else
|
||||
{
|
||||
d2 = 1.f;
|
||||
p = other.B;
|
||||
}
|
||||
return ClosestPoint(p, d);
|
||||
}
|
||||
else if (d2 >= 0.f && d2 <= 1.f) // Only d is out of bounds.
|
||||
{
|
||||
Vector3 p;
|
||||
if (d < 0.f)
|
||||
{
|
||||
d = 0.f;
|
||||
p = A;
|
||||
}
|
||||
else
|
||||
{
|
||||
d = 1.f;
|
||||
p = B;
|
||||
}
|
||||
|
||||
other.ClosestPoint(p, d2);
|
||||
return p;
|
||||
}
|
||||
else // Both u and u2 are out of bounds.
|
||||
{
|
||||
Vector3 p;
|
||||
if (d < 0.f)
|
||||
{
|
||||
p = A;
|
||||
d = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = B;
|
||||
d = 1.f;
|
||||
}
|
||||
|
||||
Vector3 p2;
|
||||
if (d2 < 0.f)
|
||||
{
|
||||
p2 = other.A;
|
||||
d2 = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
p2 = other.B;
|
||||
d2 = 1.f;
|
||||
}
|
||||
|
||||
float T, T2;
|
||||
Vector3 closestPoint = ClosestPoint(p2, T);
|
||||
Vector3 closestPoint2 = other.ClosestPoint(p, T2);
|
||||
|
||||
if (closestPoint.DistanceSq(p2) <= closestPoint2.DistanceSq(p))
|
||||
{
|
||||
d = T;
|
||||
return closestPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
d2 = T2;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float LineSegment::Distance(const Ray &other, float &d, float &d2) const {
|
||||
{
|
||||
ClosestPoint(other, d, d2);
|
||||
return GetPoint(d).Distance(other.GetPoint(d2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float LineSegment::Distance(const Line &other, float &d, float &d2) const {
|
||||
Vector3 closestPoint2 = other.ClosestPoint(*this, d, d2);
|
||||
Vector3 closestPoint = GetPoint(d2);
|
||||
return closestPoint.Distance(closestPoint2);
|
||||
}
|
||||
|
||||
Vector3 LineSegment::Dir() const {
|
||||
return (B - A).Normalize();
|
||||
}
|
||||
|
||||
float LineSegment::Length() const {
|
||||
return A.Distance(B);
|
||||
}
|
||||
|
||||
float LineSegment::LengthSq() const
|
||||
{
|
||||
return A.DistanceSq(B);
|
||||
}
|
||||
|
||||
bool LineSegment::Intersects(const LineSegment &segment) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 LineSegment::ExtremePoint(const Vector3 &direction) const {
|
||||
return Vector3::Dot(direction, B-A) >= 0.f ? B : A;
|
||||
}
|
||||
|
||||
Vector3 LineSegment::ExtremePoint(const Vector3 &direction, float &projectionDistance) const {
|
||||
Vector3 extremePoint = ExtremePoint(direction);
|
||||
projectionDistance = extremePoint.Dot(direction);
|
||||
return extremePoint;
|
||||
}
|
||||
|
||||
bool LineSegment::Contains(const Vector3 &point, float distanceThreshold) const {
|
||||
return ClosestPoint(point).DistanceSq(point) <= distanceThreshold;
|
||||
}
|
||||
|
||||
Vector3 LineSegment::ClosestPoint(const Vector3 &point) const { float d; return ClosestPoint(point, d); }
|
||||
|
||||
float LineSegment::Distance(const LineSegment &other, float &d, float &d2) const {
|
||||
ClosestPoint(other, d, d2);
|
||||
return GetPoint(d).Distance(other.GetPoint(d2));
|
||||
}
|
||||
|
||||
LineSegment operator*(const Matrix4x4 &transform, const LineSegment &l) {
|
||||
return LineSegment(transform.Mul(l.A), transform.Mul(l.B));
|
||||
}
|
||||
}
|
@@ -1,3 +1,443 @@
|
||||
//
|
||||
// Created by dawsh on 1/25/24.
|
||||
//
|
||||
#include <J3ML/Geometry/Shape.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
#include <J3ML/Geometry/OBB.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
Polyhedron OBB::ToPolyhedron() const {
|
||||
// Note to maintainer: This function is an exact copy of AABB::ToPolyhedron() and Frustum::ToPolyhedron()
|
||||
|
||||
Polyhedron p;
|
||||
// populate the corners of this OBB
|
||||
// this will be in the order 0: ---, 1: --+, 2: -+-, 3: -++, 4: +--, 5: +-+, 6: ++-, 7: +++
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
p.v.push_back(CornerPoint(i));
|
||||
}
|
||||
|
||||
// generate the 6 faces of this OBB.
|
||||
const int faces[6][4] =
|
||||
{
|
||||
{0, 1, 3, 2}, // X-
|
||||
{4, 6, 7, 5}, // X+
|
||||
{0, 4, 5, 1}, // Y-
|
||||
{7, 6, 2, 3}, // Y+
|
||||
{0, 2, 6, 4}, // Z-
|
||||
{1, 5, 7, 3} // Z+
|
||||
};
|
||||
|
||||
for (int f = 0; f < 6; ++f)
|
||||
{
|
||||
Polyhedron::Face face;
|
||||
for (int v = 0; v < 4; ++v)
|
||||
{
|
||||
face.v.push_back(faces[f][v]);
|
||||
}
|
||||
//p.f.push_back(face);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
Sphere OBB::MinimalEnclosingSphere() const {
|
||||
Sphere s;
|
||||
s.Position = pos;
|
||||
s.Radius = HalfDiagonal().Length();
|
||||
return s;
|
||||
}
|
||||
|
||||
Sphere OBB::MaximalContainedSphere() const {
|
||||
Sphere s;
|
||||
s.Position = pos;
|
||||
s.Radius = r.MinElement();
|
||||
return s;
|
||||
}
|
||||
|
||||
bool OBB::IsFinite() const {
|
||||
return pos.IsFinite() && r.IsFinite() && axis[0].IsFinite() && axis[1].IsFinite() && axis[2].IsFinite();
|
||||
}
|
||||
|
||||
bool OBB::IsDegenerate() const
|
||||
{
|
||||
return !(r.x > 0.f && r.y > 0.f && r.z > 0.f);
|
||||
}
|
||||
|
||||
Vector3 OBB::CenterPoint() const
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
Vector3 OBB::PointInside(float x, float y, float z) const
|
||||
{
|
||||
assert(0.f <= x && x <= 1.f);
|
||||
assert(0.f <= y && y <= 1.f);
|
||||
assert(0.f <= z && z <= 1.f);
|
||||
|
||||
return pos + axis[0] * (2.f * r.x * x - r.x)
|
||||
+ axis[1] * (2.f * r.y * y - r.y)
|
||||
+ axis[2] * (2.f * r.z * z - r.z);
|
||||
}
|
||||
|
||||
Vector3 OBB::PointInside(const Vector3& pt) const
|
||||
{
|
||||
return PointInside(pt.x, pt.y, pt.z);
|
||||
}
|
||||
|
||||
LineSegment OBB::Edge(int edgeIndex) const
|
||||
{
|
||||
assert(0 <= edgeIndex && edgeIndex <= 11);
|
||||
|
||||
switch(edgeIndex)
|
||||
{
|
||||
default: // For release builds where Assert() is disabled, return always the first option if out-of-bounds
|
||||
case 0: return LineSegment(CornerPoint(0), CornerPoint(1));
|
||||
case 1: return LineSegment(CornerPoint(0), CornerPoint(2));
|
||||
case 2: return LineSegment(CornerPoint(0), CornerPoint(4));
|
||||
case 3: return LineSegment(CornerPoint(1), CornerPoint(3));
|
||||
case 4: return LineSegment(CornerPoint(1), CornerPoint(5));
|
||||
case 5: return LineSegment(CornerPoint(2), CornerPoint(3));
|
||||
case 6: return LineSegment(CornerPoint(2), CornerPoint(6));
|
||||
case 7: return LineSegment(CornerPoint(3), CornerPoint(7));
|
||||
case 8: return LineSegment(CornerPoint(4), CornerPoint(5));
|
||||
case 9: return LineSegment(CornerPoint(4), CornerPoint(6));
|
||||
case 10: return LineSegment(CornerPoint(5), CornerPoint(7));
|
||||
case 11: return LineSegment(CornerPoint(6), CornerPoint(7));
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 OBB::CornerPoint(int cornerIndex) const
|
||||
{
|
||||
assert(0 <= cornerIndex && cornerIndex <= 7);
|
||||
switch(cornerIndex)
|
||||
{
|
||||
default: // For release builds, return always the first option if out of bounds
|
||||
case 0: return pos - r.x * axis[0] - r.y * axis[1] - r.z * axis[2];
|
||||
case 1: return pos - r.x * axis[0] - r.y * axis[1] + r.z * axis[2];
|
||||
case 2: return pos - r.x * axis[0] + r.y * axis[1] - r.z * axis[2];
|
||||
case 3: return pos - r.x * axis[0] + r.y * axis[1] + r.z * axis[2];
|
||||
case 4: return pos + r.x * axis[0] - r.y * axis[1] - r.z * axis[2];
|
||||
case 5: return pos + r.x * axis[0] - r.y * axis[1] + r.z * axis[2];
|
||||
case 6: return pos + r.x * axis[0] + r.y * axis[1] - r.z * axis[2];
|
||||
case 7: return pos + r.x * axis[0] + r.y * axis[1] + r.z * axis[2];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 OBB::ExtremePoint(const Vector3& direction) const
|
||||
{
|
||||
Vector3 pt = pos;
|
||||
pt += axis[0] * (Vector3::Dot(direction, axis[0]) >= 0.f ? r.x : -r.x);
|
||||
pt += axis[1] * (Vector3::Dot(direction, axis[1]) >= 0.f ? r.y : -r.y);
|
||||
pt += axis[2] * (Vector3::Dot(direction, axis[2]) >= 0.f ? r.z : -r.z);
|
||||
return pt;
|
||||
}
|
||||
|
||||
Vector3 OBB::ExtremePoint(const Vector3& direction, float &projectionDistance) const
|
||||
{
|
||||
Vector3 extremePoint = ExtremePoint(direction);
|
||||
projectionDistance = extremePoint.Dot(direction);
|
||||
return extremePoint;
|
||||
}
|
||||
|
||||
void OBB::ProjectToAxis(const Vector3& direction, float& outMin, float& outMax) const
|
||||
{
|
||||
float x = std::abs(Vector3::Dot(direction, axis[0]) * r.x);
|
||||
float y = std::abs(Vector3::Dot(direction, axis[1]) * r.y);
|
||||
float z = std::abs(Vector3::Dot(direction, axis[2]) * r.z);
|
||||
float pt = Vector3::Dot(direction, pos);
|
||||
outMin = pt - x - y - z;
|
||||
outMax = pt + x + y + z;
|
||||
}
|
||||
|
||||
int OBB::UniqueFaceNormals(Vector3* out) const
|
||||
{
|
||||
out[0] = axis[0];
|
||||
out[1] = axis[1];
|
||||
out[2] = axis[2];
|
||||
return 3;
|
||||
}
|
||||
|
||||
int OBB::UniqueEdgeDirections(Vector3* out) const
|
||||
{
|
||||
out[0] = axis[0];
|
||||
out[1] = axis[1];
|
||||
out[2] = axis[2];
|
||||
return 3;
|
||||
}
|
||||
|
||||
Vector3 OBB::PointOnEdge(int edgeIndex, float u) const
|
||||
{
|
||||
assert(0 <= edgeIndex && edgeIndex <= 11);
|
||||
assert(0 <= u && u <= 1.f);
|
||||
|
||||
edgeIndex = std::clamp(edgeIndex, 0, 11);
|
||||
Vector3 d = axis[edgeIndex/4] * (2.f * u - 1.f) * r[edgeIndex/4];
|
||||
switch(edgeIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return pos - r.y * axis[1] - r.z * axis[2] + d;
|
||||
case 1: return pos - r.y * axis[1] + r.z * axis[2] + d;
|
||||
case 2: return pos + r.y * axis[1] - r.z * axis[2] + d;
|
||||
case 3: return pos + r.y * axis[1] + r.z * axis[2] + d;
|
||||
|
||||
case 4: return pos - r.x * axis[0] - r.z * axis[2] + d;
|
||||
case 5: return pos - r.x * axis[0] + r.z * axis[2] + d;
|
||||
case 6: return pos + r.x * axis[0] - r.z * axis[2] + d;
|
||||
case 7: return pos + r.x * axis[0] + r.z * axis[2] + d;
|
||||
|
||||
case 8: return pos - r.x * axis[0] - r.y * axis[1] + d;
|
||||
case 9: return pos - r.x * axis[0] + r.y * axis[1] + d;
|
||||
case 10: return pos + r.x * axis[0] - r.y * axis[1] + d;
|
||||
case 11: return pos + r.x * axis[0] + r.y * axis[1] + d;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 OBB::FaceCenterPoint(int faceIndex) const
|
||||
{
|
||||
assert(0 <= faceIndex && faceIndex <= 6);
|
||||
switch(faceIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return pos - r.x * axis[0];
|
||||
case 1: return pos + r.x * axis[0];
|
||||
case 2: return pos - r.y * axis[1];
|
||||
case 3: return pos + r.y * axis[1];
|
||||
case 4: return pos - r.z * axis[2];
|
||||
case 5: return pos + r.z * axis[2];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 OBB::FacePoint(int faceIndex, float u, float v) const
|
||||
{
|
||||
assert(0 <= faceIndex && faceIndex <= 5);
|
||||
assert(0 <= u && u <= 1.f);
|
||||
assert(0 <= v && v <= 1.f);
|
||||
|
||||
int uIdx = faceIndex/2;
|
||||
int vIdx = (faceIndex/2 + 1) % 3;
|
||||
Vector3 U = axis[uIdx] * (2.f * u - 1.f) * r[uIdx];
|
||||
Vector3 V = axis[vIdx] * (2.f * v - 1.f) * r[vIdx];
|
||||
|
||||
switch(faceIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return pos - r.z * axis[2] + U + V;
|
||||
case 1: return pos + r.z * axis[2] + U + V;
|
||||
case 2: return pos - r.x * axis[0] + U + V;
|
||||
case 3: return pos + r.x * axis[0] + U + V;
|
||||
case 4: return pos - r.y * axis[1] + U + V;
|
||||
case 5: return pos + r.y * axis[1] + U + V;
|
||||
}
|
||||
}
|
||||
|
||||
Plane OBB::FacePlane(int faceIndex) const
|
||||
{
|
||||
assert(0 <= faceIndex && faceIndex <= 5);
|
||||
switch(faceIndex)
|
||||
{
|
||||
default:
|
||||
case 0: return Plane(FaceCenterPoint(0), -axis[0]);
|
||||
case 1: return Plane(FaceCenterPoint(1), axis[0]);
|
||||
case 2: return Plane(FaceCenterPoint(2), -axis[1]);
|
||||
case 3: return Plane(FaceCenterPoint(3), axis[1]);
|
||||
case 4: return Plane(FaceCenterPoint(4), -axis[2]);
|
||||
case 5: return Plane(FaceCenterPoint(5), axis[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void OBB::GetCornerPoints(Vector3 *outPointArray) const
|
||||
{
|
||||
assert(outPointArray);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
outPointArray[i] = CornerPoint(i);
|
||||
}
|
||||
|
||||
void OBB::GetFacePlanes(Plane *outPlaneArray) const
|
||||
{
|
||||
assert(outPlaneArray);
|
||||
for (int i = 0; i < 6; ++i)
|
||||
outPlaneArray[i] = FacePlane(i);
|
||||
}
|
||||
|
||||
void OBB::ExtremePointsAlongDirection(const Vector3& dir, const Vector3* pointArray, int numPoints, int &idxSmallest, int &idxLargest, float &smallestD, float &largestD)
|
||||
{
|
||||
assert(pointArray || numPoints == 0);
|
||||
|
||||
idxSmallest = idxLargest = 0;
|
||||
|
||||
smallestD = INFINITY;
|
||||
largestD = -INFINITY;
|
||||
|
||||
for (int i = 0; i < numPoints; ++i)
|
||||
{
|
||||
float d = Vector3::Dot(pointArray[i], dir);
|
||||
if (d < smallestD)
|
||||
{
|
||||
smallestD = d;
|
||||
idxSmallest = i;
|
||||
}
|
||||
if (d > largestD)
|
||||
{
|
||||
largestD = d;
|
||||
idxLargest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 OBB::Size() const {
|
||||
return r * 2.f;
|
||||
}
|
||||
|
||||
Vector3 OBB::HalfSize() const {
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector3 OBB::Diagonal() const {
|
||||
return 2.f * HalfDiagonal();
|
||||
}
|
||||
|
||||
Vector3 OBB::HalfDiagonal() const {
|
||||
return axis[0] * r[0] + axis[1] * r[1] + axis[2] * r[2];
|
||||
}
|
||||
|
||||
float OBB::Volume() const {
|
||||
Vector3 size = Size();
|
||||
return size.x*size.y*size.z;
|
||||
}
|
||||
|
||||
float OBB::SurfaceArea() const {
|
||||
const Vector3 size = Size();
|
||||
return 2.f * (size.x*size.y + size.x*size.z + size.y*size.z);
|
||||
}
|
||||
|
||||
template <typename Matrix>
|
||||
void OBBSetFrom(OBB &obb, const AABB& aabb, const Matrix& m)
|
||||
{
|
||||
assert(m.IsColOrthogonal()); // We cannot convert transform an AABB to OBB if it gets sheared in the process.
|
||||
assert(m.HasUniformScale()); // Nonuniform scale will produce shear as well
|
||||
obb.pos = m.Mul(aabb.Centroid());
|
||||
obb.r = aabb.HalfSize();
|
||||
obb.axis[0] = Vector3(m.GetColumn3(0));
|
||||
obb.axis[1] = Vector3(m.GetColumn3(1));
|
||||
obb.axis[2] = Vector3(m.GetColumn3(2));
|
||||
// If te matrix m contains scaling, propagate the scaling from the axis vectors to the half-length vectors,
|
||||
// since we want to keep the axis vectors always normalized in our representations.
|
||||
float matrixScale = obb.axis[0].LengthSquared();
|
||||
matrixScale = std::sqrt(matrixScale);
|
||||
obb.r *= matrixScale;
|
||||
matrixScale = 1.f / matrixScale;
|
||||
obb.axis[0] *= matrixScale;
|
||||
obb.axis[1] *= matrixScale;
|
||||
obb.axis[2] *= matrixScale;
|
||||
|
||||
Vector3::Orthonormalize(obb.axis[0], obb.axis[1], obb.axis[2]);
|
||||
|
||||
}
|
||||
|
||||
template <typename Matrix>
|
||||
void OBBTransform(OBB& o, const Matrix& transform)
|
||||
{
|
||||
o.pos = transform.Mul(o.pos);
|
||||
o.axis[0] = transform.Mul(o.r.x * o.axis[0]);
|
||||
o.axis[1] = transform.Mul(o.r.y * o.axis[1]);
|
||||
o.axis[2] = transform.Mul(o.r.z * o.axis[2]);
|
||||
o.r.x = o.axis[0].Normalize().x;
|
||||
o.r.y = o.axis[1].Normalize().y;
|
||||
o.r.z = o.axis[2].Normalize().z;
|
||||
}
|
||||
|
||||
void OBB::SetFrom(const AABB& aabb, const Matrix3x3 &transform) {
|
||||
assert(transform.IsColOrthogonal());
|
||||
|
||||
OBBSetFrom(*this, aabb, transform);
|
||||
}
|
||||
|
||||
void OBB::SetFrom(const AABB& aabb, const Matrix4x4 &transform) {
|
||||
assert(transform.IsColOrthogonal3());
|
||||
|
||||
OBBSetFrom(*this, aabb, transform);
|
||||
}
|
||||
|
||||
void OBB::SetFrom(const AABB& aabb, const Quaternion &transform) {
|
||||
|
||||
OBBSetFrom(*this, aabb, Matrix3x3(transform));
|
||||
}
|
||||
|
||||
void OBB::Transform(const Matrix3x3 &transform) {
|
||||
assert(transform.IsColOrthogonal());
|
||||
OBBTransform(*this, transform);
|
||||
}
|
||||
|
||||
void OBB::Transform(const Matrix4x4 &transform) {
|
||||
assert(transform.IsColOrthogonal3());
|
||||
OBBTransform(*this, transform);
|
||||
}
|
||||
|
||||
void OBB::Transform(const Quaternion &transform) {
|
||||
OBBTransform(*this, transform.ToMatrix3x3());
|
||||
}
|
||||
|
||||
/// The implementation of OBB-Plane intersection test follows Christer Ericson's Real-Time Collision Detection, p. 163.
|
||||
bool OBB::Intersects(const Plane& plane) const {
|
||||
// Compute the projection interval radius of this OBB onto L(t) = this->pos + x * p.normal;
|
||||
float t = r[0] * std::abs(Vector3::Dot(plane.Normal, axis[0])) +
|
||||
r[1] * std::abs(Vector3::Dot(plane.Normal, axis[1])) +
|
||||
r[2] * std::abs(Vector3::Dot(plane.Normal, axis[2]));
|
||||
// Compute the distance of this OBB center from the plane.
|
||||
float s = Vector3::Dot(plane.Normal, pos) - plane.distance;
|
||||
return std::abs(s) <= t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool OBB::Intersects(const LineSegment &lineSegment) const {
|
||||
AABB aabb(Vector3(0.f), Size());
|
||||
LineSegment l = WorldToLocal() * lineSegment;
|
||||
return aabb.Intersects(l);
|
||||
}
|
||||
|
||||
Matrix4x4 OBB::WorldToLocal() const
|
||||
{
|
||||
Matrix4x4 m = LocalToWorld();
|
||||
m.InverseOrthonormal();
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix4x4 OBB::LocalToWorld() const
|
||||
{
|
||||
// To produce a normalized local->world matrix, do the following.
|
||||
/*
|
||||
float3x4 m;
|
||||
vec x = axis[0] * r.x;
|
||||
vec y = axis[1] * r.y;
|
||||
vec z = axis[2] * r.z;
|
||||
m.SetCol(0, 2.f * x);
|
||||
m.SetCol(1, 2.f * y);
|
||||
m.SetCol(2, 2.f * z);
|
||||
m.SetCol(3, pos - x - y - z);
|
||||
return m;
|
||||
*/
|
||||
|
||||
assert(axis[0].IsNormalized());
|
||||
assert(axis[1].IsNormalized());
|
||||
assert(axis[2].IsNormalized());
|
||||
Matrix4x4 m; ///\todo sse-matrix
|
||||
m.SetCol(0, axis[0].ptr());
|
||||
m.SetCol(1, axis[1].ptr());
|
||||
m.SetCol(2, axis[2].ptr());
|
||||
Vector3 p = pos - axis[0] * r.x - axis[1] * r.y - axis[2] * r.z;
|
||||
m.SetCol(3, p.ptr());
|
||||
assert(m.Row3(0).IsNormalized());
|
||||
assert(m.Row3(1).IsNormalized());
|
||||
assert(m.Row3(2).IsNormalized());
|
||||
assert(m.Col(0).IsPerpendicular(m.Col(1)));
|
||||
assert(m.Col(0).IsPerpendicular(m.Col(2)));
|
||||
assert(m.Col(1).IsPerpendicular(m.Col(2)));
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1 +1,266 @@
|
||||
#include <J3ML/Geometry/Plane.h>
|
||||
#include <J3ML/Geometry/Plane.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/OBB.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
bool Plane::IntersectLinePlane(const Vector3 &planeNormal, float planeD, const Vector3 &linePos, const Vector3 &lineDir, float &t)
|
||||
{
|
||||
/* The set of points x lying on a plane is defined by the equation
|
||||
|
||||
<planeNormal, x> = planeD.
|
||||
|
||||
The set of points x on a line is constructed explicitly by a single parameter t by
|
||||
|
||||
x = linePos + t*lineDir.
|
||||
|
||||
To solve the intersection of these two objects, substitute the second equation to the first above,
|
||||
and we get
|
||||
|
||||
<planeNormal, linePos + t*lineDir> == planeD, or
|
||||
<planeNormal, linePos> + t * <planeNormal, lineDir> == planeD, or
|
||||
t == (planeD - <planeNormal, linePos>) / <planeNormal, lineDir>,
|
||||
|
||||
assuming that <planeNormal, lineDir> != 0.
|
||||
|
||||
If <planeNormal, lineDir> == 0, then the line is parallel to the plane, and either no intersection occurs, or the whole line
|
||||
is embedded on the plane, and infinitely many intersections occur. */
|
||||
|
||||
float denom = Vector3::Dot(planeNormal, lineDir);
|
||||
if (std::abs(denom) > 1e-4f)
|
||||
{
|
||||
// Compute the distance from the line starting point to the point of intersection.
|
||||
t = (planeD - Vector3::Dot(planeNormal, linePos)) / denom;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (denom != 0.f)
|
||||
{
|
||||
t = (planeD - Vector3::Dot(planeNormal, linePos)) / denom;
|
||||
if (std::abs(t) < 1e4f)
|
||||
return true;
|
||||
}
|
||||
t = 0.f;
|
||||
return Math::EqualAbs(Vector3::Dot(planeNormal, linePos), planeD, 1e-3f);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
float Plane_SignedDistance(const Plane &plane, const T &object)
|
||||
{
|
||||
float pMin, pMax;
|
||||
assert(plane.Normal.IsNormalized());
|
||||
object.ProjectToAxis(plane.Normal, pMin, pMax);
|
||||
pMin -= plane.distance;
|
||||
pMax -= plane.distance;
|
||||
if (pMin * pMax <= 0.f)
|
||||
return 0.f;
|
||||
return std::abs(pMin) < std::abs(pMax) ? pMin : pMax;
|
||||
}
|
||||
|
||||
float Plane::SignedDistance(const Vector3 &point) const {
|
||||
assert(Normal.IsNormalized());
|
||||
return Normal.Dot(point) - distance;
|
||||
}
|
||||
|
||||
float Plane::SignedDistance(const AABB &aabb) const { return Plane_SignedDistance(*this, aabb); }
|
||||
|
||||
float Plane::SignedDistance(const OBB &obb) const { return Plane_SignedDistance(*this, obb); }
|
||||
|
||||
float Plane::SignedDistance(const Capsule &capsule) const { return Plane_SignedDistance(*this, capsule); }
|
||||
|
||||
float Plane::SignedDistance(const Frustum &frustum) const { return Plane_SignedDistance(*this, frustum); }
|
||||
|
||||
float Plane::SignedDistance(const LineSegment &lineSegment) const { return Plane_SignedDistance(*this, lineSegment); }
|
||||
|
||||
float Plane::SignedDistance(const Ray &ray) const { return Plane_SignedDistance(*this, ray); }
|
||||
|
||||
float Plane::SignedDistance(const Polygon &polygon) const { return Plane_SignedDistance(*this, polygon); }
|
||||
|
||||
float Plane::SignedDistance(const Polyhedron &polyhedron) const { return Plane_SignedDistance(*this, polyhedron); }
|
||||
|
||||
float Plane::SignedDistance(const Sphere &sphere) const { return Plane_SignedDistance(*this, sphere); }
|
||||
|
||||
float Plane::SignedDistance(const Triangle &triangle) const { return Plane_SignedDistance(*this, triangle); }
|
||||
|
||||
float Plane::Distance(const Vector3 &point) const {
|
||||
std::abs(SignedDistance(point));
|
||||
}
|
||||
|
||||
float Plane::Distance(const LineSegment &lineSegment) const
|
||||
{
|
||||
return lineSegment.Distance(*this);
|
||||
}
|
||||
|
||||
float Plane::Distance(const Sphere &sphere) const
|
||||
{
|
||||
return std::max(0.f, Distance(sphere.Position) - sphere.Radius);
|
||||
}
|
||||
|
||||
float Plane::Distance(const Capsule &capsule) const
|
||||
{
|
||||
return std::max(0.f, Distance(capsule.l) - capsule.r);
|
||||
}
|
||||
|
||||
Plane::Plane(const Line &line, const Vector3 &normal) {
|
||||
Vector3 perpNormal = normal - normal.ProjectToNorm(line.Direction);
|
||||
Set(line.Position, perpNormal.Normalize());
|
||||
}
|
||||
|
||||
void Plane::Set(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) {
|
||||
Normal = (v2-v1).Cross(v3-v1);
|
||||
float len = Normal.Length();
|
||||
assert(len > 1e-10f);
|
||||
Normal /= len;
|
||||
assert(Normal.IsNormalized());
|
||||
distance = Normal.Dot(v1);
|
||||
}
|
||||
|
||||
void Plane::Set(const Vector3 &point, const Vector3 &normal_) {
|
||||
Normal = normal_;
|
||||
assert(Normal.IsNormalized());
|
||||
distance = point.Dot(Normal);
|
||||
|
||||
#ifdef MATH_ASSERT_CORRECTNESS
|
||||
assert1(EqualAbs(SignedDistance(point), 0.f, 0.01f), SignedDistance(point));
|
||||
assert1(EqualAbs(SignedDistance(point + normal_), 1.f, 0.01f), SignedDistance(point + normal_));
|
||||
#endif
|
||||
}
|
||||
|
||||
Plane::Plane(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) {
|
||||
Set(v1, v2, v3);
|
||||
}
|
||||
|
||||
Plane::Plane(const Vector3 &pos, const Vector3 &norm)
|
||||
: Shape(), Position(pos), Normal(norm) {}
|
||||
|
||||
bool Plane::Intersects(J3ML::Geometry::Ray ray, float *dist) const {
|
||||
float t;
|
||||
bool success = IntersectLinePlane(Normal, this->distance, ray.Origin, ray.Direction, t);
|
||||
if (dist)
|
||||
*dist = t;
|
||||
return success && t >= 0.f;
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const Line &line, float *dist) const
|
||||
{
|
||||
float t;
|
||||
bool intersects = IntersectLinePlane(Normal, this->distance, line.Position, line.Direction, t);
|
||||
if (dist)
|
||||
*dist = t;
|
||||
return intersects;
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const LineSegment &lineSegment, float *dist) const
|
||||
{
|
||||
float t;
|
||||
bool success = IntersectLinePlane(Normal, this->distance, lineSegment.A, lineSegment.Dir(), t);
|
||||
const float lineSegmentLength = lineSegment.Length();
|
||||
if (dist)
|
||||
*dist = t / lineSegmentLength;
|
||||
return success && t >= 0.f && t <= lineSegmentLength;
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const Sphere &sphere) const
|
||||
{
|
||||
return Distance(sphere.Position) <= sphere.Radius;
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const Capsule &capsule) const
|
||||
{
|
||||
return capsule.Intersects(*this);
|
||||
}
|
||||
|
||||
/// The Plane-AABB intersection is implemented according to Christer Ericson's Real-Time Collision Detection, p.164. [groupSyntax]
|
||||
bool Plane::Intersects(const AABB &aabb) const
|
||||
{
|
||||
Vector3 c = aabb.Centroid();
|
||||
Vector3 e = aabb.HalfDiagonal();
|
||||
|
||||
// Compute the projection interval radius of the AABB onto L(t) = aabb.center + t * plane.normal;
|
||||
float r = e[0]*std::abs(Normal[0]) + e[1]*std::abs(Normal[1]) + e[2]*std::abs(Normal[2]);
|
||||
// Compute the distance of the box center from plane.
|
||||
//float s = Dot(normal, c) - d;
|
||||
float s = Vector3::Dot(Normal, c) - distance; ///\todo Use the above form when Plane is SSE'ized.
|
||||
return std::abs(s) <= r;
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const OBB &obb) const
|
||||
{
|
||||
return obb.Intersects(*this);
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const Triangle &triangle) const
|
||||
{
|
||||
float a = SignedDistance(triangle.V0);
|
||||
float b = SignedDistance(triangle.V1);
|
||||
float c = SignedDistance(triangle.V2);
|
||||
return (a*b <= 0.f || a*c <= 0.f);
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const Frustum &frustum) const
|
||||
{
|
||||
bool sign = IsOnPositiveSide(frustum.CornerPoint(0));
|
||||
for(int i = 1; i < 8; ++i)
|
||||
if (sign != IsOnPositiveSide(frustum.CornerPoint(i)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Plane::IsOnPositiveSide(const Vector3 &point) const {
|
||||
return SignedDistance(point) >= 0.f;
|
||||
}
|
||||
|
||||
bool Plane::Intersects(const Polyhedron &polyhedron) const
|
||||
{
|
||||
if (polyhedron.NumVertices() == 0)
|
||||
return false;
|
||||
bool sign = IsOnPositiveSide(polyhedron.Vertex(0));
|
||||
for(int i = 1; i < polyhedron.NumVertices(); ++i)
|
||||
if (sign != IsOnPositiveSide(polyhedron.Vertex(i)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 Plane::Project(const Vector3 &point) const
|
||||
{
|
||||
Vector3 projected = point - (Normal.Dot(point) - distance) * Normal;
|
||||
return projected;
|
||||
}
|
||||
|
||||
LineSegment Plane::Project(const LineSegment &lineSegment) {
|
||||
return LineSegment(Project(lineSegment.A), Project(lineSegment.B));
|
||||
}
|
||||
|
||||
/*int Plane::Intersects(const Circle &circle, Vector3 *pt1, Vector3 *pt2) const
|
||||
{
|
||||
Line line;
|
||||
bool planeIntersects = Intersects(circle.ContainingPlane(), &line);
|
||||
if (!planeIntersects)
|
||||
return false;
|
||||
|
||||
// Offset both line and circle position so the circle origin is at center.
|
||||
line.pos -= circle.pos;
|
||||
|
||||
float a = 1.f;
|
||||
float b = 2.f * Dot(line.pos, line.dir);
|
||||
float c = line.pos.LengthSq() - circle.r * circle.r;
|
||||
float r1, r2;
|
||||
int numRoots = Polynomial::SolveQuadratic(a, b, c, r1, r2);
|
||||
if (numRoots >= 1 && pt1)
|
||||
*pt1 = circle.pos + line.GetPoint(r1);
|
||||
if (numRoots >= 2 && pt2)
|
||||
*pt2 = circle.pos + line.GetPoint(r2);
|
||||
return numRoots;
|
||||
}
|
||||
|
||||
int Plane::Intersects(const Circle &circle) const
|
||||
{
|
||||
return Intersects(circle, 0, 0);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,603 @@
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/Triangle.h>
|
||||
#include "J3ML/Geometry/Plane.h"
|
||||
#include "J3ML/Geometry/Line.h"
|
||||
#include <J3ML/Algorithm/GJK.h>
|
||||
|
||||
namespace J3ML::Geometry {
|
||||
AABB Polygon::MinimalEnclosingAABB() const {
|
||||
AABB aabb;
|
||||
aabb.SetNegativeInfinity();
|
||||
for(int i = 0; i < NumVertices(); ++i)
|
||||
aabb.Enclose(Vertex(i));
|
||||
return aabb;
|
||||
}
|
||||
|
||||
Vector3 Polygon::ExtremePoint(const Vector3 &direction, float &projectionDistance) const
|
||||
{
|
||||
Vector3 mostExtreme = Vector3::NaN;
|
||||
projectionDistance = -INFINITY;
|
||||
for(int i = 0; i < NumVertices(); ++i)
|
||||
{
|
||||
Vector3 pt = Vertex(i);
|
||||
float d = Vector3::Dot(direction, pt);
|
||||
if (d > projectionDistance)
|
||||
{
|
||||
projectionDistance = d;
|
||||
mostExtreme = pt;
|
||||
}
|
||||
}
|
||||
return mostExtreme;
|
||||
}
|
||||
|
||||
Vector3 Polygon::ExtremePoint(const Vector3 &direction) const
|
||||
{
|
||||
float projectionDistance;
|
||||
return ExtremePoint(direction, projectionDistance);
|
||||
}
|
||||
|
||||
void Polygon::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
|
||||
{
|
||||
///\todo Optimize!
|
||||
Vector3 minPt = ExtremePoint(-direction);
|
||||
Vector3 maxPt = ExtremePoint(direction);
|
||||
outMin = Vector3::Dot(minPt, direction);
|
||||
outMax = Vector3::Dot(maxPt, direction);
|
||||
}
|
||||
|
||||
Vector3 Polygon::Vertex(int vertexIndex) const {
|
||||
assert(vertexIndex >= 0);
|
||||
assert(vertexIndex < (int) vertices.size());
|
||||
return vertices[vertexIndex];
|
||||
}
|
||||
|
||||
int Polygon::NumVertices() const {
|
||||
return (int)vertices.size();
|
||||
}
|
||||
|
||||
bool Polygon::IsPlanar(float epsilonSq) const
|
||||
{
|
||||
if (vertices.empty())
|
||||
return false;
|
||||
if (vertices.size() <= 3)
|
||||
return true;
|
||||
Vector3 normal = (Vector3(vertices[1])-Vector3(vertices[0])).Cross(Vector3(vertices[2])-Vector3(vertices[0]));
|
||||
float lenSq = normal.LengthSquared();
|
||||
for(size_t i = 3; i < vertices.size(); ++i)
|
||||
{
|
||||
float d = normal.Dot(Vector3(vertices[i])-Vector3(vertices[0]));
|
||||
if (d*d > epsilonSq * lenSq)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 Polygon::BasisU() const
|
||||
{
|
||||
if (vertices.size() < 2)
|
||||
return Vector3::Right;
|
||||
Vector3 u = (Vector3)vertices[1] - (Vector3)vertices[0];
|
||||
u = u.Normalize(); // Always succeeds, even if u was zero (generates (1,0,0)).
|
||||
return u;
|
||||
}
|
||||
|
||||
Vector3 Polygon::BasisV() const
|
||||
{
|
||||
if (vertices.size() < 2)
|
||||
return Vector3::Down;
|
||||
return Vector3::Cross(PlaneCCW().Normal, BasisU()).Normalize();
|
||||
}
|
||||
|
||||
Plane Polygon::PlaneCCW() const
|
||||
{
|
||||
if (vertices.size() > 3)
|
||||
{
|
||||
Plane plane;
|
||||
for(size_t i = 0; i < vertices.size()-2; ++i)
|
||||
for(size_t j = i+1; j < vertices.size()-1; ++j)
|
||||
{
|
||||
Vector3 pij = Vector3(vertices[j])-Vector3(vertices[i]);
|
||||
for(size_t k = j+1; k < vertices.size(); ++k)
|
||||
{
|
||||
plane.Normal = pij.Cross(Vector3(vertices[k])-Vector3(vertices[i]));
|
||||
float lenSq = plane.Normal.LengthSquared();
|
||||
if (lenSq > 1e-8f)
|
||||
{
|
||||
plane.Normal /= std::sqrt(lenSq);
|
||||
plane.distance = plane.Normal.Dot(Vector3(vertices[i]));
|
||||
return plane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// warn("Polygon contains %d points, but they are all collinear! Cannot form a plane for the Polygon using three points!", (int)p.size());
|
||||
|
||||
// Polygon contains multiple points, but they are all collinear.
|
||||
// Pick an arbitrary plane along the line as the polygon plane (as if the polygon had only two points)
|
||||
Vector3 dir = (Vector3(vertices[1])-Vector3(vertices[0])).Normalize();
|
||||
return Plane(Line(vertices[0], dir), dir.Perpendicular());
|
||||
}
|
||||
if (vertices.size() == 3)
|
||||
return Plane(vertices[0], vertices[1], vertices[2]);
|
||||
if (vertices.size() == 2)
|
||||
{
|
||||
Vector3 dir = (Vector3(vertices[1])-Vector3(vertices[0])).Normalize();
|
||||
return Plane(Line(vertices[0], dir), dir.Perpendicular());
|
||||
}
|
||||
if (vertices.size() == 1)
|
||||
return Plane(vertices[0], Vector3(0,1,0));
|
||||
return Plane();
|
||||
}
|
||||
|
||||
Vector2 Polygon::MapTo2D(int i) const
|
||||
{
|
||||
assert(i >= 0);
|
||||
assert(i < (int)vertices.size());
|
||||
return MapTo2D(vertices[i]);
|
||||
}
|
||||
|
||||
Vector2 Polygon::MapTo2D(const Vector3 &point) const
|
||||
{
|
||||
assert(!vertices.empty());
|
||||
Vector3 basisU = BasisU();
|
||||
Vector3 basisV = BasisV();
|
||||
Vector3 pt = point - vertices[0];
|
||||
return Vector2(Vector3::Dot(pt, basisU), Vector3::Dot(pt, basisV));
|
||||
}
|
||||
|
||||
Vector3 Polygon::MapFrom2D(const Vector2 &point) const
|
||||
{
|
||||
assert(!vertices.empty());
|
||||
return (Vector3)vertices[0] + point.x * BasisU() + point.y * BasisV();
|
||||
}
|
||||
|
||||
// A(u) = a1 + u * (a2-a1).
|
||||
// B(v) = b1 + v * (b2-b1).
|
||||
// Returns (u,v).
|
||||
bool IntersectLineLine2D(const Vector2 &a1, const Vector2 &a2, const Vector2 &b1, const Vector2 &b2, Vector2 &out)
|
||||
{
|
||||
float u = (b2.x - b1.x)*(a1.y - b1.y) - (b2.y - b1.y)*(a1.x - b1.x);
|
||||
float v = (a2.x - a1.x)*(a1.y - b1.y) - (a2.y - a1.y)*(a1.x - b1.x);
|
||||
|
||||
float det = (b2.y - b1.y)*(a2.x - a1.x) - (b2.x - b1.x)*(a2.y - a1.y);
|
||||
if (std::abs(det) < 1e-4f)
|
||||
return false;
|
||||
det = 1.f / det;
|
||||
out.x = u * det;
|
||||
out.y = v * det;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntersectLineSegmentLineSegment2D(const Vector2 &a1, const Vector2 &a2, const Vector2 &b1, const Vector2 &b2, Vector2 &out)
|
||||
{
|
||||
bool ret = IntersectLineLine2D(a1, a2, b1, b2, out);
|
||||
return ret && out.x >= 0.f && out.x <= 1.f && out.y >= 0.f && out.y <= 1.f;
|
||||
}
|
||||
|
||||
|
||||
/// Returns true if poly[i+1] is an ear.
|
||||
/// Precondition: i+2 == j (mod poly.size()).
|
||||
bool IsAnEar(const std::vector<Vector2> &poly, int i, int j)
|
||||
{
|
||||
Vector2 dummy;
|
||||
int x = (int)poly.size()-1;
|
||||
for(int y = 0; y < i; ++y)
|
||||
{
|
||||
if (IntersectLineSegmentLineSegment2D(poly[i], poly[j], poly[x], poly[y], dummy))
|
||||
return false;
|
||||
x = y;
|
||||
}
|
||||
x = j+1;
|
||||
for(int y = x+1; y < (int)poly.size(); ++y)
|
||||
{
|
||||
if (IntersectLineSegmentLineSegment2D(poly[i], poly[j], poly[x], poly[y], dummy))
|
||||
return false;
|
||||
x = y;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::Contains(const Polygon &worldSpacePolygon, float polygonThickness) const
|
||||
{
|
||||
for(int i = 0; i < worldSpacePolygon.NumVertices(); ++i)
|
||||
if (!Contains(worldSpacePolygon.Vertex(i), polygonThickness))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::Contains(const Vector3 &worldSpacePoint, float polygonThicknessSq) const
|
||||
{
|
||||
// Implementation based on the description from http://erich.realtimerendering.com/ptinpoly/
|
||||
|
||||
if (vertices.size() < 3)
|
||||
return false;
|
||||
|
||||
Vector3 basisU = BasisU();
|
||||
Vector3 basisV = BasisV();
|
||||
assert(basisU.IsNormalized());
|
||||
assert(basisV.IsNormalized());
|
||||
assert(basisU.IsPerpendicular(basisV));
|
||||
assert(basisU.IsPerpendicular(PlaneCCW().Normal));
|
||||
assert(basisV.IsPerpendicular(PlaneCCW().Normal));
|
||||
|
||||
Vector3 normal = basisU.Cross(basisV);
|
||||
// float lenSq = normal.LengthSq(); ///\todo Could we treat basisU and basisV unnormalized here?
|
||||
float dot = normal.Dot(Vector3(vertices[0]) - worldSpacePoint);
|
||||
if (dot*dot > polygonThicknessSq)
|
||||
return false; // The point is not even within the plane of the polygon - can't be contained.
|
||||
|
||||
int numIntersections = 0;
|
||||
|
||||
const float epsilon = 1e-4f;
|
||||
|
||||
// General strategy: transform all points on the polygon onto 2D face plane of the polygon, where the target query point is
|
||||
// centered to lie in the origin.
|
||||
// If the test ray (0,0) -> (+inf, 0) intersects exactly an odd number of polygon edge segments, then the query point must have been
|
||||
// inside the polygon. The test ray is chosen like that to avoid all extra per-edge computations.
|
||||
// This method works for both simple and non-simple (self-intersecting) polygons.
|
||||
Vector3 vt = Vector3(vertices.back()) - worldSpacePoint;
|
||||
Vector2 p0 = Vector2(Vector3::Dot(vt, basisU), Vector3::Dot(vt, basisV));
|
||||
if (std::abs(p0.y) < epsilon)
|
||||
p0.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.
|
||||
|
||||
for(int i = 0; i < (int)vertices.size(); ++i)
|
||||
{
|
||||
vt = Vector3(vertices[i]) - worldSpacePoint;
|
||||
Vector2 p1 = Vector2(Vector3::Dot(vt, basisU), Vector3::Dot(vt, basisV));
|
||||
if (std::abs(p1.y) < epsilon)
|
||||
p1.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.
|
||||
|
||||
if (p0.y * p1.y < 0.f) // If the line segment p0 -> p1 straddles the line x=0, it could intersect the ray (0,0) -> (+inf, 0)
|
||||
{
|
||||
if (std::min(p0.x, p1.x) > 0.f) // If both x-coordinates are positive, then there certainly is an intersection with the ray.
|
||||
++numIntersections;
|
||||
else if (std::max(p0.x, p1.x) > 0.f) // If one of them is positive, there could be an intersection. (otherwise both are negative and they can't intersect ray)
|
||||
{
|
||||
// P = p0 + t*(p1-p0) == (x,0)
|
||||
// p0.x + t*(p1.x-p0.x) == x
|
||||
// p0.y + t*(p1.y-p0.y) == 0
|
||||
// t == -p0.y / (p1.y - p0.y)
|
||||
|
||||
// Test whether the lines (0,0) -> (+inf,0) and p0 -> p1 intersect at a positive X-coordinate?
|
||||
Vector2 d = p1 - p0;
|
||||
if (d.y != 0.f)
|
||||
{
|
||||
float t = -p0.y / d.y; // The line segment parameter, t \in [0,1] forms the line segment p0->p1.
|
||||
float x = p0.x + t * d.x; // The x-coordinate of intersection with the ray.
|
||||
if (t >= 0.f && t <= 1.f && x > 0.f)
|
||||
++numIntersections;
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
return numIntersections % 2 == 1;
|
||||
}
|
||||
|
||||
bool Polygon::Contains(const LineSegment &worldSpaceLineSegment, float polygonThickness) const
|
||||
{
|
||||
if (vertices.size() < 3)
|
||||
return false;
|
||||
|
||||
Plane plane = PlaneCCW();
|
||||
if (plane.Distance(worldSpaceLineSegment.A) > polygonThickness ||
|
||||
plane.Distance(worldSpaceLineSegment.B) > polygonThickness)
|
||||
return false;
|
||||
|
||||
// For robustness, project onto the polygon plane.
|
||||
LineSegment l = plane.Project(worldSpaceLineSegment);
|
||||
|
||||
if (!Contains(l.A) || !Contains(l.B))
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < (int)vertices.size(); ++i)
|
||||
if (plane.Project(Edge(i)).Intersects(l))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::Contains(const Triangle &worldSpaceTriangle, float polygonThickness) const
|
||||
{
|
||||
return Contains(worldSpaceTriangle.Edge(0), polygonThickness) &&
|
||||
Contains(worldSpaceTriangle.Edge(1), polygonThickness) &&
|
||||
Contains(worldSpaceTriangle.Edge(2), polygonThickness);
|
||||
}
|
||||
|
||||
/** The implementation of this function is based on the paper
|
||||
"Kong, Everett, Toussant. The Graham Scan Triangulates Simple Polygons."
|
||||
See also p. 772-775 of Geometric Tools for Computer Graphics.
|
||||
The running time of this function is O(n^2). */
|
||||
std::vector<Triangle> Polygon::Triangulate() const
|
||||
{
|
||||
assert(IsPlanar());
|
||||
// assume1(IsPlanar(), this->SerializeToString()); // TODO: enable
|
||||
|
||||
std::vector<Triangle> t;
|
||||
// Handle degenerate cases.
|
||||
if (NumVertices() < 3)
|
||||
return t;
|
||||
if (NumVertices() == 3)
|
||||
{
|
||||
t.push_back(Triangle(Vertex(0), Vertex(1), Vertex(2)));
|
||||
return t;
|
||||
}
|
||||
std::vector<Vector2> p2d;
|
||||
std::vector<int> polyIndices;
|
||||
for(int v = 0; v < NumVertices(); ++v)
|
||||
{
|
||||
p2d.push_back(MapTo2D(v));
|
||||
polyIndices.push_back(v);
|
||||
}
|
||||
|
||||
// Clip ears of the polygon until it has been reduced to a triangle.
|
||||
int i = 0;
|
||||
int j = 1;
|
||||
int k = 2;
|
||||
size_t numTries = 0; // Avoid creating an infinite loop.
|
||||
while(p2d.size() > 3 && numTries < p2d.size())
|
||||
{
|
||||
if (Vector2::OrientedCCW(p2d[i], p2d[j], p2d[k]) && IsAnEar(p2d, i, k))
|
||||
{
|
||||
// The vertex j is an ear. Clip it off.
|
||||
t.push_back(Triangle(vertices[polyIndices[i]], vertices[polyIndices[j]], vertices[polyIndices[k]]));
|
||||
p2d.erase(p2d.begin() + j);
|
||||
polyIndices.erase(polyIndices.begin() + j);
|
||||
|
||||
// The previous index might now have become an ear. Move back one index to see if so.
|
||||
if (i > 0)
|
||||
{
|
||||
i = (i + (int)p2d.size() - 1) % p2d.size();
|
||||
j = (j + (int)p2d.size() - 1) % p2d.size();
|
||||
k = (k + (int)p2d.size() - 1) % p2d.size();
|
||||
}
|
||||
numTries = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The vertex at j is not an ear. Move to test next vertex.
|
||||
i = j;
|
||||
j = k;
|
||||
k = (k+1) % p2d.size();
|
||||
++numTries;
|
||||
}
|
||||
}
|
||||
|
||||
assert(p2d.size() == 3);
|
||||
if (p2d.size() > 3) // If this occurs, then the polygon is NOT counter-clockwise oriented.
|
||||
return t;
|
||||
/*
|
||||
{
|
||||
// For conveniency, create a copy that has the winding order fixed, and triangulate that instead.
|
||||
// (Causes a large performance hit!)
|
||||
Polygon p2 = *this;
|
||||
for(size_t i = 0; i < p2.p.size()/2; ++i)
|
||||
std::swap(p2.p[i], p2.p[p2.p.size()-1-i]);
|
||||
return p2.Triangulate();
|
||||
}
|
||||
*/
|
||||
// Add the last poly.
|
||||
t.push_back(Triangle(vertices[polyIndices[0]], vertices[polyIndices[1]], vertices[polyIndices[2]]));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool Polygon::Intersects(const Capsule &capsule) const {
|
||||
///@todo Optimize.
|
||||
std::vector<Triangle> tris = Triangulate();
|
||||
for(size_t i = 0; i < tris.size(); ++i)
|
||||
if (tris[i].Intersects(capsule))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polygon::Intersects(const Line &line) const {
|
||||
float d;
|
||||
if (!PlaneCCW().Intersects(line, &d))
|
||||
return false;
|
||||
return Contains(line.GetPoint(d));
|
||||
}
|
||||
bool Polygon::Intersects(const Ray &ray) const
|
||||
{
|
||||
float d;
|
||||
if (!PlaneCCW().Intersects(ray, &d))
|
||||
return false;
|
||||
return Contains(ray.GetPoint(d));
|
||||
}
|
||||
|
||||
bool Polygon::Intersects2D(const LineSegment &localSpaceLineSegment) const
|
||||
{
|
||||
if (vertices.size() < 3)
|
||||
return false;
|
||||
|
||||
const Vector3 basisU = BasisU();
|
||||
const Vector3 basisV = BasisV();
|
||||
const Vector3 origin = vertices[0];
|
||||
|
||||
LineSegment edge;
|
||||
edge.A = Vector3(Vector3::Dot(vertices.back(), basisU), Vector3::Dot(vertices.back(), basisV), 0); // map to 2D
|
||||
for (int i = 0; i < (int)vertices.size(); ++i)
|
||||
{
|
||||
edge.B = Vector3(Vector3::Dot(vertices[i], basisU), Vector3::Dot(vertices[i], basisV), 0); // map to 2D
|
||||
if (edge.Intersects(localSpaceLineSegment))
|
||||
return true;
|
||||
edge.A = edge.B;
|
||||
}
|
||||
|
||||
// The line segment did not intersect with any of the polygon edges, so either the whole line segment is inside
|
||||
// the polygon, or it is fully outside the polygon. Test one point of the line segment to determine which.
|
||||
Vector2 xy = {
|
||||
localSpaceLineSegment.A.x,
|
||||
localSpaceLineSegment.A.y
|
||||
};
|
||||
return Contains(MapFrom2D(xy));
|
||||
}
|
||||
|
||||
bool Polygon::Intersects(const LineSegment &lineSegment) const
|
||||
{
|
||||
Plane plane = PlaneCCW();
|
||||
|
||||
// Compute line-plane intersection (unroll Plane::IntersectLinePlane())
|
||||
float denom = Vector3::Dot(plane.Normal, lineSegment.B - lineSegment.A);
|
||||
|
||||
if (std::abs(denom) < 1e-4f) // The plane of the polygon and the line are planar? Do the test in 2D.
|
||||
return Intersects2D(LineSegment(Vector3(MapTo2D(lineSegment.A), 0), Vector3(MapTo2D(lineSegment.B), 0)));
|
||||
|
||||
// The line segment properly intersects the plane of the polygon, so there is exactly one
|
||||
// point of intersection between the plane of the polygon and the line segment. Test that intersection point against
|
||||
// the line segment end points.
|
||||
float t = (plane.distance - Vector3::Dot(plane.Normal, lineSegment.A)) / denom;
|
||||
if (t < 0.f || t > 1.f)
|
||||
return false;
|
||||
|
||||
return Contains(lineSegment.GetPoint(t));
|
||||
}
|
||||
bool Polygon::Intersects(const Plane &plane) const
|
||||
{
|
||||
// Project the points of this polygon onto the 1D axis of the plane normal.
|
||||
// If there are points on both sides of the plane, then the polygon intersects the plane.
|
||||
float minD = INFINITY;
|
||||
float maxD = -INFINITY;
|
||||
for(size_t i = 0; i < vertices.size(); ++i)
|
||||
{
|
||||
float d = plane.SignedDistance(vertices[i]);
|
||||
minD = std::min(minD, d);
|
||||
maxD = std::max(maxD, d);
|
||||
}
|
||||
// Allow a very small epsilon tolerance.
|
||||
return minD <= 1e-4f && maxD >= -1e-4f;
|
||||
}
|
||||
bool Polygon::ConvexIntersects(const AABB &aabb) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, aabb);
|
||||
}
|
||||
|
||||
bool Polygon::ConvexIntersects(const OBB &obb) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, obb);
|
||||
}
|
||||
|
||||
bool Polygon::ConvexIntersects(const Frustum &frustum) const
|
||||
{
|
||||
return Algorithms::GJKIntersect(*this, frustum);
|
||||
}
|
||||
|
||||
template<typename Convex /* = AABB, OBB, Frustum. */>
|
||||
bool Convex_Intersects_Polygon(const Convex &c, const Polygon &p)
|
||||
{
|
||||
LineSegment l;
|
||||
l.A = p.vertices.back();
|
||||
for(size_t i = 0; i < p.vertices.size(); ++i)
|
||||
{
|
||||
l.B = p.vertices[i];
|
||||
if (c.Intersects(l))
|
||||
return true;
|
||||
l.A = l.B;
|
||||
}
|
||||
|
||||
// Check all the edges of the convex shape against the polygon.
|
||||
for(int i = 0; i < c.NumEdges(); ++i)
|
||||
{
|
||||
l = c.Edge(i);
|
||||
if (p.Intersects(l))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polygon::Intersects(const AABB &aabb) const
|
||||
{
|
||||
// Because GJK test is so fast, use that as an early-out. (computes intersection between the convex hull of this poly, and the aabb)
|
||||
bool convexIntersects = ConvexIntersects(aabb);
|
||||
if (!convexIntersects)
|
||||
return false;
|
||||
|
||||
return Convex_Intersects_Polygon(aabb, *this);
|
||||
}
|
||||
|
||||
bool Polygon::Intersects(const OBB &obb) const
|
||||
{
|
||||
// Because GJK test is so fast, use that as an early-out. (computes intersection between the convex hull of this poly, and the obb)
|
||||
bool convexIntersects = ConvexIntersects(obb);
|
||||
if (!convexIntersects)
|
||||
return false;
|
||||
|
||||
return Convex_Intersects_Polygon(obb, *this);
|
||||
}
|
||||
|
||||
bool Polygon::Intersects(const Frustum &frustum) const
|
||||
{
|
||||
// Because GJK test is so fast, use that as an early-out. (computes intersection between the convex hull of this poly, and the frustum)
|
||||
bool convexIntersects = ConvexIntersects(frustum);
|
||||
if (!convexIntersects)
|
||||
return false;
|
||||
|
||||
return Convex_Intersects_Polygon(frustum, *this);
|
||||
}
|
||||
|
||||
LineSegment Polygon::Edge(int i) const
|
||||
{
|
||||
if (vertices.empty())
|
||||
return LineSegment(Vector3::NaN, Vector3::NaN);
|
||||
if (vertices.size() == 1)
|
||||
return LineSegment(vertices[0], vertices[0]);
|
||||
return LineSegment(vertices[i], vertices[(i+1)%vertices.size()]);
|
||||
}
|
||||
|
||||
Vector3 Polygon::ClosestPoint(const Vector3 &point) const
|
||||
{
|
||||
assert(IsPlanar());
|
||||
|
||||
std::vector<Triangle> tris = Triangulate();
|
||||
Vector3 closestPt = Vector3::NaN;
|
||||
float closestDist = FLT_MAX;
|
||||
for(size_t i = 0; i < tris.size(); ++i)
|
||||
{
|
||||
Vector3 pt = ((Triangle)tris[i]).ClosestPoint(point);
|
||||
float d = pt.DistanceSq(point);
|
||||
if (d < closestDist)
|
||||
{
|
||||
closestPt = pt;
|
||||
closestDist = d;
|
||||
}
|
||||
}
|
||||
return closestPt;
|
||||
}
|
||||
|
||||
Vector3 Polygon::ClosestPoint(const LineSegment &lineSegment) const
|
||||
{
|
||||
return ClosestPoint(lineSegment, 0);
|
||||
}
|
||||
|
||||
Vector3 Polygon::ClosestPoint(const LineSegment &lineSegment, Vector3 *lineSegmentPt) const
|
||||
{
|
||||
std::vector<Triangle> tris = Triangulate();
|
||||
Vector3 closestPt = Vector3::NaN;
|
||||
Vector3 closestLineSegmentPt = Vector3::NaN;
|
||||
float closestDist = FLT_MAX;
|
||||
for(size_t i = 0; i < tris.size(); ++i)
|
||||
{
|
||||
Vector3 lineSegPt;
|
||||
Vector3 pt = ((Triangle)tris[i]).ClosestPoint(lineSegment, &lineSegPt);
|
||||
float d = pt.DistanceSq(lineSegPt);
|
||||
if (d < closestDist)
|
||||
{
|
||||
closestPt = pt;
|
||||
closestLineSegmentPt = lineSegPt;
|
||||
closestDist = d;
|
||||
}
|
||||
}
|
||||
if (lineSegmentPt)
|
||||
*lineSegmentPt = closestLineSegmentPt;
|
||||
return closestPt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
}
|
@@ -1,6 +1,681 @@
|
||||
#include <J3ML/Geometry/Polyhedron.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/Triangle.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include "J3ML/Geometry/Ray.h"
|
||||
#include "J3ML/Geometry/Line.h"
|
||||
#include <J3ML/Geometry/Polygon.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
#include <set>
|
||||
#include <cfloat>
|
||||
|
||||
namespace Geometry
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
int Polyhedron::ExtremeVertex(const Vector3 &direction) const
|
||||
{
|
||||
int mostExtreme = -1;
|
||||
float mostExtremeDist = -FLT_MAX;
|
||||
for(int i = 0; i < NumVertices(); ++i)
|
||||
{
|
||||
float d = Vector3::Dot(direction, Vertex(i));
|
||||
if (d > mostExtremeDist)
|
||||
{
|
||||
mostExtremeDist = d;
|
||||
mostExtreme = i;
|
||||
}
|
||||
}
|
||||
return mostExtreme;
|
||||
}
|
||||
|
||||
Vector3 Polyhedron::ExtremePoint(const Vector3 &direction) const
|
||||
{
|
||||
return Vertex(ExtremeVertex(direction));
|
||||
}
|
||||
|
||||
void Polyhedron::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
|
||||
{
|
||||
///\todo Optimize!
|
||||
Vector3 minPt = ExtremePoint(-direction);
|
||||
Vector3 maxPt = ExtremePoint(direction);
|
||||
outMin = Vector3::Dot(minPt, direction);
|
||||
outMax = Vector3::Dot(maxPt, direction);
|
||||
}
|
||||
|
||||
int Polyhedron::NumEdges() const
|
||||
{
|
||||
int numEdges = 0;
|
||||
for(size_t i = 0; i < f.size(); ++i)
|
||||
numEdges += (int)f[i].v.size();
|
||||
return numEdges / 2;
|
||||
}
|
||||
|
||||
AABB Polyhedron::MinimalEnclosingAABB() const {
|
||||
AABB aabb;
|
||||
aabb.SetNegativeInfinity();
|
||||
for (int i = 0; i < NumVertices(); ++i)
|
||||
aabb.Enclose(Vertex(i));
|
||||
return aabb;
|
||||
}
|
||||
|
||||
Vector3 Polyhedron::Vertex(int vertexIndex) const {
|
||||
return v[vertexIndex];
|
||||
}
|
||||
|
||||
LineSegment Polyhedron::Edge(int edgeIndex) const
|
||||
{
|
||||
assert(edgeIndex >= 0);
|
||||
std::vector<LineSegment> edges = Edges();
|
||||
assert(edgeIndex < (int)edges.size());
|
||||
return edges[edgeIndex];
|
||||
}
|
||||
|
||||
Polygon Polyhedron::FacePolygon(int faceIndex) const
|
||||
{
|
||||
Polygon p;
|
||||
assert(faceIndex >= 0);
|
||||
assert(faceIndex < (int)f.size());
|
||||
|
||||
p.vertices.reserve(f[faceIndex].v.size());
|
||||
for(size_t i = 0; i < f[faceIndex].v.size(); ++i)
|
||||
p.vertices.push_back(Vertex(f[faceIndex].v[i]));
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
std::vector<LineSegment> Polyhedron::Edges() const
|
||||
{
|
||||
std::vector<std::pair<int, int> > edges = EdgeIndices();
|
||||
std::vector<LineSegment> edgeLines;
|
||||
edgeLines.reserve(edges.size());
|
||||
for(size_t i = 0; i < edges.size(); ++i)
|
||||
edgeLines.push_back(LineSegment(Vertex(edges[i].first), Vertex(edges[i].second)));
|
||||
|
||||
return edgeLines;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int> > Polyhedron::EdgeIndices() const
|
||||
{
|
||||
std::set<std::pair<int, int> > uniqueEdges;
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
{
|
||||
assert(f[i].v.size() >= 3);
|
||||
if (f[i].v.size() < 3)
|
||||
continue; // Degenerate face with less than three vertices, skip!
|
||||
int x = f[i].v.back();
|
||||
for(size_t j = 0; j < f[i].v.size(); ++j)
|
||||
{
|
||||
int y = f[i].v[j];
|
||||
uniqueEdges.insert(std::make_pair(std::min(x, y), std::max(x, y)));
|
||||
x = y;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int> >edges;
|
||||
edges.insert(edges.end(), uniqueEdges.begin(), uniqueEdges.end());
|
||||
return edges;
|
||||
}
|
||||
|
||||
|
||||
bool Polyhedron::Contains(const Vector3 &point) const {
|
||||
|
||||
if (v.size() <= 3) {
|
||||
if (v.size() == 3)
|
||||
return Triangle(Vector3(v[0]), Vector3(v[1]), Vector3(v[2])).Contains(point);
|
||||
else if (v.size() == 2)
|
||||
return LineSegment(Vector3(v[0]), Vector3(v[1])).Contains(point);
|
||||
else if (v.size() == 1)
|
||||
return Vector3(v[0]).Equals(point);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int bestNumIntersections = 0;
|
||||
float bestFaceContainmentDistance = 0.f;
|
||||
|
||||
// For N > 4 Order Polyhedra
|
||||
// General strategy: pick a ray from the query point to a random direction, and count the number of times the ray intersects a face.
|
||||
// If it intersects an odd number of times, the given point must have been inside the polyhedron.
|
||||
// But unfortunately for numerical stability, we must be smart with the choice of the ray direction. If we pick a ray direction
|
||||
// which exits the polyhedron precisely at a vertex, or at an edge of two adjoining faces, we might count those twice. Therefore
|
||||
// Try to pick a ray direction that passes safely through a center of some face. If we detect that there was a tricky face that
|
||||
// the ray passed too close to an edge, we have no choice but to pick another ray direction and hope that it passes through
|
||||
// the polyhedron in a safe manner.
|
||||
|
||||
|
||||
// Loop through each face to choose the ray direction. If our choice was good, we only do this once and the algorithm
|
||||
// after the first iteration at j == 0. If not, we iterate more faces of the polyhedron to try to find one that is safe for
|
||||
// ray-polyhedron examination.
|
||||
for (int j = 0; j < (int)f.size(); ++j)
|
||||
{
|
||||
if (f[j].v.size() < 3)
|
||||
continue;
|
||||
|
||||
// Accumulate how many times the ray intersected a face of the polyhedron.
|
||||
int numIntersections = 0;
|
||||
// Track a pseudo-distance of the closest edge of a face that the ray passed through. If this distance ends up being too small,
|
||||
// we decide not to trust the result we got, and proceed to another iteration of j, hoping to guess a better-behaving direction
|
||||
// for the test ray.
|
||||
float faceContainmentDistance = INFINITY;
|
||||
|
||||
Vector3 Dir = ((Vector3)v[f[j].v[0]] + (Vector3)v[f[j].v[1]] + (Vector3)v[f[j].v[2]]) * 0.333333333333f - point;
|
||||
|
||||
//if (Dir.Normalize() <= 0.f)
|
||||
//continue;
|
||||
Ray r(Vector3(), Dir);
|
||||
|
||||
for (int i = 0; i < (int)f.size(); ++i)
|
||||
{
|
||||
Plane p((Vector3)v[f[i].v[0]] - point, (Vector3)v[f[i].v[1]] - point, (Vector3)v[f[i].v[2]] - point);
|
||||
|
||||
float d;
|
||||
// Find the intersection of the plane and the ray.
|
||||
if (p.Intersects(r, &d))
|
||||
{
|
||||
float containmentDistance2D = FaceContainmentDistance2D(i, r.GetPoint(d) + point);
|
||||
if (containmentDistance2D >= 0.f)
|
||||
++numIntersections;
|
||||
faceContainmentDistance = std::min(faceContainmentDistance, std::abs(containmentDistance2D));
|
||||
}
|
||||
}
|
||||
if (faceContainmentDistance > 1e-2f) // The nearest edge was far enough, we conclude the result is believable.
|
||||
return (numIntersections % 2) == 1;
|
||||
else if (faceContainmentDistance >= bestFaceContainmentDistance)
|
||||
{
|
||||
// The ray passed too close to a face edge. Remember this result, but proceed to another test iteration to see if we can
|
||||
// find a more plausible test ray.
|
||||
bestNumIntersections = numIntersections;
|
||||
bestFaceContainmentDistance = faceContainmentDistance;
|
||||
}
|
||||
}
|
||||
// We tested rays through each face of the polyhedron, but all rays passed too close to edges of the polyhedron faces. Return
|
||||
// the result from the test that was farthest to any of the face edges.
|
||||
return (bestNumIntersections % 2) == 1;
|
||||
}
|
||||
|
||||
float Polyhedron::FaceContainmentDistance2D(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness) const
|
||||
{
|
||||
// N.B. This implementation is a duplicate of Polygon::Contains, but adapted to avoid dynamic memory allocation
|
||||
// related to converting the face of a Polyhedron to a Polygon object.
|
||||
|
||||
// Implementation based on the description from http://erich.realtimerendering.com/ptinpoly/
|
||||
|
||||
const Face &face = f[faceIndex];
|
||||
const std::vector<int> &vertices = face.v;
|
||||
|
||||
if (vertices.size() < 3)
|
||||
return -INFINITY; // Certainly not intersecting, so return -inf denoting "strongly not contained"
|
||||
|
||||
Plane p = FacePlane(faceIndex);
|
||||
if (FacePlane(faceIndex).Distance(worldSpacePoint) > polygonThickness)
|
||||
return -INFINITY;
|
||||
|
||||
int numIntersections = 0;
|
||||
|
||||
Vector3 basisU = (Vector3)v[vertices[1]] - (Vector3)v[vertices[0]];
|
||||
basisU.Normalize();
|
||||
Vector3 basisV = Vector3::Cross(p.Normal, basisU).Normalize();
|
||||
assert(basisU.IsNormalized());
|
||||
assert(basisV.IsNormalized());
|
||||
assert(basisU.IsPerpendicular(basisV));
|
||||
assert(basisU.IsPerpendicular(p.Normal));
|
||||
assert(basisV.IsPerpendicular(p.Normal));
|
||||
|
||||
// Tracks a pseudo-distance of the point to the ~nearest edge of the polygon. If the point is very close to the polygon
|
||||
// edge, this is very small, and it's possible that due to numerical imprecision we cannot rely on the result in higher-level
|
||||
// algorithms that invoke this function.
|
||||
float faceContainmentDistance = INFINITY;
|
||||
const float epsilon = 1e-4f;
|
||||
|
||||
Vector3 vt = Vector3(v[vertices.back()]) - worldSpacePoint;
|
||||
Vector2 p0 = Vector2(Vector3::Dot(vt, basisU), Vector3::Dot(vt, basisV));
|
||||
if (std::abs(p0.y) < epsilon)
|
||||
p0.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.
|
||||
|
||||
for(size_t i = 0; i < vertices.size(); ++i)
|
||||
{
|
||||
vt = Vector3(v[vertices[i]]) - worldSpacePoint;
|
||||
Vector2 p1 = Vector2(Vector3::Dot(vt, basisU), Vector3::Dot(vt, basisV));
|
||||
if (std::abs(p1.y) < epsilon)
|
||||
p1.y = -epsilon; // Robustness check - if the ray (0,0) -> (+inf, 0) would pass through a vertex, move the vertex slightly.
|
||||
|
||||
if (p0.y * p1.y < 0.f)
|
||||
{
|
||||
float minX = std::min(p0.x, p1.x);
|
||||
if (minX > 0.f)
|
||||
{
|
||||
faceContainmentDistance = std::min(faceContainmentDistance, minX);
|
||||
++numIntersections;
|
||||
}
|
||||
else if (std::max(p0.x, p1.x) > 0.f)
|
||||
{
|
||||
// P = p0 + t*(p1-p0) == (x,0)
|
||||
// p0.x + t*(p1.x-p0.x) == x
|
||||
// p0.y + t*(p1.y-p0.y) == 0
|
||||
// t == -p0.y / (p1.y - p0.y)
|
||||
|
||||
// Test whether the lines (0,0) -> (+inf,0) and p0 -> p1 intersect at a positive X-coordinate.
|
||||
Vector2 d = p1 - p0;
|
||||
if (d.y != 0.f)
|
||||
{
|
||||
float t = -p0.y / d.y;
|
||||
float x = p0.x + t * d.x;
|
||||
if (t >= 0.f && t <= 1.f)
|
||||
{
|
||||
// Remember how close the point was to the edge, for tracking robustness/goodness of the result.
|
||||
// If this is very large, then we can conclude that the point was contained or not contained in the face.
|
||||
faceContainmentDistance = std::min(faceContainmentDistance, std::abs(x));
|
||||
if (x >= 0.f)
|
||||
++numIntersections;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
// Positive return value: face contains the point. Negative: face did not contain the point.
|
||||
return (numIntersections % 2 == 1) ? faceContainmentDistance : -faceContainmentDistance;
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const LineSegment &lineSegment) const
|
||||
{
|
||||
return Contains(lineSegment.A) && Contains(lineSegment.B);
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const Triangle &triangle) const
|
||||
{
|
||||
return Contains(triangle.V0) && Contains(triangle.V1) && Contains(triangle.V2);
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const Polygon &polygon) const
|
||||
{
|
||||
for(int i = 0; i < polygon.NumVertices(); ++i)
|
||||
if (!Contains(polygon.Vertex(i)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const AABB &aabb) const
|
||||
{
|
||||
for(int i = 0; i < 8; ++i)
|
||||
if (!Contains(aabb.CornerPoint(i)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const OBB &obb) const
|
||||
{
|
||||
for(int i = 0; i < 8; ++i)
|
||||
if (!Contains(obb.CornerPoint(i)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const Frustum &frustum) const
|
||||
{
|
||||
for(int i = 0; i < 8; ++i)
|
||||
if (!Contains(frustum.CornerPoint(i)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::Contains(const Polyhedron &polyhedron) const
|
||||
{
|
||||
assert(polyhedron.IsClosed());
|
||||
for(int i = 0; i < polyhedron.NumVertices(); ++i)
|
||||
if (!Contains(polyhedron.Vertex(i)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::IsClosed() const {
|
||||
|
||||
}
|
||||
|
||||
Plane Polyhedron::FacePlane(int faceIndex) const
|
||||
{
|
||||
const Face &face = f[faceIndex];
|
||||
if (face.v.size() >= 3)
|
||||
return Plane(v[face.v[0]], v[face.v[1]], v[face.v[2]]);
|
||||
else if (face.v.size() == 2)
|
||||
return Plane(Line(v[face.v[0]], v[face.v[1]]), ((Vector3)v[face.v[0]]-(Vector3)v[face.v[1]]).Perpendicular());
|
||||
else if (face.v.size() == 1)
|
||||
return Plane(v[face.v[0]], Vector3(0,1,0));
|
||||
else
|
||||
return Plane();
|
||||
}
|
||||
|
||||
std::vector<Polygon> Polyhedron::Faces() const
|
||||
{
|
||||
std::vector<Polygon> faces;
|
||||
faces.reserve(NumFaces());
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
faces.push_back(FacePolygon(i));
|
||||
return faces;
|
||||
}
|
||||
|
||||
Vector4 PolyFaceNormal(const Polyhedron &poly, int faceIndex)
|
||||
{
|
||||
const Polyhedron::Face &face = poly.f[faceIndex];
|
||||
if (face.v.size() == 3)
|
||||
{
|
||||
Vector4 a = Vector4(poly.v[face.v[0]], 1.f);
|
||||
Vector4 b = Vector4(poly.v[face.v[1]], 1.f);
|
||||
Vector4 c = Vector4(poly.v[face.v[2]], 1.f);
|
||||
Vector4 normal = (b-a).Cross(c-a);
|
||||
normal.Normalize();
|
||||
return normal;
|
||||
// return ((vec)v[face.v[1]]-(vec)v[face.v[0]]).Cross((vec)v[face.v[2]]-(vec)v[face.v[0]]).Normalized();
|
||||
}
|
||||
else if (face.v.size() > 3)
|
||||
{
|
||||
// Use Newell's method of computing the face normal for best stability.
|
||||
// See Christer Ericson, Real-Time Collision Detection, pp. 491-495.
|
||||
Vector4 normal(0, 0, 0, 0);
|
||||
int v0 = face.v.back();
|
||||
for(size_t i = 0; i < face.v.size(); ++i)
|
||||
{
|
||||
int v1 = face.v[i];
|
||||
normal.x += (double(poly.v[v0].y) - poly.v[v1].y) * (double(poly.v[v0].z) + poly.v[v1].z); // Project on yz
|
||||
normal.y += (double(poly.v[v0].z) - poly.v[v1].z) * (double(poly.v[v0].x) + poly.v[v1].x); // Project on xz
|
||||
normal.z += (double(poly.v[v0].x) - poly.v[v1].x) * (double(poly.v[v0].y) + poly.v[v1].y); // Project on xy
|
||||
v0 = v1;
|
||||
}
|
||||
normal.Normalize();
|
||||
return normal;
|
||||
#if 0
|
||||
cv bestNormal;
|
||||
cs bestLen = -FLOAT_INF;
|
||||
cv a = poly.v[face.v[face.v.size()-2]];
|
||||
cv b = poly.v[face.v.back()];
|
||||
for(size_t i = 0; i < face.v.size()-2; ++i)
|
||||
{
|
||||
cv c = poly.v[face.v[i]];
|
||||
cv normal = (c-b).Cross(a-b);
|
||||
float len = normal.Normalize();
|
||||
if (len > bestLen)
|
||||
{
|
||||
bestLen = len;
|
||||
bestNormal = normal;
|
||||
}
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
assert(bestLen != -FLOAT_INF);
|
||||
return DIR_VEC((float)bestNormal.x, (float)bestNormal.y, (float)bestNormal.z);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Find the longest edge.
|
||||
cs bestLenSq = -FLOAT_INF;
|
||||
cv bestEdge;
|
||||
int v0 = face.v.back();
|
||||
int bestV0 = 0;
|
||||
int bestV1 = 0;
|
||||
for(size_t i = 0; i < face.v.size(); ++i)
|
||||
{
|
||||
int v1 = face.v[i];
|
||||
cv edge = cv(poly.v[v1]) - cv(poly.v[v0]);
|
||||
cs lenSq = edge.Normalize();
|
||||
if (lenSq > bestLenSq)
|
||||
{
|
||||
bestLenSq = lenSq;
|
||||
bestEdge = edge;
|
||||
bestV0 = v0;
|
||||
bestV1 = v1;
|
||||
}
|
||||
v0 = v1;
|
||||
}
|
||||
|
||||
cv bestNormal;
|
||||
cs bestLen = -FLOAT_INF;
|
||||
for(size_t i = 0; i < face.v.size(); ++i)
|
||||
{
|
||||
if (face.v[i] == bestV0 || face.v[i] == bestV1)
|
||||
continue;
|
||||
cv edge = cv(poly.v[i]) - cv(poly.v[bestV0]);
|
||||
edge.Normalize();
|
||||
cv normal = bestEdge.Cross(edge);
|
||||
cs len = normal.Normalize();
|
||||
if (len > bestLen)
|
||||
{
|
||||
bestLen = len;
|
||||
bestNormal = normal;
|
||||
}
|
||||
}
|
||||
assert(bestLen != -FLOAT_INF);
|
||||
return DIR_VEC((float)bestNormal.x, (float)bestNormal.y, (float)bestNormal.z);
|
||||
#endif
|
||||
}
|
||||
else if (face.v.size() == 2)
|
||||
return Vector4(Vector3(((Vector3)poly.v[face.v[1]]-(Vector3)poly.v[face.v[0]]).Cross(((Vector3)poly.v[face.v[0]]-(Vector3)poly.v[face.v[1]]).Perpendicular()-poly.v[face.v[0]]).TryNormalize()));
|
||||
else if (face.v.size() == 1)
|
||||
return Vector4(0, 1, 0, 0);
|
||||
else
|
||||
return Vector4::NaN;
|
||||
}
|
||||
|
||||
Vector3 Polyhedron::FaceNormal(int faceIndex) const
|
||||
{
|
||||
Vector4 normal = PolyFaceNormal(*this, faceIndex);
|
||||
return Vector3((float)normal.x, (float)normal.y, (float)normal.z);
|
||||
}
|
||||
|
||||
bool Polyhedron::IsConvex() const
|
||||
{
|
||||
// This function is O(n^2).
|
||||
/** @todo Real-Time Collision Detection, p. 64:
|
||||
A faster O(n) approach is to compute for each face F of P the centroid C of F,
|
||||
and for all neighboring faces G of F test if C lies behind the supporting plane of
|
||||
G. If some C fails to lie behind the supporting plane of one or more neighboring
|
||||
faces, P is concave, and is otherwise assumed convex. However, note that just as the
|
||||
corresponding polygonal convexity test may fail for a pentagram this test may fail for,
|
||||
for example, a pentagram extruded out of its plane and capped at the ends. */
|
||||
|
||||
float farthestD = -INFINITY;
|
||||
int numPointsOutside = 0;
|
||||
for(size_t i = 0; i < f.size(); ++i)
|
||||
{
|
||||
if (f[i].v.empty())
|
||||
continue;
|
||||
|
||||
Vector3 pointOnFace = v[f[i].v[0]];
|
||||
Vector3 faceNormal = FaceNormal((int)i);
|
||||
for(size_t j = 0; j < v.size(); ++j)
|
||||
{
|
||||
float d = faceNormal.Dot(Vector3(v[j]) - pointOnFace);
|
||||
if (d > 1e-2f)
|
||||
{
|
||||
if (d > farthestD)
|
||||
farthestD = d;
|
||||
++numPointsOutside;
|
||||
// LOGW("Distance of vertex %s/%d from face %s/%d: %f",
|
||||
// Vertex(j).ToString().c_str(), (int)j, FacePlane(i).ToString().c_str(), (int)i, d);
|
||||
// return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numPointsOutside > 0)
|
||||
{
|
||||
printf("%d point-planes are outside the face planes. Farthest is at distance %f!", numPointsOutside, farthestD);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::ContainsConvex(const Vector3 &point, float epsilon) const
|
||||
{
|
||||
assert(IsConvex());
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
if (FacePlane(i).SignedDistance(point) > epsilon)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyhedron::ContainsConvex(const LineSegment &lineSegment) const {
|
||||
return ContainsConvex(lineSegment.A) && ContainsConvex(lineSegment.B);
|
||||
}
|
||||
|
||||
bool Polyhedron::ContainsConvex(const Triangle &triangle) const
|
||||
{
|
||||
return ContainsConvex(triangle.V0) && ContainsConvex(triangle.V1) && ContainsConvex(triangle.V2);
|
||||
}
|
||||
|
||||
bool Polyhedron::FaceContains(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness) const
|
||||
{
|
||||
float faceContainmentDistance = FaceContainmentDistance2D(faceIndex, worldSpacePoint, polygonThickness);
|
||||
return faceContainmentDistance >= 0.f;
|
||||
}
|
||||
|
||||
bool Polyhedron::Intersects(const LineSegment &lineSegment) const
|
||||
{
|
||||
if (Contains(lineSegment))
|
||||
return true;
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
{
|
||||
float t;
|
||||
Plane plane = FacePlane(i);
|
||||
bool intersects = Plane::IntersectLinePlane(plane.Normal, plane.distance, lineSegment.A, lineSegment.B - lineSegment.A, t);
|
||||
if (intersects && t >= 0.f && t <= 1.f)
|
||||
if (FaceContains(i, lineSegment.GetPoint(t)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyhedron::Intersects(const Line &line) const
|
||||
{
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
if (FacePolygon(i).Intersects(line))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyhedron::Intersects(const Ray &ray) const
|
||||
{
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
if (FacePolygon(i).Intersects(ray))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyhedron::Intersects(const Plane &plane) const
|
||||
{
|
||||
return plane.Intersects(*this);
|
||||
}
|
||||
|
||||
Vector3 Polyhedron::ApproximateConvexCentroid() const
|
||||
{
|
||||
// Since this shape is convex, the averaged position of all vertices is inside this polyhedron.
|
||||
Vector3 arbitraryCenterVertex = Vector3::Zero;
|
||||
for(int i = 0; i < NumVertices(); ++i)
|
||||
arbitraryCenterVertex += Vertex(i);
|
||||
return arbitraryCenterVertex / (float)NumVertices();
|
||||
}
|
||||
|
||||
|
||||
/** The algorithm for Polyhedron-Polyhedron intersection is from Christer Ericson's Real-Time Collision Detection, p. 384.
|
||||
As noted by the author, the algorithm is very naive (and here unoptimized), and better methods exist. [groupSyntax] */
|
||||
bool Polyhedron::Intersects(const Polyhedron &polyhedron) const
|
||||
{
|
||||
Vector3 c = this->ApproximateConvexCentroid();
|
||||
if (polyhedron.Contains(c) && this->Contains(c))
|
||||
return true;
|
||||
c = polyhedron.ApproximateConvexCentroid();
|
||||
if (polyhedron.Contains(c) && this->Contains(c))
|
||||
return true;
|
||||
|
||||
// This test assumes that both this and the other polyhedron are closed.
|
||||
// This means that for each edge running through vertices i and j, there's a face
|
||||
// that contains the line segment (i,j) and another neighboring face that contains
|
||||
// the line segment (j,i). These represent the same line segment (but in opposite direction)
|
||||
// so we only have to test one of them for intersection. Take i < j as the canonical choice
|
||||
// and skip the other winding order.
|
||||
|
||||
// Test for each edge of this polyhedron whether the other polyhedron intersects it.
|
||||
for(size_t i = 0; i < f.size(); ++i)
|
||||
{
|
||||
assert(!f[i].v.empty()); // Cannot have degenerate faces here, and for performance reasons, don't start checking for this condition in release mode!
|
||||
int v0 = f[i].v.back();
|
||||
Vector3 l0 = v[v0];
|
||||
for(size_t j = 0; j < f[i].v.size(); ++j)
|
||||
{
|
||||
int v1 = f[i].v[j];
|
||||
Vector3 l1 = v[v1];
|
||||
if (v0 < v1 && polyhedron.Intersects(LineSegment(l0, l1))) // If v0 < v1, then this line segment is the canonical one.
|
||||
return true;
|
||||
l0 = l1;
|
||||
v0 = v1;
|
||||
}
|
||||
}
|
||||
|
||||
// Test for each edge of the other polyhedron whether this polyhedron intersects it.
|
||||
for(size_t i = 0; i < polyhedron.f.size(); ++i)
|
||||
{
|
||||
assert(!polyhedron.f[i].v.empty()); // Cannot have degenerate faces here, and for performance reasons, don't start checking for this condition in release mode!
|
||||
int v0 = polyhedron.f[i].v.back();
|
||||
Vector3 l0 = polyhedron.v[v0];
|
||||
for(size_t j = 0; j < polyhedron.f[i].v.size(); ++j)
|
||||
{
|
||||
int v1 = polyhedron.f[i].v[j];
|
||||
Vector3 l1 = polyhedron.v[v1];
|
||||
if (v0 < v1 && Intersects(LineSegment(l0, l1))) // If v0 < v1, then this line segment is the canonical one.
|
||||
return true;
|
||||
l0 = l1;
|
||||
v0 = v1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyhedron::Intersects(const Capsule &capsule) const {
|
||||
Vector3 pt, ptOnLineSegment;
|
||||
pt = ClosestPoint(capsule.l, &ptOnLineSegment);
|
||||
return pt.DistanceSq(ptOnLineSegment) <= capsule.r * capsule.r;
|
||||
}
|
||||
|
||||
Vector3 Polyhedron::ClosestPoint(const LineSegment& lineSegment, Vector3 *lineSegmentPt) const {
|
||||
if (Contains(lineSegment.A))
|
||||
{
|
||||
if (lineSegmentPt)
|
||||
*lineSegmentPt = lineSegment.A;
|
||||
return lineSegment.A;
|
||||
}
|
||||
if (Contains(lineSegment.B))
|
||||
{
|
||||
if (lineSegmentPt)
|
||||
*lineSegmentPt = lineSegment.B;
|
||||
return lineSegment.B;
|
||||
}
|
||||
Vector3 closestPt = Vector3::NaN;
|
||||
float closestDistance = FLT_MAX;
|
||||
Vector3 closestLineSegmentPt = Vector3::NaN;
|
||||
for(int i = 0; i < NumFaces(); ++i)
|
||||
{
|
||||
Vector3 lineSegPt;
|
||||
Vector3 pt = FacePolygon(i).ClosestPoint(lineSegment, &lineSegPt);
|
||||
float d = pt.DistanceSq(lineSegPt);
|
||||
if (d < closestDistance)
|
||||
{
|
||||
closestDistance = d;
|
||||
closestPt = pt;
|
||||
closestLineSegmentPt = lineSegPt;
|
||||
}
|
||||
}
|
||||
if (lineSegmentPt)
|
||||
*lineSegmentPt = closestLineSegmentPt;
|
||||
return closestPt;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,207 @@
|
||||
#include <J3ML/Geometry/Ray.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
|
||||
namespace Geometry
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
}
|
||||
void Ray::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
|
||||
{
|
||||
outMin = outMax = Vector3::Dot(direction, Origin);
|
||||
float d = Vector3::Dot(direction, Direction);
|
||||
|
||||
// Most of the time, the projection interval will be a half-infinite range, extending to either -inf or +inf.
|
||||
if (d > 1e-4f)
|
||||
outMax = INFINITY;
|
||||
else if (d < -1e4f)
|
||||
outMin = -INFINITY;
|
||||
}
|
||||
|
||||
RaycastResult Ray::Cast(const Sphere &target, float maxDistance)
|
||||
{
|
||||
Vector3 p0 = this->Origin;
|
||||
Vector3 d = this->Direction.Normalize();
|
||||
|
||||
Vector3 c = target.Position;
|
||||
float r = target.Radius;
|
||||
|
||||
Vector3 e = c - p0;
|
||||
// Using Length here would cause floating point error to creep in
|
||||
float Esq = Vector3::LengthSquared(e);
|
||||
float a = Vector3::Dot(e, d);
|
||||
float b = std::sqrt(Esq - (a*a));
|
||||
float f = std::sqrt((r*r) - (b*b));
|
||||
|
||||
float t = 0;
|
||||
// No collision
|
||||
if (r * r - Esq + a * a < 0.f)
|
||||
{
|
||||
t = -1;
|
||||
} else if ( Esq < r*r) {
|
||||
t = a + f;
|
||||
} else
|
||||
{
|
||||
t = a - f;
|
||||
}
|
||||
|
||||
// TODO: Verify this
|
||||
Vector3 intersection = p0.Project(d*t);
|
||||
Vector3 intersection_from_sphere_origin = (intersection - target.Position);
|
||||
|
||||
Vector3 normal = -intersection_from_sphere_origin.Normalize();
|
||||
|
||||
return RaycastResult{
|
||||
intersection,
|
||||
normal,
|
||||
true,
|
||||
(Shape *) &target
|
||||
};
|
||||
}
|
||||
|
||||
RaycastResult Ray::Cast(const AABB &target, float maxDistance) {
|
||||
float t1 = (target.minPoint.x - Origin.x) / Direction.x;
|
||||
float t2 = (target.maxPoint.x - Origin.x) / Direction.x;
|
||||
float t3 = (target.minPoint.y - Origin.y) / Direction.y;
|
||||
float t4 = (target.maxPoint.y - Origin.y) / Direction.y;
|
||||
float t5 = (target.minPoint.z - Origin.z) / Direction.z;
|
||||
float t6 = (target.maxPoint.z - Origin.z) / Direction.z;
|
||||
|
||||
float tmin = std::max( std::max( std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6));
|
||||
float tmax = std::min( std::min( std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6));
|
||||
|
||||
// if tmax < 0, ray is intersecting aabb, but whole aabb is behind us.
|
||||
if (tmax < 0)
|
||||
return RaycastResult::NoHit();
|
||||
|
||||
// if tmin > tmax, ray doesn't intersect AABB
|
||||
if (tmin > tmax)
|
||||
return RaycastResult::NoHit();
|
||||
|
||||
float t = 0.f;
|
||||
|
||||
if (tmin < 0.f)
|
||||
t = tmax;
|
||||
|
||||
t = tmin;
|
||||
|
||||
Vector3 p0 = this->Origin;
|
||||
Vector3 d = this->Direction.Normalize();
|
||||
|
||||
// TODO: Verify this
|
||||
Vector3 intersection = p0.Project(d*t);
|
||||
|
||||
// WTF: This algorithm is only valid for spheres!!!
|
||||
// TODO: Calculate surfacenormal against rectangle
|
||||
Vector3 intersection_from_sphere_origin = (intersection - target.Centroid());
|
||||
|
||||
Vector3 normal = -intersection_from_sphere_origin.Normalize();
|
||||
|
||||
return RaycastResult
|
||||
{
|
||||
intersection,
|
||||
normal,
|
||||
true,
|
||||
(Shape*)&target
|
||||
};
|
||||
}
|
||||
|
||||
RaycastResult Ray::Cast(const Plane &target, float maxDistance) {
|
||||
float nd = Vector3::Dot(Direction, target.Normal);
|
||||
float pn = Vector3::Dot(Origin, target.Normal);
|
||||
|
||||
if (nd >= 0.f)
|
||||
return RaycastResult::NoHit();
|
||||
|
||||
float t = (target.distance - pn) / nd;
|
||||
|
||||
Vector3 d = this->Direction.Normalize();
|
||||
|
||||
// TODO: verify this
|
||||
Vector3 intersection = this->Origin.Project(d*t);
|
||||
|
||||
// TODO: flip the axis based on direction of incoming ray?
|
||||
// Take dot product
|
||||
Vector3 normal = target.Normal;
|
||||
|
||||
if (t >= 0.f)
|
||||
return RaycastResult
|
||||
{
|
||||
intersection,
|
||||
normal,
|
||||
true,
|
||||
(Shape*) &target
|
||||
};
|
||||
|
||||
return RaycastResult::NoHit();
|
||||
}
|
||||
|
||||
Vector3 Ray::ClosestPoint(const Vector3 &targetPoint, float &d) const
|
||||
{
|
||||
d = std::max(0.f, Vector3::Dot(targetPoint - Origin, Direction));
|
||||
return GetPoint(d);
|
||||
}
|
||||
|
||||
Vector3 Ray::ClosestPoint(const LineSegment &other, float &d, float &d2) const {
|
||||
Line::ClosestPointLineLine(Origin, Direction, other.A, other.B - other.A, d, d2);
|
||||
if (d < 0.f)
|
||||
{
|
||||
d = 0.f;
|
||||
if (d2 >= 0.f && d2 <= 1.f)
|
||||
{
|
||||
other.ClosestPoint(Origin, d2);
|
||||
return Origin;
|
||||
}
|
||||
|
||||
Vector3 p;
|
||||
float t2;
|
||||
|
||||
if (d2 < 0.f)
|
||||
{
|
||||
p = other.A;
|
||||
t2 = 0.f;
|
||||
}
|
||||
else // u2 > 1.f
|
||||
{
|
||||
p = other.B;
|
||||
t2 = 1.f;
|
||||
}
|
||||
|
||||
Vector3 closestPoint = ClosestPoint(p, d);
|
||||
Vector3 closestPoint2 = other.ClosestPoint(Origin, d2);
|
||||
if (closestPoint.DistanceSquared(p) <= closestPoint2.DistanceSquared(Origin))
|
||||
{
|
||||
d2 = t2;
|
||||
return closestPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
d = 0.f;
|
||||
return Origin;
|
||||
}
|
||||
}
|
||||
else if (d2 < 0.f)
|
||||
{
|
||||
d2 = 0.f;
|
||||
return ClosestPoint(other.A, d);
|
||||
}
|
||||
else if (d2 > 1.f)
|
||||
{
|
||||
d2 = 1.f;
|
||||
return ClosestPoint(other.B, d);
|
||||
}
|
||||
else
|
||||
return GetPoint(d);
|
||||
}
|
||||
|
||||
Ray::Ray(const Vector3 &pos, const Vector3 &dir) {
|
||||
Origin = pos;
|
||||
Direction = dir;
|
||||
}
|
||||
|
||||
Vector3 Ray::GetPoint(float distance) const {
|
||||
assert(Direction.IsNormalized());
|
||||
return Origin + distance * Direction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1 +1,28 @@
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
#include <J3ML/Geometry/Sphere.h>
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
bool Sphere::Contains(const LineSegment &lineseg) const {
|
||||
|
||||
}
|
||||
|
||||
void Sphere::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
|
||||
{
|
||||
float d = Vector3::Dot(direction, Position);
|
||||
outMin = d - Radius;
|
||||
outMax = d + Radius;
|
||||
}
|
||||
|
||||
Vector3 Sphere::ExtremePoint(const Vector3 &direction) const {
|
||||
float len = direction.Length();
|
||||
assert(len > 0.f);
|
||||
return Position + direction * (Radius / len);
|
||||
}
|
||||
|
||||
Vector3 Sphere::ExtremePoint(const Vector3 &direction, float &projectionDistance) const {
|
||||
Vector3 extremePoint = ExtremePoint(direction);
|
||||
projectionDistance = extremePoint.Dot(direction);
|
||||
return extremePoint;
|
||||
}
|
||||
}
|
414
src/J3ML/Geometry/Triangle.cpp
Normal file
414
src/J3ML/Geometry/Triangle.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
#include <J3ML/Geometry/Triangle.h>
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include <J3ML/Geometry/AABB.h>
|
||||
#include <J3ML/Geometry/LineSegment.h>
|
||||
#include <J3ML/Geometry/Line.h>
|
||||
#include <J3ML/Geometry/Capsule.h>
|
||||
|
||||
|
||||
namespace J3ML::Geometry
|
||||
{
|
||||
|
||||
LineSegment Triangle::Edge(int i) const
|
||||
{
|
||||
assert(0 <= i);
|
||||
assert(i <= 2);
|
||||
if (i == 0)
|
||||
return LineSegment(V0, V1);
|
||||
else if (i == 1)
|
||||
return LineSegment(V1, V2);
|
||||
else if (i == 2)
|
||||
return LineSegment(V2, V0);
|
||||
else
|
||||
return LineSegment(Vector3::NaN, Vector3::NaN);
|
||||
}
|
||||
|
||||
Vector3 Triangle::Vertex(int i) const
|
||||
{
|
||||
assert(0 <= i);
|
||||
assert(i <= 2);
|
||||
if (i == 0)
|
||||
return V0;
|
||||
else if (i == 1)
|
||||
return V1;
|
||||
else if (i == 2)
|
||||
return V2;
|
||||
else
|
||||
return Vector3::NaN;
|
||||
}
|
||||
|
||||
Plane Triangle::PlaneCCW() const
|
||||
{
|
||||
return Plane(V0, V1, V2);
|
||||
}
|
||||
|
||||
Plane Triangle::PlaneCW() const
|
||||
{
|
||||
return Plane(V0, V1, V2);
|
||||
}
|
||||
|
||||
void Triangle::ProjectToAxis(const Vector3 &axis, float &dMin, float &dMax) const
|
||||
{
|
||||
dMin = dMax = Vector3::Dot(axis, V0);
|
||||
float t = Vector3::Dot(axis, V1);
|
||||
dMin = std::min(t, dMin);
|
||||
dMax = std::max(t, dMax);
|
||||
t = Vector3::Dot(axis, V2);
|
||||
dMin = std::min(t, dMin);
|
||||
dMax = std::max(t, dMax);
|
||||
}
|
||||
|
||||
AABB Triangle::BoundingAABB() const {
|
||||
AABB aabb;
|
||||
aabb.minPoint = Vector3::Min(V0, V1, V2);
|
||||
aabb.maxPoint = Vector3::Max(V0, V1, V2);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
bool Triangle::Intersects(const AABB &aabb) const {
|
||||
return aabb.Intersects(*this);
|
||||
}
|
||||
|
||||
/** Calculates the intersection between a line and a triangle. The facing is not accounted for, so
|
||||
rays are reported to intersect triangles that are both front and backfacing.
|
||||
According to "T. Möller, B. Trumbore. Fast, Minimum Storage Ray/Triangle Intersection. 2005."
|
||||
http://jgt.akpeters.com/papers/MollerTrumbore97/
|
||||
@param linePos The starting point of the line.
|
||||
@param lineDir The direction vector of the line. This does not need to be normalized.
|
||||
@param v0 Vertex 0 of the triangle.
|
||||
@param v1 Vertex 1 of the triangle.
|
||||
@param v2 Vertex 2 of the triangle.
|
||||
@param u [out] The barycentric u coordinate is returned here if an intersection occurred.
|
||||
@param v [out] The barycentric v coordinate is returned here if an intersection occurred.
|
||||
@return The distance along the ray to the point of intersection, or +inf if no intersection occurred.
|
||||
If no intersection, then u and v and t will contain undefined values. If lineDir was not normalized, then to get the
|
||||
real world-space distance, one must scale the returned value with lineDir.Length(). If the returned value is negative,
|
||||
then the intersection occurs 'behind' the line starting position, with respect to the direction vector lineDir. */
|
||||
float Triangle::IntersectLineTri(const Vector3 &linePos, const Vector3 &lineDir,
|
||||
const Vector3 &v0, const Vector3 &v1, const Vector3 &v2,
|
||||
float &u, float &v)
|
||||
{
|
||||
const float epsilon = 1e-4f;
|
||||
|
||||
// Edge vectors
|
||||
Vector3 vE1 = v1 - v0;
|
||||
Vector3 vE2 = v2 - v0;
|
||||
|
||||
// begin calculating determinant - also used to calculate U parameter
|
||||
Vector3 vP = lineDir.Cross(vE2);
|
||||
|
||||
// If det < 0, intersecting backfacing tri, > 0, intersecting frontfacing tri, 0, parallel to plane.
|
||||
const float det = vE1.Dot(vP);
|
||||
|
||||
// If determinant is near zero, ray lies in plane of triangle.
|
||||
if (std::abs(det) <= epsilon)
|
||||
return INFINITY;
|
||||
const float recipDet = 1.f / det;
|
||||
|
||||
// Calculate distance from v0 to ray origin
|
||||
Vector3 vT = linePos - v0;
|
||||
|
||||
// Output barycentric u
|
||||
u = vT.Dot(vP) * recipDet;
|
||||
if (u < -epsilon || u > 1.f + epsilon)
|
||||
return INFINITY; // Barycentric U is outside the triangle - early out.
|
||||
|
||||
// Prepare to test V parameter
|
||||
Vector3 vQ = vT.Cross(vE1);
|
||||
|
||||
// Output barycentric v
|
||||
v = lineDir.Dot(vQ) * recipDet;
|
||||
if (v < -epsilon || u + v > 1.f + epsilon) // Barycentric V or the combination of U and V are outside the triangle - no intersection.
|
||||
return INFINITY;
|
||||
|
||||
// Barycentric u and v are in limits, the ray intersects the triangle.
|
||||
|
||||
// Output signed distance from ray to triangle.
|
||||
return vE2.Dot(vQ) * recipDet;
|
||||
// return (det < 0.f) ? IntersectBackface : IntersectFrontface;
|
||||
}
|
||||
|
||||
Vector3 Triangle::ClosestPointToTriangleEdge(const Line &other, float *outU, float *outV, float *outD) const
|
||||
{
|
||||
///@todo Optimize!
|
||||
// The line is parallel to the triangle.
|
||||
float unused1, unused2, unused3;
|
||||
float d1, d2, d3;
|
||||
Vector3 pt1 = Edge(0).ClosestPoint(other, unused1, d1);
|
||||
Vector3 pt2 = Edge(1).ClosestPoint(other, unused2, d2);
|
||||
Vector3 pt3 = Edge(2).ClosestPoint(other, unused3, d3);
|
||||
float dist1 = pt1.DistanceSq(other.GetPoint(d1));
|
||||
float dist2 = pt2.DistanceSq(other.GetPoint(d2));
|
||||
float dist3 = pt3.DistanceSq(other.GetPoint(d3));
|
||||
if (dist1 <= dist2 && dist1 <= dist3)
|
||||
{
|
||||
if (outU) *outU = BarycentricUV(pt1).x;
|
||||
if (outV) *outV = BarycentricUV(pt1).y;
|
||||
if (outD) *outD = d1;
|
||||
return pt1;
|
||||
}
|
||||
else if (dist2 <= dist3)
|
||||
{
|
||||
if (outU) *outU = BarycentricUV(pt2).x;
|
||||
if (outV) *outV = BarycentricUV(pt2).y;
|
||||
if (outD) *outD = d2;
|
||||
return pt2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outU) *outU = BarycentricUV(pt3).x;
|
||||
if (outV) *outV = BarycentricUV(pt3).y;
|
||||
if (outD) *outD = d3;
|
||||
return pt3;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Triangle::ClosestPointToTriangleEdge(const LineSegment &lineSegment, float *outU, float *outV, float *outD) const
|
||||
{
|
||||
///@todo Optimize!
|
||||
// The line is parallel to the triangle.
|
||||
float unused1, unused2, unused3;
|
||||
float d1, d2, d3;
|
||||
Vector3 pt1 = Edge(0).ClosestPoint(lineSegment, unused1, d1);
|
||||
Vector3 pt2 = Edge(1).ClosestPoint(lineSegment, unused2, d2);
|
||||
Vector3 pt3 = Edge(2).ClosestPoint(lineSegment, unused3, d3);
|
||||
float dist1 = pt1.DistanceSq(lineSegment.GetPoint(d1));
|
||||
float dist2 = pt2.DistanceSq(lineSegment.GetPoint(d2));
|
||||
float dist3 = pt3.DistanceSq(lineSegment.GetPoint(d3));
|
||||
if (dist1 <= dist2 && dist1 <= dist3)
|
||||
{
|
||||
if (outU) *outU = BarycentricUV(pt1).x;
|
||||
if (outV) *outV = BarycentricUV(pt1).y;
|
||||
if (outD) *outD = d1;
|
||||
return pt1;
|
||||
}
|
||||
else if (dist2 <= dist3)
|
||||
{
|
||||
if (outU) *outU = BarycentricUV(pt2).x;
|
||||
if (outV) *outV = BarycentricUV(pt2).y;
|
||||
if (outD) *outD = d2;
|
||||
return pt2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outU) *outU = BarycentricUV(pt3).x;
|
||||
if (outV) *outV = BarycentricUV(pt3).y;
|
||||
if (outD) *outD = d3;
|
||||
return pt3;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Triangle::ClosestPoint(const Vector3 &p) const
|
||||
{
|
||||
/** The code for Triangle-float3 test is from Christer Ericson's Real-Time Collision Detection, pp. 141-142. */
|
||||
|
||||
// Check if P is in vertex region outside A.
|
||||
Vector3 ab = V1 - V0;
|
||||
Vector3 ac = V2 - V0;
|
||||
Vector3 ap = p - V0;
|
||||
float d1 = Vector3::Dot(ab, ap);
|
||||
float d2 = Vector3::Dot(ac, ap);
|
||||
if (d1 <= 0.f && d2 <= 0.f)
|
||||
return V0; // Barycentric coordinates are (1,0,0).
|
||||
|
||||
// Check if P is in vertex region outside B.
|
||||
Vector3 bp = p - V1;
|
||||
float d3 = Vector3::Dot(ab, bp);
|
||||
float d4 = Vector3::Dot(ac, bp);
|
||||
if (d3 >= 0.f && d4 <= d3)
|
||||
return V1; // Barycentric coordinates are (0,1,0).
|
||||
|
||||
// Check if P is in edge region of AB, and if so, return the projection of P onto AB.
|
||||
float vc = d1*d4 - d3*d2;
|
||||
if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f)
|
||||
{
|
||||
float v = d1 / (d1 - d3);
|
||||
return V0 + v * ab; // The barycentric coordinates are (1-v, v, 0).
|
||||
}
|
||||
|
||||
// Check if P is in vertex region outside C.
|
||||
Vector3 cp = p - V2;
|
||||
float d5 = Vector3::Dot(ab, cp);
|
||||
float d6 = Vector3::Dot(ac, cp);
|
||||
if (d6 >= 0.f && d5 <= d6)
|
||||
return V2; // The barycentric coordinates are (0,0,1).
|
||||
|
||||
// Check if P is in edge region of AC, and if so, return the projection of P onto AC.
|
||||
float vb = d5*d2 - d1*d6;
|
||||
if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f)
|
||||
{
|
||||
float w = d2 / (d2 - d6);
|
||||
return V0 + w * ac; // The barycentric coordinates are (1-w, 0, w).
|
||||
}
|
||||
|
||||
// Check if P is in edge region of BC, and if so, return the projection of P onto BC.
|
||||
float va = d3*d6 - d5*d4;
|
||||
if (va <= 0.f && d4 - d3 >= 0.f && d5 - d6 >= 0.f)
|
||||
{
|
||||
float w = (d4 - d3) / (d4 - d3 + d5 - d6);
|
||||
return V1 + w * (V2 - V0); // The barycentric coordinates are (0, 1-w, w).
|
||||
}
|
||||
|
||||
// P must be inside the face region. Compute the closest point through its barycentric coordinates (u,v,w).
|
||||
float denom = 1.f / (va + vb + vc);
|
||||
float v = vb * denom;
|
||||
float w = vc * denom;
|
||||
return V0 + ab * v + ac * w;
|
||||
}
|
||||
|
||||
Vector3 Triangle::ClosestPoint(const LineSegment& lineSegment, Vector3 *otherPt) const
|
||||
{
|
||||
///\todo Optimize.
|
||||
float u, v;
|
||||
float t = IntersectLineTri(lineSegment.A, lineSegment.B - lineSegment.A, V0, V1, V2, u, v);
|
||||
bool intersects = (t >= 0.0f && t <= 1.0f);
|
||||
if (intersects)
|
||||
{
|
||||
// assume3(lineSegment.GetPoint(t).Equals(this->Point(u, v)), lineSegment.GetPoint(t).SerializeToCodeString(), this->Point(u, v).SerializeToCodeString(), lineSegment.GetPoint(t).Distance(this->Point(u, v)));
|
||||
if (otherPt)
|
||||
*otherPt = lineSegment.GetPoint(t);
|
||||
return this->Point(u, v);
|
||||
}
|
||||
|
||||
float u1,v1,d1;
|
||||
Vector3 pt1 = ClosestPointToTriangleEdge(lineSegment, &u1, &v1, &d1);
|
||||
|
||||
Vector3 pt2 = ClosestPoint(lineSegment.A);
|
||||
Vector3 pt3 = ClosestPoint(lineSegment.B);
|
||||
|
||||
float D1 = pt1.DistanceSq(lineSegment.GetPoint(d1));
|
||||
float D2 = pt2.DistanceSq(lineSegment.A);
|
||||
float D3 = pt3.DistanceSq(lineSegment.B);
|
||||
|
||||
if (D1 <= D2 && D1 <= D3)
|
||||
{
|
||||
if (otherPt)
|
||||
*otherPt = lineSegment.GetPoint(d1);
|
||||
return pt1;
|
||||
}
|
||||
else if (D2 <= D3)
|
||||
{
|
||||
if (otherPt)
|
||||
*otherPt = lineSegment.A;
|
||||
return pt2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (otherPt)
|
||||
*otherPt = lineSegment.B;
|
||||
return pt3;
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation from Christer Ericson's Real-Time Collision Detection, pp. 51-52.
|
||||
inline float TriArea2D(float x1, float y1, float x2, float y2, float x3, float y3)
|
||||
{
|
||||
return (x1-x2)*(y2-y3) - (x2-x3)*(y1-y2);
|
||||
}
|
||||
|
||||
|
||||
Vector3 Triangle::BarycentricUVW(const Vector3 &point) const {
|
||||
// Implementation from Christer Ericson's Real-Time Collision Detection, pp. 51-52.
|
||||
|
||||
// Unnormalized triangle normal.
|
||||
Vector3 m = Vector3::Cross(V1-V0, V2-V1);
|
||||
|
||||
// Nominators and one-over-denominator for u and v ratios.
|
||||
float nu, nv, ood;
|
||||
|
||||
// Absolute components for determining projection plane.
|
||||
float x = std::abs(m.x);
|
||||
float y = std::abs(m.y);
|
||||
float z = std::abs(m.z);
|
||||
|
||||
if (x >= y && x >= z)
|
||||
{
|
||||
// Project to the yz plane.
|
||||
nu = TriArea2D(point.y, point.z, V1.y, V1.z, V2.y, V2.z); // Area of PBC in yz-plane.
|
||||
nv = TriArea2D(point.y, point.z, V2.y, V2.z, V0.y, V0.z); // Area OF PCA in yz-plane.
|
||||
ood = 1.f / m.x; // 1 / (2*area of ABC in yz plane)
|
||||
}
|
||||
else if (y >= z) // Note: The book has a redundant 'if (y >= x)' comparison
|
||||
{
|
||||
// y is largest, project to the xz-plane.
|
||||
nu = TriArea2D(point.x, point.z, V1.x, V1.z, V2.x, V2.z);
|
||||
nv = TriArea2D(point.x, point.z, V2.x, V2.z, V0.x, V0.z);
|
||||
ood = 1.f / -m.y;
|
||||
}
|
||||
else // z is largest, project to the xy-plane.
|
||||
{
|
||||
nu = TriArea2D(point.x, point.y, V1.x, V1.y, V2.x, V2.y);
|
||||
nv = TriArea2D(point.x, point.y, V2.x, V2.y, V0.x, V0.y);
|
||||
ood = 1.f / m.z;
|
||||
}
|
||||
float u = nu * ood;
|
||||
float v = nv * ood;
|
||||
float w = 1.f - u - v;
|
||||
return Vector3(u,v,w);
|
||||
}
|
||||
|
||||
bool Triangle::Intersects(const Capsule &capsule) const {
|
||||
return capsule.Intersects(*this);
|
||||
}
|
||||
|
||||
Vector3 Triangle::ExtremePoint(const Vector3 &direction) const {
|
||||
Vector3 mostExtreme = Vector3::NaN;
|
||||
float mostExtremeDist = -FLT_MAX;
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
Vector3 pt = Vertex(i);
|
||||
float d = Vector3::Dot(direction, pt);
|
||||
if (d > mostExtremeDist)
|
||||
{
|
||||
mostExtremeDist = d;
|
||||
mostExtreme = pt;
|
||||
}
|
||||
}
|
||||
return mostExtreme;
|
||||
}
|
||||
|
||||
Vector3 Triangle::ExtremePoint(const Vector3 &direction, float &projectionDistance) const {
|
||||
Vector3 extremePoint = ExtremePoint(direction);
|
||||
projectionDistance = extremePoint.Dot(direction);
|
||||
return extremePoint;
|
||||
}
|
||||
|
||||
float Triangle::DistanceSq(const Vector3 &point) const {
|
||||
return ClosestPoint(point).DistanceSq(point);
|
||||
}
|
||||
|
||||
Vector2 Triangle::BarycentricUV(const Vector3 &point) const {
|
||||
Vector3 uvw = BarycentricUVW(point);
|
||||
return Vector2{uvw.y, uvw.z};
|
||||
}
|
||||
|
||||
Vector3 Triangle::Point(const Vector2 &uv) const {
|
||||
return Point(uv.x, uv.y);
|
||||
}
|
||||
|
||||
Vector3 Triangle::Point(float u, float v) const {
|
||||
// In case the triangle is far away from the origin but is small in size, the elements of 'a' will have large magnitudes,
|
||||
// and the elements of (b-a) and (c-a) will be much smaller quantities. Therefore be extra careful with the
|
||||
// parentheses and first sum the small floats together before adding it to the large one.
|
||||
return V0 + ((V1-V0) * u + (V2-V0) * v);
|
||||
}
|
||||
|
||||
Vector3 Triangle::Point(const Vector3 &uvw) const {
|
||||
return Point(uvw.x, uvw.y, uvw.z);
|
||||
}
|
||||
|
||||
Vector3 Triangle::Point(float u, float v, float w) const {
|
||||
return u * V0 + v * V1 + w * V2;
|
||||
}
|
||||
|
||||
bool Triangle::Contains(const Vector3 &point, float triangleThicknessSq) const {
|
||||
Vector3 normal = (V1-V0).Cross(V2-V0);
|
||||
float lenSq = normal.LengthSquared();
|
||||
float d = normal.Dot(V1 - point);
|
||||
if (d*d > triangleThicknessSq * lenSq)
|
||||
return false; ///@todo The plane-point distance test is omitted in Real-Time Collision Detection. p. 25. A bug in the book?
|
||||
|
||||
Vector3 br = BarycentricUVW(point);
|
||||
return br.x >= -1e-3f && br.y >= -1e-3f && br.z >= -1e-3f; // Allow for a small epsilon to properly account for points very near the edges of the triangle.
|
||||
}
|
||||
}
|
52
src/J3ML/J3ML.cpp
Normal file
52
src/J3ML/J3ML.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <J3ML/J3ML.h>
|
||||
|
||||
|
||||
namespace J3ML
|
||||
{
|
||||
|
||||
Math::Rotation Math::operator ""_degrees(long double rads) { return {Radians((float)rads)}; }
|
||||
|
||||
Math::Rotation Math::operator ""_deg(long double rads) { return {Radians((float)rads)}; }
|
||||
|
||||
Math::Rotation Math::operator ""_radians(long double rads) { return {(float)rads}; }
|
||||
|
||||
Math::Rotation Math::operator ""_rad(long double rads) { return {(float)rads}; }
|
||||
|
||||
float Math::FastRSqrt(float x) {
|
||||
return 1.f / FastSqrt(x);
|
||||
}
|
||||
|
||||
float Math::RSqrt(float x) {
|
||||
return 1.f / Sqrt(x);
|
||||
}
|
||||
|
||||
float Math::Radians(float degrees) { return degrees * (Pi/180.f); }
|
||||
|
||||
float Math::Degrees(float radians) { return radians * (180.f/Pi); }
|
||||
|
||||
bool Math::EqualAbs(float a, float b, float epsilon) {
|
||||
return std::abs(a - b) < epsilon;
|
||||
}
|
||||
|
||||
float Math::RecipFast(float x) {
|
||||
// TODO: Implement SSE rcp instruction.
|
||||
return 1.f / x;
|
||||
}
|
||||
|
||||
Math::Rotation::Rotation() : valueInRadians(0) {}
|
||||
|
||||
Math::Rotation::Rotation(float value) : valueInRadians(value) {}
|
||||
|
||||
Math::Rotation Math::Rotation::operator+(const Math::Rotation &rhs) {
|
||||
valueInRadians += rhs.valueInRadians;
|
||||
}
|
||||
|
||||
float Math::Interp::SmoothStart(float t) {
|
||||
assert(t >= 0.f && t <= 1.f);
|
||||
return t*t;
|
||||
}
|
||||
|
||||
int Math::BitTwiddling::CountBitsSet(u32 value) {
|
||||
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#include <J3ML/LinearAlgebra.h>
|
||||
#include "J3ML/LinearAlgebra.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#include <J3ML/LinearAlgebra/AxisAngle.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
AxisAngle::AxisAngle() : axis(Vector3::Zero) {}
|
||||
|
||||
|
@@ -3,50 +3,49 @@
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#pragma region EulerAngle
|
||||
namespace LinearAlgebra {
|
||||
EulerAngle::EulerAngle(float pitch, float yaw, float roll): pitch(pitch), yaw(yaw), roll(roll)
|
||||
{}
|
||||
namespace J3ML::LinearAlgebra {
|
||||
EulerAngle::EulerAngle(float pitch, float yaw, float roll): pitch(pitch), yaw(yaw), roll(roll)
|
||||
{}
|
||||
|
||||
float EulerAngle::GetPitch(float pitch_limit) const
|
||||
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
|
||||
float EulerAngle::GetPitch(float pitch_limit) const
|
||||
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
|
||||
|
||||
float EulerAngle::GetYaw(float yaw_limit) const
|
||||
{ return std::clamp(std::remainderf(yaw, 360.f), -yaw_limit, yaw_limit); }
|
||||
float EulerAngle::GetYaw(float yaw_limit) const
|
||||
{ return std::clamp(std::remainderf(yaw, 360.f), -yaw_limit, yaw_limit); }
|
||||
|
||||
float EulerAngle::GetRoll(float pitch_limit) const
|
||||
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
|
||||
float EulerAngle::GetRoll(float pitch_limit) const
|
||||
{ return std::clamp( std::remainderf(pitch,360.f), -pitch_limit, pitch_limit); }
|
||||
|
||||
bool EulerAngle::operator==(const EulerAngle& a) const
|
||||
{
|
||||
return (pitch == a.pitch) && (yaw == a.yaw) && (roll == a.roll);
|
||||
}
|
||||
bool EulerAngle::operator==(const EulerAngle& a) const
|
||||
{
|
||||
return (pitch == a.pitch) && (yaw == a.yaw) && (roll == a.roll);
|
||||
}
|
||||
|
||||
void EulerAngle::clamp()
|
||||
{
|
||||
if (this->pitch > 89.0f)
|
||||
this->pitch = 89.0f;
|
||||
if (this->pitch <= -89.0f)
|
||||
this->pitch = -89.0f;
|
||||
//TODO: Make this entirely seamless by getting the amount they rotated passed -180 and +180 by.
|
||||
if (this->yaw <= -180.0f)
|
||||
this->yaw = 180.0f;
|
||||
if (this->yaw >= 180.01f)
|
||||
this->yaw = -179.9f;
|
||||
if (this->roll >= 360.0f)
|
||||
this->roll = 0.0;
|
||||
if (this->roll <= -360.0f)
|
||||
this->roll = 0.0;
|
||||
}
|
||||
void EulerAngle::clamp()
|
||||
{
|
||||
if (this->pitch > 89.0f)
|
||||
this->pitch = 89.0f;
|
||||
if (this->pitch <= -89.0f)
|
||||
this->pitch = -89.0f;
|
||||
//TODO: Make this entirely seamless by getting the amount they rotated passed -180 and +180 by.
|
||||
if (this->yaw <= -180.0f)
|
||||
this->yaw = 180.0f;
|
||||
if (this->yaw >= 180.01f)
|
||||
this->yaw = -179.9f;
|
||||
if (this->roll >= 360.0f)
|
||||
this->roll = 0.0;
|
||||
if (this->roll <= -360.0f)
|
||||
this->roll = 0.0;
|
||||
}
|
||||
|
||||
EulerAngle EulerAngle::movementAngle() const
|
||||
{
|
||||
EulerAngle a;
|
||||
a.pitch = (cos(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
|
||||
a.yaw = -sin(Math::Radians(pitch));
|
||||
a.roll = (sin(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
|
||||
return a;
|
||||
}
|
||||
EulerAngle EulerAngle::movementAngle() const
|
||||
{
|
||||
EulerAngle a;
|
||||
a.pitch = (cos(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
|
||||
a.yaw = -sin(Math::Radians(pitch));
|
||||
a.roll = (sin(Math::Radians(yaw)) * cos(Math::Radians(pitch)));
|
||||
return a;
|
||||
}
|
||||
|
||||
EulerAngle::EulerAngle() : pitch(0), yaw(0), roll(0) {}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#include <J3ML/LinearAlgebra/Matrix2x2.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
Vector2 Matrix2x2::GetRow(int index) const {
|
||||
float x = this->elems[index][0];
|
||||
@@ -18,4 +18,8 @@ namespace LinearAlgebra {
|
||||
float Matrix2x2::At(int x, int y) const {
|
||||
return this->elems[x][y];
|
||||
}
|
||||
|
||||
float &Matrix2x2::At(int x, int y) {
|
||||
return this->elems[x][y];
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
const Matrix3x3 Matrix3x3::Zero = Matrix3x3(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
const Matrix3x3 Matrix3x3::Identity = Matrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);
|
||||
@@ -313,16 +313,148 @@ namespace LinearAlgebra {
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::FromRS(const Quaternion &rotate, const Vector3 &scale) {
|
||||
return Matrix3x3(rotate) * Matrix3x3::Scale(scale);
|
||||
return Matrix3x3(rotate) * Matrix3x3::FromScale(scale);
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::FromRS(const Matrix3x3 &rotate, const Vector3 &scale) {
|
||||
return rotate * Matrix3x3::Scale(scale);
|
||||
return rotate * Matrix3x3::FromScale(scale);
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::FromQuat(const Quaternion &orientation) {
|
||||
return Matrix3x3(orientation);
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::ScaleBy(const Vector3 &rhs) {
|
||||
return *this * FromScale(rhs);
|
||||
}
|
||||
|
||||
Vector3 Matrix3x3::GetScale() const {
|
||||
return Vector3(GetColumn(0).Length(), GetColumn(1).Length(), GetColumn(2).Length());
|
||||
}
|
||||
|
||||
Vector3 Matrix3x3::operator[](int row) const {
|
||||
return Vector3{elems[row][0], elems[row][1], elems[row][2]};
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::FromScale(const Vector3 &scale) {
|
||||
Matrix3x3 m;
|
||||
m.At(0,0) = scale.x;
|
||||
m.At(1,1) = scale.y;
|
||||
m.At(2,2) = scale.z;
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::FromScale(float sx, float sy, float sz) {
|
||||
Matrix3x3 m;
|
||||
m.At(0,0) = sx;
|
||||
m.At(1,1) = sy;
|
||||
m.At(2,2) = sz;
|
||||
return m;
|
||||
}
|
||||
|
||||
void Matrix3x3::Orthonormalize(int c0, int c1, int c2) {
|
||||
Vector3 v0 = GetColumn(c0);
|
||||
Vector3 v1 = GetColumn(c1);
|
||||
Vector3 v2 = GetColumn(c2);
|
||||
Vector3::Orthonormalize(v0, v1, v2);
|
||||
SetColumn(c0, v0);
|
||||
SetColumn(c1, v1);
|
||||
SetColumn(c2, v2);
|
||||
}
|
||||
|
||||
Matrix3x3::Matrix3x3(const float *data) {
|
||||
assert(data);
|
||||
At(0, 0) = data[0];
|
||||
At(0, 1) = data[1];
|
||||
At(0, 2) = data[2];
|
||||
|
||||
At(1, 0) = data[4];
|
||||
At(1, 1) = data[5];
|
||||
At(1, 2) = data[6];
|
||||
|
||||
At(2, 0) = data[8];
|
||||
At(2, 1) = data[9];
|
||||
At(2, 2) = data[10];
|
||||
}
|
||||
|
||||
Vector4 Matrix3x3::Mul(const Vector4 &rhs) const {
|
||||
return {Mul(rhs.XYZ()), rhs.GetW()};
|
||||
}
|
||||
|
||||
Vector3 Matrix3x3::Mul(const Vector3 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
Vector2 Matrix3x3::Mul(const Vector2 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix3x3::Mul(const Matrix4x4 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
Matrix3x3 Matrix3x3::Mul(const Matrix3x3 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
bool Matrix3x3::IsRowOrthogonal(float epsilon) const
|
||||
{
|
||||
return GetRow(0).IsPerpendicular(GetRow(1), epsilon)
|
||||
&& GetRow(0).IsPerpendicular(GetRow(2), epsilon)
|
||||
&& GetRow(1).IsPerpendicular(GetRow(2), epsilon);
|
||||
}
|
||||
|
||||
bool Matrix3x3::IsColOrthogonal(float epsilon) const
|
||||
{
|
||||
return GetColumn(0).IsPerpendicular(GetColumn(1), epsilon)
|
||||
&& GetColumn(0).IsPerpendicular(GetColumn(2), epsilon)
|
||||
&& GetColumn(1).IsPerpendicular(GetColumn(2), epsilon);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Matrix3x3::HasUniformScale(float epsilon) const {
|
||||
Vector3 scale = ExtractScale();
|
||||
return Math::EqualAbs(scale.x, scale.y, epsilon) && Math::EqualAbs(scale.x, scale.z, epsilon);
|
||||
}
|
||||
|
||||
Vector3 Matrix3x3::GetRow3(int index) const {
|
||||
return GetRow(index);
|
||||
}
|
||||
|
||||
Vector3 Matrix3x3::GetColumn3(int index) const {
|
||||
return GetColumn(index);
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix3x3::operator*(const Matrix4x4 &rhs) const {
|
||||
auto lhs = *this;
|
||||
Matrix4x4 r;
|
||||
|
||||
r[0][0] = lhs.At(0, 0) * rhs.At(0, 0) + lhs.At(0, 1) * rhs.At(1, 0) + lhs.At(0, 2) * rhs.At(2, 0);
|
||||
r[0][1] = lhs.At(0, 0) * rhs.At(0, 1) + lhs.At(0, 1) * rhs.At(1, 1) + lhs.At(0, 2) * rhs.At(2, 1);
|
||||
r[0][2] = lhs.At(0, 0) * rhs.At(0, 2) + lhs.At(0, 1) * rhs.At(1, 2) + lhs.At(0, 2) * rhs.At(2, 2);
|
||||
r[0][3] = lhs.At(0, 0) * rhs.At(0, 3) + lhs.At(0, 1) * rhs.At(1, 3) + lhs.At(0, 2) * rhs.At(2, 3);
|
||||
|
||||
r[1][0] = lhs.At(1, 0) * rhs.At(0, 0) + lhs.At(1, 1) * rhs.At(1, 0) + lhs.At(1, 2) * rhs.At(2, 0);
|
||||
r[1][1] = lhs.At(1, 0) * rhs.At(0, 1) + lhs.At(1, 1) * rhs.At(1, 1) + lhs.At(1, 2) * rhs.At(2, 1);
|
||||
r[1][2] = lhs.At(1, 0) * rhs.At(0, 2) + lhs.At(1, 1) * rhs.At(1, 2) + lhs.At(1, 2) * rhs.At(2, 2);
|
||||
r[1][3] = lhs.At(1, 0) * rhs.At(0, 3) + lhs.At(1, 1) * rhs.At(1, 3) + lhs.At(1, 2) * rhs.At(2, 3);
|
||||
|
||||
r[2][0] = lhs.At(2, 0) * rhs.At(0, 0) + lhs.At(2, 1) * rhs.At(1, 0) + lhs.At(2, 2) * rhs.At(2, 0);
|
||||
r[2][1] = lhs.At(2, 0) * rhs.At(0, 1) + lhs.At(2, 1) * rhs.At(1, 1) + lhs.At(2, 2) * rhs.At(2, 1);
|
||||
r[2][2] = lhs.At(2, 0) * rhs.At(0, 2) + lhs.At(2, 1) * rhs.At(1, 2) + lhs.At(2, 2) * rhs.At(2, 2);
|
||||
r[2][3] = lhs.At(2, 0) * rhs.At(0, 3) + lhs.At(2, 1) * rhs.At(1, 3) + lhs.At(2, 2) * rhs.At(2, 3);
|
||||
|
||||
r[3][0] = rhs.At(3, 0);
|
||||
r[3][1] = rhs.At(3, 1);
|
||||
r[3][2] = rhs.At(3, 2);
|
||||
r[3][3] = rhs.At(3, 3);
|
||||
return r;
|
||||
}
|
||||
|
||||
Vector2 Matrix3x3::operator*(const Vector2 &rhs) const {
|
||||
return Transform(rhs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,11 @@
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
#include <J3ML/LinearAlgebra/Vector4.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
const Matrix4x4 Matrix4x4::Zero = Matrix4x4(0);
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
|
||||
|
||||
const Matrix4x4 Matrix4x4::Zero = Matrix4x4(0.f);
|
||||
const Matrix4x4 Matrix4x4::Identity = Matrix4x4({1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1});
|
||||
const Matrix4x4 Matrix4x4::NaN = Matrix4x4(NAN);
|
||||
|
||||
@@ -420,6 +423,16 @@ namespace LinearAlgebra {
|
||||
return GetColumn3(3);
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix4x4::Scale(const Vector3& scale)
|
||||
{
|
||||
auto mat = *this;
|
||||
|
||||
mat.At(3, 0) *= scale.x;
|
||||
mat.At(3, 1) *= scale.y;
|
||||
mat.At(3, 2) *= scale.z;
|
||||
return mat;
|
||||
}
|
||||
|
||||
Matrix4x4
|
||||
Matrix4x4::LookAt(const Vector3 &localFwd, const Vector3 &targetDir, const Vector3 &localUp, const Vector3 &worldUp) {
|
||||
Matrix4x4 m;
|
||||
@@ -514,4 +527,183 @@ namespace LinearAlgebra {
|
||||
At(row, 2) *= scalar;
|
||||
At(row, 3) *= scalar;
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix4x4::OpenGLOrthoProjLH(float n, float f, float h, float v) {
|
||||
/// Same as OpenGLOrthoProjRH, except that the camera looks towards +Z in view space, instead of -Z.
|
||||
using f32 = float;
|
||||
f32 p00 = 2.f / h; f32 p01 = 0; f32 p02 = 0; float p03 = 0.f;
|
||||
f32 p10 = 0; f32 p11 = 2.f / v; f32 p12 = 0; float p13 = 0.f;
|
||||
f32 p20 = 0; f32 p21 = 0; f32 p22 = 2.f / (f-n); float p23 = (f+n) / (n-f);
|
||||
f32 p30 = 0; f32 p31 = 0; f32 p32 = 0; float p33 = 1.f;
|
||||
|
||||
return {p00, p01, p02, p03, p10, p11, p12, p13, p20, p21, p22, p23, p30, p31, p32, p33};
|
||||
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix4x4::OpenGLOrthoProjRH(float n, float f, float h, float v) {
|
||||
using f32 = float;
|
||||
f32 p00 = 2.f / h; f32 p01 = 0; f32 p02 = 0; f32 p03 = 0.f;
|
||||
f32 p10 = 0; f32 p11 = 2.f / v; f32 p12 = 0; f32 p13 = 0.f;
|
||||
f32 p20 = 0; f32 p21 = 0; f32 p22 = 2.f / (n-f); f32 p23 = (f+n) / (n-f);
|
||||
f32 p30 = 0; f32 p31 = 0; f32 p32 = 0; f32 p33 = 1.f;
|
||||
|
||||
return {p00, p01, p02, p03, p10, p11, p12, p13, p20, p21, p22, p23, p30, p31, p32, p33};
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix4x4::OpenGLPerspProjLH(float n, float f, float h, float v) {
|
||||
// Same as OpenGLPerspProjRH, except that the camera looks towards +Z in view space, instead of -Z.
|
||||
using f32 = float;
|
||||
f32 p00 = 2.f *n / h; f32 p01 = 0; f32 p02 = 0; f32 p03 = 0.f;
|
||||
f32 p10 = 0; f32 p11 = 2.f * n / v; f32 p12 = 0; f32 p13 = 0.f;
|
||||
f32 p20 = 0; f32 p21 = 0; f32 p22 = (n+f) / (f-n); f32 p23 = 2.f*n*f / (n-f);
|
||||
f32 p30 = 0; f32 p31 = 0; f32 p32 = 1.f; f32 p33 = 0.f;
|
||||
|
||||
return {p00, p01, p02, p03, p10, p11, p12, p13, p20, p21, p22, p23, p30, p31, p32, p33};
|
||||
}
|
||||
|
||||
Matrix4x4 Matrix4x4::OpenGLPerspProjRH(float n, float f, float h, float v) {
|
||||
// In OpenGL, the post-perspective unit cube ranges in [-1, 1] in all X, Y and Z directions.
|
||||
// See http://www.songho.ca/opengl/gl_projectionmatrix.html , unlike in Direct3D, where the
|
||||
// Z coordinate ranges in [0, 1]. This is the only difference between D3DPerspProjRH and OpenGLPerspProjRH.
|
||||
using f32 = float;
|
||||
float p00 = 2.f *n / h; float p01 = 0; float p02 = 0; float p03 = 0.f;
|
||||
float p10 = 0; float p11 = 2.f * n / v; float p12 = 0; float p13 = 0.f;
|
||||
float p20 = 0; float p21 = 0; float p22 = (n+f) / (n-f); float p23 = 2.f*n*f / (n-f);
|
||||
float p30 = 0; float p31 = 0; float p32 = -1.f; float p33 = 0.f;
|
||||
|
||||
return {p00, p01, p02, p03, p10, p11, p12, p13, p20, p21, p22, p23, p30, p31, p32, p33};
|
||||
}
|
||||
|
||||
Matrix4x4::Matrix4x4(const float *data) {
|
||||
assert(data);
|
||||
At(0, 0) = data[0];
|
||||
At(0, 1) = data[1];
|
||||
At(0, 2) = data[2];
|
||||
At(0, 3) = data[3];
|
||||
|
||||
At(1, 0) = data[4];
|
||||
At(1, 1) = data[5];
|
||||
At(1, 2) = data[6];
|
||||
At(1, 3) = data[7];
|
||||
|
||||
At(2, 0) = data[8];
|
||||
At(2, 1) = data[9];
|
||||
At(2, 2) = data[10];
|
||||
At(2, 3) = data[11];
|
||||
|
||||
At(3, 0) = data[12];
|
||||
At(3, 1) = data[13];
|
||||
At(3, 2) = data[14];
|
||||
At(3, 3) = data[15];
|
||||
}
|
||||
|
||||
bool Matrix4x4::ContainsProjection(float epsilon) const {
|
||||
return GetRow(3).Equals(0.f, 0.f, 0.f, 1.f, epsilon) == false;
|
||||
}
|
||||
|
||||
Vector4 Matrix4x4::Mul(const Vector4 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
Vector3 Matrix4x4::Mul(const Vector3 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
Vector2 Matrix4x4::Mul(const Vector2 &rhs) const {
|
||||
return *this * rhs;
|
||||
}
|
||||
|
||||
Vector4 Matrix4x4::operator[](int row) const {
|
||||
return GetRow(row);
|
||||
}
|
||||
|
||||
bool Matrix4x4::HasUniformScale(float epsilon) const {
|
||||
Vector3 scale = ExtractScale();
|
||||
return Math::EqualAbs(scale.x, scale.y, epsilon) && Math::EqualAbs(scale.x, scale.z, epsilon);
|
||||
}
|
||||
|
||||
Vector3 Matrix4x4::ExtractScale() const {
|
||||
return {GetColumn3(0).Length(), GetColumn3(1).Length(), GetColumn3(2).Length()};
|
||||
}
|
||||
|
||||
bool Matrix4x4::IsColOrthogonal(float epsilon) const {
|
||||
return IsColOrthogonal3(epsilon);
|
||||
}
|
||||
|
||||
bool Matrix4x4::IsRowOrthogonal(float epsilon) const {
|
||||
return IsRowOrthogonal3(epsilon);
|
||||
}
|
||||
|
||||
bool Matrix4x4::IsColOrthogonal3(float epsilon) const {
|
||||
return GetColumn(0).IsPerpendicular(GetColumn(1), epsilon)
|
||||
&& GetColumn(0).IsPerpendicular(GetColumn(2), epsilon)
|
||||
&& GetColumn(1).IsPerpendicular(GetColumn(2), epsilon);
|
||||
}
|
||||
|
||||
bool Matrix4x4::IsRowOrthogonal3(float epsilon) const {
|
||||
return GetRow(0).IsPerpendicular(GetRow(1), epsilon)
|
||||
&& GetRow(0).IsPerpendicular(GetRow(2), epsilon)
|
||||
&& GetRow(1).IsPerpendicular(GetRow(2), epsilon);
|
||||
}
|
||||
|
||||
Vector4 Matrix4x4::Col(int i) const { return GetColumn(i);}
|
||||
|
||||
Vector4 Matrix4x4::Row(int i) const { return GetRow(i);}
|
||||
|
||||
Vector4 Matrix4x4::Col3(int i) const { return GetColumn3(i);}
|
||||
|
||||
Vector4 Matrix4x4::Row3(int i) const { return GetRow3(i);}
|
||||
|
||||
Vector3 Matrix4x4::TransformDir(float tx, float ty, float tz) const
|
||||
{
|
||||
assert(!this->ContainsProjection()); // This function does not divide by w or output it, so cannot have projection.
|
||||
return Vector3(At(0, 0) * tx + At(0, 1) * ty + At(0, 2) * tz,
|
||||
At(1, 0) * tx + At(1, 1) * ty + At(1, 2) * tz,
|
||||
At(2, 0) * tx + At(2, 1) * ty + At(2, 2) * tz);
|
||||
|
||||
}
|
||||
|
||||
void Matrix4x4::InverseOrthonormal()
|
||||
{
|
||||
assert(!ContainsProjection());
|
||||
|
||||
// a) Transpose the top-left 3x3 part in-place to produce R^t.
|
||||
Swap(elems[0][1], elems[1][0]);
|
||||
Swap(elems[0][2], elems[2][0]);
|
||||
Swap(elems[1][2], elems[2][1]);
|
||||
|
||||
// b) Replace the top-right 3x1 part by computing R^t(-T).
|
||||
SetTranslatePart(TransformDir(-At(0, 3), -At(1, 3), -At(2, 3)));
|
||||
|
||||
}
|
||||
|
||||
void Matrix4x4::SetCol(int col, const float *data) {
|
||||
assert(data != nullptr);
|
||||
SetCol(col, data[0], data[1], data[2], data[3]);
|
||||
}
|
||||
|
||||
void Matrix4x4::SetCol(int column, float m_0c, float m_1c, float m_2c, float m_3c)
|
||||
{
|
||||
assert(column >= 0);
|
||||
assert(column < Cols);
|
||||
assert(std::isfinite(m_0c));
|
||||
assert(std::isfinite(m_1c));
|
||||
assert(std::isfinite(m_2c));
|
||||
assert(std::isfinite(m_3c));
|
||||
At(0, column) = m_0c;
|
||||
At(1, column) = m_1c;
|
||||
At(2, column) = m_2c;
|
||||
At(3, column) = m_3c;
|
||||
}
|
||||
|
||||
void Matrix4x4::SetCol(int column, const Vector3 &columnVector, float m_3c)
|
||||
{
|
||||
SetCol(column, columnVector.x, columnVector.y, columnVector.z, m_3c);
|
||||
}
|
||||
|
||||
void Matrix4x4::SetCol(int column, const Vector4 &columnVector)
|
||||
{
|
||||
SetCol(column, columnVector.x, columnVector.y, columnVector.z, columnVector.w);
|
||||
}
|
||||
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
Quaternion Quaternion::operator-() const
|
||||
{
|
||||
return {-x, -y, -z, -w};
|
||||
@@ -47,7 +47,18 @@ namespace LinearAlgebra {
|
||||
|
||||
void Quaternion::SetFromAxisAngle(const Vector3 &axis, float angle) {
|
||||
float sinz, cosz;
|
||||
sinz = std::sin(angle*0.5f);
|
||||
cosz = std::cos(angle*0.5f);
|
||||
|
||||
x = axis.x * sinz;
|
||||
y = axis.y * sinz;
|
||||
z = axis.z * sinz;
|
||||
w = cosz;
|
||||
}
|
||||
|
||||
void Quaternion::SetFromAxisAngle(const Vector4 &axis, float angle)
|
||||
{
|
||||
SetFromAxisAngle(Vector3(axis.x, axis.y, axis.z), angle);
|
||||
}
|
||||
|
||||
Quaternion Quaternion::operator*(float scalar) const {
|
||||
@@ -172,4 +183,22 @@ namespace LinearAlgebra {
|
||||
Matrix4x4 Quaternion::ToMatrix4x4(const Vector3 &translation) const {
|
||||
return {*this, translation};
|
||||
}
|
||||
|
||||
float Quaternion::GetAngle() const {
|
||||
return std::acos(w) * 2.f;
|
||||
}
|
||||
|
||||
Vector3 Quaternion::GetAxis() const {
|
||||
float rcpSinAngle = 1 - (std::sqrt(1 - w * w));
|
||||
|
||||
return Vector3(x, y, z) * rcpSinAngle;
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Vector3 &rotationAxis, float rotationAngleBetween) {
|
||||
SetFromAxisAngle(rotationAxis, rotationAngleBetween);
|
||||
}
|
||||
|
||||
Quaternion::Quaternion(const Vector4 &rotationAxis, float rotationAngleBetween) {
|
||||
SetFromAxisAngle(rotationAxis, rotationAngleBetween);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#include <J3ML/LinearAlgebra/Transform2D.h>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
const Transform2D Transform2D::Identity = Transform2D({0, 0}, {1, 1}, {0,0}, {0,0}, 0);
|
||||
const Transform2D Transform2D::FlipX = Transform2D({0, 0}, {-1, 1}, {0,0}, {0,0}, 0);
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <valarray>
|
||||
#include <iostream>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
Vector2::Vector2(): x(0), y(0)
|
||||
{}
|
||||
@@ -15,13 +15,16 @@ namespace LinearAlgebra {
|
||||
Vector2::Vector2(const Vector2& rhs): x(rhs.x), y(rhs.y)
|
||||
{}
|
||||
|
||||
float Vector2::operator[](std::size_t index)
|
||||
float Vector2::operator[](std::size_t index) const
|
||||
{
|
||||
assert(index < 2);
|
||||
if (index == 0) return x;
|
||||
if (index == 1) return y;
|
||||
return 0;
|
||||
return At(index);
|
||||
}
|
||||
|
||||
float &Vector2::operator[](std::size_t index)
|
||||
{
|
||||
return At(index);
|
||||
}
|
||||
|
||||
bool Vector2::IsWithinMarginOfError(const Vector2& rhs, float margin) const
|
||||
{
|
||||
return this->Distance(rhs) <= margin;
|
||||
@@ -158,6 +161,7 @@ namespace LinearAlgebra {
|
||||
const Vector2 Vector2::Left = {-1, 0};
|
||||
const Vector2 Vector2::Right = {1, 0};
|
||||
const Vector2 Vector2::NaN = {NAN, NAN};
|
||||
const Vector2 Vector2::Infinity = {INFINITY, INFINITY};
|
||||
|
||||
float Vector2::GetX() const { return x; }
|
||||
|
||||
@@ -253,4 +257,91 @@ namespace LinearAlgebra {
|
||||
Vector2 Vector2::Div(const Vector2 &v) const {
|
||||
return {this->x/v.x, this->y/v.y};
|
||||
}
|
||||
|
||||
Vector2 Vector2::Abs() const { return {std::abs(x), std::abs(y)};}
|
||||
|
||||
float *Vector2::ptr() {
|
||||
return &x;
|
||||
}
|
||||
|
||||
const float *Vector2::ptr() const { return &x;}
|
||||
|
||||
const float Vector2::At(std::size_t index) const {
|
||||
assert(index >= 0);
|
||||
assert(index < Dimensions);
|
||||
return ptr()[index];
|
||||
}
|
||||
|
||||
float &Vector2::At(std::size_t index) {
|
||||
assert(index >= 0);
|
||||
assert(index < Dimensions);
|
||||
return ptr()[index];
|
||||
}
|
||||
|
||||
Vector2 &Vector2::operator/=(float scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2 &Vector2::operator*=(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2 &Vector2::operator-=(const Vector2 &rhs) // Subtracts a vector from this vector, in-place
|
||||
{
|
||||
x -= rhs.x;
|
||||
y -= rhs.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2 &Vector2::operator+=(const Vector2 &rhs) // Adds a vector to this vector, in-place.
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2 &Vector2::operator=(const Vector2 &rhs) {
|
||||
x = rhs.x;
|
||||
y = rhs.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2::Vector2(const float *data) {
|
||||
assert(data);
|
||||
x = data[0];
|
||||
y = data[1];
|
||||
}
|
||||
|
||||
Vector2::Vector2(float scalar) {
|
||||
x = scalar;
|
||||
y = scalar;
|
||||
}
|
||||
|
||||
float Vector2::DistanceSq(const Vector2 &to) const {
|
||||
return (*this-to).LengthSquared();
|
||||
}
|
||||
|
||||
float Vector2::DistanceSq(const Vector2 &from, const Vector2 &to) {
|
||||
return (from-to).LengthSquared();
|
||||
}
|
||||
|
||||
bool Vector2::OrientedCCW(const Vector2 &a, const Vector2 &b, const Vector2 &c)
|
||||
{
|
||||
// Compute the determinant
|
||||
// | ax ay 1 |
|
||||
// | bx by 1 |
|
||||
// | cx cy 1 |
|
||||
// See Christer Ericson, Real-Time Collision Detection, p.32.
|
||||
return (a.x-c.x)*(b.y-c.y) - (a.y-c.y)*(b.x-c.x) >= 0.f;
|
||||
}
|
||||
|
||||
}
|
@@ -3,9 +3,7 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
|
||||
#pragma region vector3
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
const Vector3 Vector3::Zero = {0,0,0};
|
||||
const Vector3 Vector3::Up = {0, -1, 0};
|
||||
@@ -15,6 +13,8 @@ namespace LinearAlgebra {
|
||||
const Vector3 Vector3::Forward = {0, 0, -1};
|
||||
const Vector3 Vector3::Backward = {0, 0, 1};
|
||||
const Vector3 Vector3::NaN = {NAN, NAN, NAN};
|
||||
const Vector3 Vector3::Infinity = {INFINITY, INFINITY, INFINITY};
|
||||
const Vector3 Vector3::NegativeInfinity = {-INFINITY, -INFINITY, -INFINITY};
|
||||
|
||||
Vector3 Vector3::operator+(const Vector3& rhs) const
|
||||
{
|
||||
@@ -80,6 +80,14 @@ namespace LinearAlgebra {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float &Vector3::operator[](std::size_t index)
|
||||
{
|
||||
assert(index < 3);
|
||||
if (index == 0) return x;
|
||||
if (index == 1) return y;
|
||||
if (index == 2) return z;
|
||||
}
|
||||
|
||||
bool Vector3::IsWithinMarginOfError(const Vector3& rhs, float margin) const
|
||||
{
|
||||
return this->Distance(rhs) <= margin;
|
||||
@@ -95,6 +103,8 @@ namespace LinearAlgebra {
|
||||
return this->IsWithinMarginOfError(rhs) == false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vector3 Vector3::Min(const Vector3& min) const
|
||||
{
|
||||
return {
|
||||
@@ -308,5 +318,188 @@ namespace LinearAlgebra {
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
Vector3 Vector3::Abs() const {
|
||||
return {std::abs(x), std::abs(y), std::abs(z)};
|
||||
}
|
||||
|
||||
float *Vector3::ptr() {
|
||||
return &x;
|
||||
}
|
||||
|
||||
void Vector3::Orthonormalize(Vector3 &a, Vector3 &b) {
|
||||
a = a.Normalize();
|
||||
b = b - b.ProjectToNorm(a);
|
||||
b = b.Normalize();
|
||||
}
|
||||
|
||||
void Vector3::Orthonormalize(Vector3 &a, Vector3 &b, Vector3 &c) {
|
||||
a = a.Normalize();
|
||||
b = b - b.ProjectToNorm(a);
|
||||
b = b.Normalize();
|
||||
c = c - c.ProjectToNorm(a);
|
||||
c = c - c.ProjectToNorm(b);
|
||||
c = c.Normalize();
|
||||
}
|
||||
|
||||
Vector3 Vector3::ProjectToNorm(const Vector3 &direction) const {
|
||||
return direction * this->Dot(direction);
|
||||
}
|
||||
|
||||
bool Vector3::IsFinite() const {
|
||||
return std::isfinite(x) && std::isfinite(y) && std::isfinite(z);
|
||||
}
|
||||
|
||||
Vector3::Vector3(const float *data) {
|
||||
x = data[0];
|
||||
y = data[1];
|
||||
z = data[2];
|
||||
}
|
||||
|
||||
Vector3 &Vector3::operator+=(const Vector3 &rhs) {
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
z += rhs.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3 &Vector3::operator-=(const Vector3 &rhs) {
|
||||
x -= rhs.x;
|
||||
y -= rhs.y;
|
||||
z -= rhs.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3 &Vector3::operator*=(float scalar) {
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3 &Vector3::operator/=(float scalar) {
|
||||
x /= scalar;
|
||||
y /= scalar;
|
||||
z /= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float Vector3::MinElement() const {
|
||||
return std::min(x, std::min(y, z));
|
||||
}
|
||||
|
||||
bool Vector3::AreOrthonormal(const Vector3 &a, const Vector3 &b, float epsilon) {
|
||||
return a.IsPerpendicular(b, epsilon) && a.IsNormalized(epsilon*epsilon) && b.IsNormalized(epsilon*epsilon);
|
||||
}
|
||||
|
||||
Vector3 Vector3::Mul(const Vector3 &rhs) const {
|
||||
return {
|
||||
this->x * rhs.x,
|
||||
this->y * rhs.y,
|
||||
this->z * rhs.z
|
||||
};
|
||||
}
|
||||
|
||||
Vector3 Vector3::Div(const Vector3 &v) const {
|
||||
return {
|
||||
this->x/v.x,
|
||||
this->y/v.y,
|
||||
this->z/v.z
|
||||
};
|
||||
}
|
||||
|
||||
Vector3 Vector3::Abs(const Vector3 &rhs) {
|
||||
return rhs.Abs();
|
||||
}
|
||||
|
||||
bool Vector3::Equals(const Vector3 &rhs, float epsilon) const {
|
||||
return std::abs(x - rhs.x) < epsilon &&
|
||||
std::abs(y - rhs.y) < epsilon &&
|
||||
std::abs(z - rhs.z) < epsilon;
|
||||
}
|
||||
|
||||
bool Vector3::Equals(float _x, float _y, float _z, float epsilon) const {
|
||||
return std::abs(x - _x) < epsilon &&
|
||||
std::abs(y - _y) < epsilon &&
|
||||
std::abs(z - _z) < epsilon;
|
||||
}
|
||||
|
||||
Vector3 Vector3::Min(const Vector3 &a, const Vector3 &b, const Vector3 &c) {
|
||||
return {
|
||||
std::min(a.x, std::min(b.x, c.x)),
|
||||
std::min(a.y, std::min(b.y, c.y)),
|
||||
std::min(a.z, std::min(b.z, c.z))
|
||||
};
|
||||
}
|
||||
|
||||
Vector3 Vector3::Max(const Vector3 &a, const Vector3 &b, const Vector3 &c) {
|
||||
return {
|
||||
std::max(a.x, std::max(b.x, c.x)),
|
||||
std::max(a.y, std::max(b.y, c.y)),
|
||||
std::max(a.z, std::max(b.z, c.z))
|
||||
};
|
||||
}
|
||||
|
||||
Vector3 Vector3::Perpendicular(const Vector3 &hint, const Vector3 &hint2) const {
|
||||
assert(!this->IsZero());
|
||||
assert(hint.IsNormalized());
|
||||
assert(hint2.IsNormalized());
|
||||
Vector3 v = this->Cross(hint);
|
||||
float len = v.TryNormalize();
|
||||
|
||||
}
|
||||
|
||||
float Vector3::TryNormalize() {
|
||||
assert(IsFinite());
|
||||
float length = Length();
|
||||
if (length > 1e-6f)
|
||||
{
|
||||
*this *= 1.f / length;
|
||||
return length;
|
||||
}
|
||||
else
|
||||
{
|
||||
Set(1.f, 0.f, 0.f); // We will always produce a normalized vector.
|
||||
return 0; // But signal failure, so user knows we have generated an arbitrary normalization.
|
||||
}
|
||||
}
|
||||
|
||||
float Vector3::At(int index) const {
|
||||
assert(index >= 0);
|
||||
assert(index < Dimensions);
|
||||
return ptr()[index];
|
||||
}
|
||||
|
||||
float &Vector3::At(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < Dimensions);
|
||||
return ptr()[index];
|
||||
}
|
||||
|
||||
void Vector3::Set(float d, float d1, float d2) {
|
||||
x = d;
|
||||
y = d1;
|
||||
z = d2;
|
||||
}
|
||||
|
||||
Vector3::Vector3(const Vector2& XY, float Z) {
|
||||
x = XY.x;
|
||||
y = XY.y;
|
||||
z = Z;
|
||||
}
|
||||
|
||||
Vector3::Vector3(float scalar) {
|
||||
x = scalar;
|
||||
y = scalar;
|
||||
z = scalar;
|
||||
}
|
||||
|
||||
float Vector3::DistanceSquared(const Vector3 &to) const {
|
||||
return (*this-to).LengthSquared();
|
||||
}
|
||||
|
||||
float Vector3::DistanceSquared(const Vector3 &from, const Vector3 &to) {
|
||||
return from.DistanceSquared(to);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace J3ML::LinearAlgebra {
|
||||
|
||||
const Vector4 Vector4::Zero = {0,0,0,0};
|
||||
const Vector4 Vector4::NaN = {NAN, NAN, NAN, NAN};
|
||||
@@ -150,6 +150,55 @@ Vector4 Vector4::operator-(const Vector4& rhs) const
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Vector4::Equals(const Vector4 &rhs, float epsilon) const {
|
||||
return std::abs(x - rhs.x) < epsilon &&
|
||||
std::abs(y - rhs.y) < epsilon &&
|
||||
std::abs(z - rhs.z) < epsilon &&
|
||||
std::abs(w - rhs.w) < epsilon;
|
||||
}
|
||||
|
||||
bool Vector4::Equals(float _x, float _y, float _z, float _w, float epsilon) const {
|
||||
return std::abs(x - _x) < epsilon &&
|
||||
std::abs(y - _y) < epsilon &&
|
||||
std::abs(z - _z) < epsilon &&
|
||||
std::abs(w - _w) < epsilon;
|
||||
}
|
||||
|
||||
float Vector4::operator[](std::size_t index) const {
|
||||
assert(index < 4);
|
||||
if (index==0) return x;
|
||||
if (index==1) return y;
|
||||
if (index==2) return z;
|
||||
if (index==3) return w;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float &Vector4::operator[](std::size_t index) {
|
||||
assert(index < 4);
|
||||
if (index == 0) return x;
|
||||
if (index == 1) return y;
|
||||
if (index == 2) return z;
|
||||
if (index == 3) return w;
|
||||
|
||||
}
|
||||
|
||||
float Vector4::LengthSqXYZ() const {
|
||||
return x*x * y*y * z*z;
|
||||
}
|
||||
|
||||
Vector4 Vector4::Cross3(const Vector3 &rhs) const {
|
||||
Vector4 dst;
|
||||
dst.x = y * rhs.z - z * rhs.y;
|
||||
dst.y = z * rhs.x - x * rhs.z;
|
||||
dst.z = x * rhs.y - y * rhs.x;
|
||||
dst.w = 0.f;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Vector4 Vector4::Cross3(const Vector4 &rhs) const {
|
||||
return Cross3(rhs.XYZ());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#pragma endregion
|
@@ -1,7 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
|
||||
using Vector2 = LinearAlgebra::Vector2;
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
TEST(Vector2Test, V2_Constructor_Default)
|
||||
{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
|
||||
using Vector3 = LinearAlgebra::Vector3;
|
||||
using J3ML::LinearAlgebra::Vector3;
|
||||
|
||||
void EXPECT_V3_EQ(const Vector3& lhs, const Vector3& rhs)
|
||||
{
|
||||
@@ -185,11 +185,12 @@ TEST(Vector3Test, V3_Lerp)
|
||||
EXPECT_V3_EQ(Start.Lerp(Finish, Percent), ExpectedResult);
|
||||
}
|
||||
TEST(Vector3Test, V3_AngleBetween) {
|
||||
using J3ML::LinearAlgebra::Angle2D;
|
||||
Vector3 A{ .5f, .5f, .5f};
|
||||
Vector3 B {.25f, .75f, .25f};
|
||||
A = A.Normalize();
|
||||
B = B.Normalize();
|
||||
LinearAlgebra::Angle2D ExpectedResult {-0.69791365, -2.3561945};
|
||||
Angle2D ExpectedResult {-0.69791365, -2.3561945};
|
||||
std::cout << A.AngleBetween(B).x << ", " << A.AngleBetween(B).y << "";
|
||||
auto angle = A.AngleBetween(B);
|
||||
EXPECT_FLOAT_EQ(angle.x, ExpectedResult.x);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <J3ML/LinearAlgebra/Vector4.h>
|
||||
|
||||
using Vector4 = LinearAlgebra::Vector4;
|
||||
using Vector4 = J3ML::LinearAlgebra::Vector4;
|
||||
|
||||
|
||||
void EXPECT_V4_EQ(const Vector4& lhs, const Vector4& rhs)
|
||||
|
Reference in New Issue
Block a user