Compare commits

...

4 Commits

Author SHA1 Message Date
dea5735c87 Implement CreateFrustumFromCamera 2024-01-10 14:46:12 -05:00
cc9ff95daa Implement ToQuat 2024-01-07 02:14:31 -05:00
a1d4df30c7 Implement ToQuat 2024-01-04 09:30:14 -05:00
7429f0782f Implementing Vector3 Unit Tests 2024-01-02 21:31:44 -05:00
14 changed files with 356 additions and 82 deletions

View File

@@ -32,7 +32,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)
src/J3ML/LinearAlgebra/AxisAngle.cpp
include/J3ML/LinearAlgebra/Vector.h)
set_target_properties(J3ML PROPERTIES LINKER_LANGUAGE CXX)
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})

View File

@@ -3,12 +3,13 @@
#pragma once
namespace Geometry {
using Point2D = LinearAlgebra::Vector2;
using Vector2 = LinearAlgebra::Vector2;
using Vector3 = LinearAlgebra::Vector3;
class LineSegment2D
{
Point2D A;
Point2D B;
Vector2 A;
Vector2 B;
};
class Rectangle; //AABB2D;
@@ -29,7 +30,7 @@ namespace Geometry {
using Point3D = LinearAlgebra::Vector3;
// A 3D axis-aligned bounding box
// This data structure can be used to represent coarse bounds of objects, in situations where detailed triangle-level
@@ -44,18 +45,59 @@ namespace Geometry {
class Line;
class LineSegment
{
Point3D A;
Point3D B;
Vector3 A;
Vector3 B;
};
class Ray
{
Point3D Origin;
Point3D Direction;
Vector3 Origin;
Vector3 Direction;
};
class OBB;
class Frustum;
class Plane;
class Plane
{
public:
Vector3 Position;
Vector3 Normal;
float distance = 0.f;
};
class Frustum {
public:
Plane TopFace;
Plane BottomFace;
Plane RightFace;
Plane LeftFace;
Plane FarFace;
Plane NearFace;
};
class Camera {
public:
Vector3 Position;
Vector3 Front;
Vector3 Right;
Vector3 Up;
};
static Frustum CreateFrustumFromCamera(const Camera& cam, float aspect, float fovY, float zNear, float zFar)
{
Frustum frustum;
const float halfVSide = zFar * tanf(fovY * 0.5f);
const float halfHSide = halfVSide * aspect;
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)};
return frustum;
}
class Polygon;
class Polyhedron;
class QuadTree;

View File

@@ -5,6 +5,10 @@
namespace LinearAlgebra {
class Angle2D {
public:
float x;
float y;
bool operator==(const Angle2D& rhs) const {
return (this->x==rhs.x && this->y==rhs.y);
}
};
}

View File

@@ -12,10 +12,20 @@ namespace LinearAlgebra {
static const Matrix2x2 Identity;
static const Matrix2x2 NaN;
Matrix2x2() {}
Matrix2x2(float val);
Matrix2x2(float m00, float m01, float m10, float m11);
Matrix2x2(const Vector2& r1, const Vector2& r2);
Vector2 GetRow(int index) const;
Vector2 GetColumn(int index) const;
float At(int x, int y) const;
float Determinant() const;
Matrix2x2 Inverse() const;
Matrix2x2 Transpose() const;
Vector2 Transform(const Vector2& rhs) const;
Vector2 operator * (const Vector2& rhs) const;
Matrix2x2 operator * (const Matrix2x2 &rhs) const;

View File

@@ -99,13 +99,9 @@ namespace LinearAlgebra {
Matrix3x3 Transpose() const;
// Transforms the given vectors by this matrix M, i.e. returns M * (x,y,z)
Vector2 Transform(const Vector2& rhs) const;
Vector3 Transform(const Vector3& rhs) const;
Vector3 operator[] (float index) const;
Vector3 operator * (const Vector3& rhs) const;
Matrix3x3 operator * (const Matrix3x3& rhs) const;

View File

@@ -2,8 +2,6 @@
#include <J3ML/LinearAlgebra.h>
namespace 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,
@@ -12,7 +10,7 @@ namespace LinearAlgebra {
* The elements of this matrix are
* m_00, m_01, m_02, m_03
* m_10, m_11, m_12, m_13
* m_20, m_21, m_22, m_23,
* m_20, m_21, m_22, xm_23,
* m_30, m_31, m_32, m_33
*
* The element m_yx is the value on the row y and column x.
@@ -20,8 +18,42 @@ namespace LinearAlgebra {
*/
class Matrix4x4 {
public:
enum { Rows = 4 };
enum { Cols = 4 };
static const Matrix4x4 Zero;
static const Matrix4x4 Identity;
static const Matrix4x4 NaN;
Matrix4x4() {}
Matrix4x4(float val);
Matrix4x4(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33);
Matrix4x4(const Vector4& r1, const Vector4& r2, const Vector4& r3, const Vector4& r4);
explicit Matrix4x4(const Quaternion& orientation);
Vector4 GetRow(int index) const;
Vector4 GetColumn(int index) const;
float At(int x, int y) const;
Vector4 Diagonal() const;
Vector4 WorldX() const;
Vector4 WorldY() const;
Vector4 WorldZ() const;
/// Computes the determinant of this matrix.
// If the determinant is nonzero, this matrix is invertible.
float Determinant() const;
Matrix4x4 Inverse() const;
Matrix4x4 Transpose() const;
Vector2 Transform(const Vector2& rhs) const;
Vector3 Transform(const Vector3& rhs) const;
Vector4 Transform(const Vector4& rhs) const;
Vector3 GetTranslationComponent() const;
Matrix3x3 GetRotationComponent() const;

View File

@@ -0,0 +1,13 @@
#pragma once
template <typename T, int Dims>
class templated_vector
{
};
using v2f = templated_vector<float, 2>;
using v3f = templated_vector<float, 3>;
using v4f = templated_vector<float, 4>;

View File

@@ -3,6 +3,7 @@
#include <J3ML/LinearAlgebra/Vector3.h>
#include <cstddef>
#include <cstdlib>
#include <J3ML/LinearAlgebra/Angle2D.h>
namespace LinearAlgebra {
@@ -95,8 +96,8 @@ public:
static Vector3 Lerp(const Vector3& lhs, const Vector3& rhs, float alpha);
float AngleBetween(const Vector3& rhs) const; // TODO: 3D Angle representation?
static float AngleBetween(const Vector3& lhs, const Vector3& rhs); // TODO: 3D Angle representation?
Angle2D AngleBetween(const Vector3& rhs) const;
static Angle2D AngleBetween(const Vector3& lhs, const Vector3& rhs);
// Adds two vectors
Vector3 operator+(const Vector3& rhs) const;
@@ -122,12 +123,9 @@ public:
Vector3 operator+() const; // TODO: Implement
// Unary - operator (Negation)
Vector3 operator-() const;
public:
float x = 0;
float y = 0;
float z = 0;
};
}

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include <J3ML/Geometry.h>
int main(int argc, char** argv)
{

View File

@@ -175,5 +175,26 @@ namespace LinearAlgebra {
};
}
Quaternion Matrix3x3::ToQuat() const {
auto m00 = At(0,0);
auto m01 = At(0, 1);
auto m02 = At(0, 2);
auto m10 = At(1,0);
auto m11 = At(1, 1);
auto m12 = At(1, 2);
auto m20 = At(2,0);
auto m21 = At(2, 1);
auto m22 = At(2, 2);
auto w = std::sqrt(1.f + m00 + m11 + m22) / 2.f;
float w4 = (4.f * w);
return {
(m21 - m12) / w4,
(m02 - m20) / w4,
(m10 - m01) / w4,
w
};
}
}

View File

@@ -1,5 +1,80 @@
#include <J3ML/LinearAlgebra/Matrix4x4.h>
#include <J3ML/LinearAlgebra/Vector4.h>
namespace LinearAlgebra {
const Matrix4x4 Matrix4x4::Zero = Matrix4x4(0);
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);
Matrix4x4::Matrix4x4(const Vector4 &r1, const Vector4 &r2, const Vector4 &r3, const Vector4 &r4) {
this->elems[0][0] = r1.x;
this->elems[0][1] = r1.y;
this->elems[0][2] = r1.z;
this->elems[0][3] = r1.w;
this->elems[1][0] = r2.x;
this->elems[1][1] = r2.y;
this->elems[1][2] = r2.z;
this->elems[1][3] = r2.w;
this->elems[2][0] = r3.x;
this->elems[2][1] = r3.y;
this->elems[2][2] = r3.z;
this->elems[2][3] = r3.w;
this->elems[3][0] = r4.x;
this->elems[3][1] = r4.y;
this->elems[3][2] = r4.z;
this->elems[3][3] = r4.w;
}
Matrix4x4::Matrix4x4(float val) {
this->elems[0][0] = val;
this->elems[0][1] = val;
this->elems[0][2] = val;
this->elems[0][3] = val;
this->elems[1][0] = val;
this->elems[1][1] = val;
this->elems[1][2] = val;
this->elems[1][3] = val;
this->elems[2][0] = val;
this->elems[2][1] = val;
this->elems[2][2] = val;
this->elems[2][3] = val;
this->elems[3][0] = val;
this->elems[3][1] = val;
this->elems[3][2] = val;
this->elems[3][3] = val;
}
Matrix4x4::Matrix4x4(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33) {
this->elems[0][0] = m00;
this->elems[0][1] = m01;
this->elems[0][2] = m02;
this->elems[0][3] = m03;
this->elems[1][0] = m10;
this->elems[1][1] = m11;
this->elems[1][2] = m12;
this->elems[1][3] = m13;
this->elems[2][0] = m20;
this->elems[2][1] = m21;
this->elems[2][2] = m22;
this->elems[2][3] = m23;
this->elems[3][0] = m30;
this->elems[3][1] = m31;
this->elems[3][2] = m32;
this->elems[3][3] = m33;
}
Matrix4x4::Matrix4x4(const Quaternion &orientation) {
}
}

View File

@@ -116,7 +116,6 @@ namespace LinearAlgebra {
{
auto numer = this->Dot(rhs);
auto denom = this->Magnitude() * rhs.Magnitude();
std::cout << numer << ", " << denom << std::endl;
return std::acos(numer / denom);
}

View File

@@ -269,7 +269,7 @@ namespace LinearAlgebra {
}
Vector3 Vector3::Sub(const Vector3 &lhs, const Vector3 &rhs) {
lhs.Sub(rhs);
return lhs.Sub(rhs);
}
Vector3 Vector3::Mul(float scalar) const {
@@ -288,5 +288,18 @@ namespace LinearAlgebra {
return lhs.Div(rhs);
}
Angle2D Vector3::AngleBetween(const Vector3 &rhs) const {
const auto Pi_x_180 = 180.f / M_PI;
auto dist = this->Distance(rhs);
float x = -(asinf((rhs.y - this->y) / dist));
float y = (atan2f(rhs.x - this->x,rhs.z - this->z));
return {x, y};
}
Angle2D Vector3::AngleBetween(const Vector3 &lhs, const Vector3 &rhs) // TODO: 3D Angle representation?
{
return lhs.AngleBetween(rhs);
}
#pragma endregion
}

View File

@@ -3,111 +3,118 @@
using Vector3 = LinearAlgebra::Vector3;
void EXPECT_V3_EQ(const Vector3& lhs, const Vector3& rhs)
{
EXPECT_FLOAT_EQ(lhs.x, rhs.x);
EXPECT_FLOAT_EQ(lhs.y, rhs.y);
EXPECT_FLOAT_EQ(lhs.z, rhs.z);
}
TEST(Vector3Test, V3_Constructor_Default)
{
EXPECT_EQ(Vector3(), Vector3::Zero);
EXPECT_V3_EQ(Vector3(), Vector3::Zero);
}
TEST(Vector3Test, V3_Constructor_XYZ)
{
Vector3 Input {0, 1, 0};
EXPECT_EQ(Input, Vector3::Down);
EXPECT_V3_EQ(Input, Vector3::Down);
}
TEST(Vector3Test, V3_Addition_Op) {
Vector3 A {};
Vector3 B {};
Vector3 A {1,1,1};
Vector3 B {2,2,2};
Vector3 ExpectedResult {};
Vector3 ExpectedResult {3,3,3};
EXPECT_EQ(A + B, ExpectedResult);
EXPECT_V3_EQ(A + B, ExpectedResult);
}
TEST(Vector3Test, V3_Addition_Method) {
Vector3 A {};
Vector3 B {};
Vector3 A {1,1,1};
Vector3 B {2,2,2};
Vector3 ExpectedResult {};
Vector3 ExpectedResult {3,3,3};
EXPECT_EQ(A.Add(B), ExpectedResult);
EXPECT_V3_EQ(A.Add(B), ExpectedResult);
}
TEST(Vector3Test, V3_Addition_Static) {
Vector3 A {};
Vector3 B {};
Vector3 A {1,1,1};
Vector3 B {3,3,3};
Vector3 ExpectedResult {};
Vector3 ExpectedResult {4,4,4};
EXPECT_EQ(Vector3::Add(A, B), ExpectedResult);
EXPECT_V3_EQ(Vector3::Add(A, B), ExpectedResult);
}
TEST(Vector3Test, V3_Subtract_Op) {
Vector3 A {};
Vector3 B {};
Vector3 A {2,2,2};
Vector3 B {.5f, .5f, .5f};
Vector3 ExpectedResult {};
Vector3 ExpectedResult {1.5f, 1.5f, 1.5f};
EXPECT_EQ(A - B, ExpectedResult);
EXPECT_V3_EQ(A - B, ExpectedResult);
}
TEST(Vector3Test, V3_Subtract_Method) {
Vector3 A {};
Vector3 B {};
Vector3 A {3,3,3};
Vector3 B {1,1,1};
Vector3 ExpectedResult {};
Vector3 ExpectedResult {2,2,2};
EXPECT_EQ(A.Sub(B), ExpectedResult);
EXPECT_V3_EQ(A.Sub(B), ExpectedResult);
}
TEST(Vector3Test, V3_Subtract_Static) {
Vector3 A {};
Vector3 B {};
Vector3 A {4,4,4};
Vector3 B {1,1,1};
Vector3 ExpectedResult {};
Vector3 ExpectedResult {3,3,3};
EXPECT_EQ(Vector3::Sub(A, B), ExpectedResult);
EXPECT_V3_EQ(Vector3::Sub(A, B), ExpectedResult);
}
TEST(Vector3Test, V3_Scalar_Mult_Op) {
Vector3 A { };
Vector3 A { 1,1,1};
float B = 1.5f;
Vector3 ExpectedResult {};
Vector3 ExpectedResult {1.5f, 1.5f, 1.5f};
EXPECT_EQ(A * B, ExpectedResult);
EXPECT_V3_EQ(A * B, ExpectedResult);
}
TEST(Vector3Test, V3_Scalar_Mult_Method) {
Vector3 A { };
Vector3 A {3,3,3};
float B = 1.5f;
Vector3 ExpectedResult {};
Vector3 ExpectedResult {4.5f, 4.5f, 4.5f};
EXPECT_EQ(A.Mul(B), ExpectedResult);
EXPECT_V3_EQ(A.Mul(B), ExpectedResult);
}
TEST(Vector3Test, V3_Scalar_Mult_Static) {
Vector3 A { };
Vector3 A {2,2,2};
float B = 1.5f;
Vector3 ExpectedResult {};
Vector3 ExpectedResult {3.f, 3.f, 3.f};
EXPECT_EQ(Vector3::Mul(A, B), ExpectedResult);
EXPECT_V3_EQ(Vector3::Mul(A, B), ExpectedResult);
}
TEST(Vector3Test, V3_Scalar_Div_Op) {
Vector3 A {};
float B = 1.5f;
Vector3 A {4,4,4};
float B = 2.f;
Vector3 ExpectedResult { };
EXPECT_EQ(A / B, ExpectedResult);
Vector3 ExpectedResult {2,2,2};
EXPECT_V3_EQ(A / B, ExpectedResult);
}
TEST(Vector3Test, V3_Scalar_Div_Method) {
Vector3 A { };
float B = 1.5f;
Vector3 ExpectedResult { };
Vector3 A {6,6,6};
float B = 2.f;
Vector3 ExpectedResult { 3,3,3};
EXPECT_EQ(A.Div(B), ExpectedResult);
EXPECT_V3_EQ(A.Div(B), ExpectedResult);
}
TEST(Vector3Test, V3_Scalar_Div_Static) {
Vector3 A { };
Vector3 A {3,3,3};
float B = 1.5f;
Vector3 ExpectedResult { };
Vector3 ExpectedResult { 2.f, 2.f, 2.f};
EXPECT_EQ(Vector3::Div(A, B), ExpectedResult);
EXPECT_V3_EQ(Vector3::Div(A, B), ExpectedResult);
}
TEST(Vector3Test, V3_Sizeof) {
EXPECT_EQ(sizeof(Vector3), 12);
@@ -116,12 +123,75 @@ TEST(Vector3Test, V3_NaN) {
EXPECT_NE(Vector3(0, 0, 0), Vector3::NaN);
}
TEST(Vector3Test, V3_Min) {}
TEST(Vector3Test, V3_Max) {}
TEST(Vector3Test, V3_Clamp) {}
TEST(Vector3Test, V3_DotProduct) {}
TEST(Vector3Test, V3_CrossProduct) {}
TEST(Vector3Test, V3_Project) {}
TEST(Vector3Test, V3_Normalize) {}
TEST(Vector3Test, V3_Lerp) {}
TEST(Vector3Test, V3_AngleBetween) {}
TEST(Vector3Test, V3_Min) {
Vector3 Input {2,2,2};
Vector3 Minimum {3,3,3};
Vector3 ExpectedResult {2,2,2};
EXPECT_V3_EQ(Input.Min(Minimum), ExpectedResult);
}
TEST(Vector3Test, V3_Max) {
Vector3 Input {2,2,2};
Vector3 Maximum {3,3,3};
Vector3 ExpectedResult {3,3,3};
EXPECT_V3_EQ(Input.Max(Maximum), ExpectedResult);
}
TEST(Vector3Test, V3_Clamp) {
Vector3 Input {5,-1,8};
Vector3 Minimum {1,1,1};
Vector3 Maximum {5,5,5};
Vector3 ExpectedResult {5,1,5};
EXPECT_V3_EQ(Input.Clamp(Minimum, Maximum), ExpectedResult);
}
TEST(Vector3Test, V3_DotProduct) {
Vector3 A{6,6,6};
Vector3 B{1,1,1};
float ExpectedResult = 1;
EXPECT_FLOAT_EQ(A.Dot(B), ExpectedResult);
}
TEST(Vector3Test, V3_CrossProduct) {
Vector3 A{1,1,1};
Vector3 B{2,2,2};
Vector3 ExpectedResult {0,0,0};
EXPECT_V3_EQ(A.Cross(B), ExpectedResult);
}
TEST(Vector3Test, V3_Project) {
Vector3 Base {};
Vector3 Projection {};
Vector3 ExpectedResult {};
}
TEST(Vector3Test, V3_Normalize) {
Vector3 Input {2, 0, 0};
Vector3 ExpectedResult {1, 0, 0};
EXPECT_V3_EQ(Input.Normalize(), ExpectedResult);
}
TEST(Vector3Test, V3_Lerp)
{
Vector3 Start {};
Vector3 Finish {};
float Percent = 50;
Vector3 ExpectedResult {};
EXPECT_V3_EQ(Start.Lerp(Finish, Percent), ExpectedResult);
}
TEST(Vector3Test, V3_AngleBetween) {
Vector3 A{ .5f, .5f, .5f};
Vector3 B {.25f, .75f, .25f};
A = A.Normalize();
B = B.Normalize();
LinearAlgebra::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);
EXPECT_FLOAT_EQ(angle.y, ExpectedResult.y);
}