Compare commits

...

20 Commits

Author SHA1 Message Date
32577f79b8 Header implementations 2024-01-29 14:50:00 -05:00
e76a0954d3 Implement Vector-to-Vector Multiplication + division 2024-01-29 14:43:51 -05:00
47b25c695f Partial Implement QuadTree 2024-01-29 14:43:26 -05:00
6c7d63e467 Implement AABB2D 2024-01-29 03:16:50 -05:00
16c8dd1998 Implement QuadTree 2D 2024-01-26 15:55:51 -05:00
79f6b2f154 Implement Vector2 functions 2024-01-26 15:55:42 -05:00
08974413ae Implement AABB2D 2024-01-26 15:53:25 -05:00
256fe730cd Implement Geometric Class Definitions (round 2) 2024-01-26 00:14:28 -05:00
4152b0d2aa Implement Geometric Class Definitions 2024-01-25 19:09:48 -05:00
56077b7c86 Implement AABB 2024-01-25 19:09:36 -05:00
5cd5a44963 Implement Geometric Types 2024-01-25 14:01:19 -05:00
d012af1214 Implement Transform2D 2024-01-23 21:46:07 -05:00
f04e08201d Merge remote-tracking branch 'origin/main' 2024-01-23 04:14:34 -05:00
f7a7ec38d7 Implement Transform2D.h 2024-01-23 04:14:24 -05:00
a64f129bf7 Make it static 2024-01-15 16:09:44 -05:00
fd2289cee3 Implement V3::Direction 2024-01-15 15:20:32 -05:00
03f0193df7 Implement M3x3::SetColumn 2024-01-15 15:17:39 -05:00
1eae732718 Implement D3DOrtho 2024-01-12 10:27:36 -05:00
32046d88cd Implement Diagonal 2024-01-12 05:53:12 -05:00
93759ba545 Jim 2024-01-12 05:17:01 -05:00
47 changed files with 1467 additions and 155 deletions

View File

@@ -4,13 +4,10 @@ PROJECT(J3ML
LANGUAGES CXX
)
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed")
endif()
set(CMAKE_CXX_STANDARD 20)
#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (WIN32)
@@ -33,7 +30,34 @@ include_directories("include")
add_library(J3ML SHARED ${J3ML_SRC}
src/J3ML/LinearAlgebra/AxisAngle.cpp
include/J3ML/LinearAlgebra/Vector.h)
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)
set_target_properties(J3ML PROPERTIES LINKER_LANGUAGE CXX)
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})

View File

@@ -1,3 +1,4 @@
#include <J3ML/LinearAlgebra/Vector2.h>
#include <J3ML/LinearAlgebra/Vector3.h>
#pragma once
@@ -12,97 +13,16 @@ namespace Geometry {
Vector2 B;
};
class Rectangle; //AABB2D;
class Rectangle;
class OBB2D;
class Line2D;
class Ray2D;
class Triangle2D;
class Polygon2D;
struct IntersectionResult2D {
};
struct IntersectionResult2D {};
bool Intersects2D(LineSegment2D seg, Rectangle rect);
IntersectionResult2D GetIntersection2D(LineSegment2D seg, Rectangle rect);
// 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
// intersection queries.
// the 'Axis-aligned' part in the name means that the local axes of this bounding box are restricted to align with the
// axes of the world space coordinate system. This makes computation involving AABB's very fast, since AABB's cannot
// 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 Capsule;
class Line;
class LineSegment
{
Vector3 A;
Vector3 B;
};
class Ray
{
Vector3 Origin;
Vector3 Direction;
};
class OBB;
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;
class OctTree;
class Sphere;
class Triangle;
class TriangleMesh;
}

View File

@@ -0,0 +1,96 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.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>
// TODO: Fix circular include between AABB and OBB
namespace Geometry
{
using namespace LinearAlgebra;
// 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
// intersection queries.
// the 'Axis-aligned' part in the name means that the local axes of this bounding box are restricted to align with the
// axes of the world space coordinate system. This makes computation involving AABB's very fast, since AABB's cannot
// 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
{
static AABB FromCenterAndSize(const Vector3 FromSize);
float MinX();
// 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;
// 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 GetCentroid() const;
// Quickly returns an arbitrary point inside this AABB
Vector3 AnyPointFast() const;
Vector3 PointInside(float x, float y, float z) const;
// Returns an edge of this AABB
LineSegment Edge(int edgeIndex) const;
Vector3 CornerPoint(int cornerIndex);
Vector3 ExtremePoint(const Vector3& direction) const;
Vector3 ExtremePoint(const Vector3& direction, float projectionDistance);
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 GetSize();
Vector3 GetVolume();
float GetVolumeCubed();
float GetSurfaceArea();
Vector3 GetRandomPointInside();
Vector3 GetRandomPointOnSurface();
Vector3 GetRandomPointOnEdge();
Vector3 GetRandomCornerPoint();
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);
bool Contains(const Vector3& point) 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 Polygon& polygon) const;
bool Contains(const Frustum& frustum) const;
bool Contains(const Polyhedron& polyhedron) const;
bool Contains(const Capsule& capsule) const;
// Tests whether this AABB and the given object intersect.
bool Intersects(const Ray& ray, float dNear, float dFar) 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;
TriangleMesh Triangulate(int numFacesX, int numFacesY, int numFacesZ, bool ccwIsFrontFacing) const;
AABB Intersection(const AABB& rhs) const;
bool IntersectLineAABB(const Vector3& linePos, const Vector3& lineDir, float tNear, float tFar) const;
};
}

View File

@@ -0,0 +1,106 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector2.h>
namespace Geometry
{
using LinearAlgebra::Vector2;
// CaveGame AABB
class AABB2D
{
public:
Vector2 minPoint;
Vector2 maxPoint;
AABB2D() {}
AABB2D(const Vector2& min, const Vector2& max):
minPoint(min), maxPoint(max)
{}
float Width() const { return maxPoint.x - minPoint.x; }
float Height() const { return maxPoint.y - minPoint.y; }
float DistanceSq(const Vector2& pt) const
{
Vector2 cp = pt.Clamp(minPoint, maxPoint);
return cp.DistanceSq(pt);
}
void SetNegativeInfinity();
void Enclose(const Vector2& point)
{
minPoint = Vector2::Min(minPoint, point);
maxPoint = Vector2::Max(maxPoint, 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 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 Vector2& pt) const
{
return pt.x >= minPoint.x && pt.y >= minPoint.y
&& pt.x <= maxPoint.x && pt.y <= maxPoint.y;
}
bool Contains(int x, int y) const
{
return x >= minPoint.x && y >= minPoint.y
&& x <= maxPoint.x && y <= maxPoint.y;
}
bool IsDegenerate() const
{
return minPoint.x >= maxPoint.x || minPoint.y >= maxPoint.y;
}
bool HasNegativeVolume() const
{
return maxPoint.x < minPoint.x || maxPoint.y < minPoint.y;
}
bool IsFinite() const
{
return minPoint.IsFinite() && maxPoint.IsFinite() && minPoint.MinElement() > -1e5f && maxPoint.MaxElement() < 1e5f;
}
Vector2 PosInside(const Vector2 &normalizedPos) const
{
return minPoint + normalizedPos.Mul(maxPoint - minPoint);
}
Vector2 ToNormalizedLocalSpace(const Vector2 &pt) const
{
return (pt - minPoint).Div(maxPoint - minPoint);
}
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 a;
a.minPoint = minPoint - pt;
a.maxPoint = maxPoint - pt;
return a;
}
};
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "LineSegment.h"
#include <J3ML/LinearAlgebra/Vector3.h>
namespace Geometry
{
using namespace LinearAlgebra;
class Capsule
{
// Specifies the two inner points of this capsule
LineSegment l;
// Specifies the radius of this capsule
float r;
Capsule() {}
Capsule(const LineSegment& endPoints, float radius);
Capsule(const Vector3& bottomPt, const Vector3& topPt, float radius);
bool IsDegenerate()const;
float Height() const;
float Diameter() const;
Vector3 Bottom() const;
Vector3 Center() const;
Vector3 Centroid() const;
Vector3 ExtremePoint(const Vector3& direction);
};
}

View File

@@ -0,0 +1,55 @@
//
// Created by dawsh on 1/25/24.
//
#pragma once
#include "Plane.h"
namespace Geometry
{
enum class FrustumType
{
Invalid,
/// 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.
/// This corresponds to the optics in the real-world, and objects become smaller as they move to the distance.
/// The shape of the Frustum is a rectangular pyramid capped from the tip.
Perspective
};
class Frustum {
public:
Plane TopFace;
Plane BottomFace;
Plane RightFace;
Plane LeftFace;
Plane FarFace;
Plane NearFace;
static Frustum CreateFrustumFromCamera(const Camera& cam, float aspect, float fovY, float zNear, float zFar);
};
Frustum 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;
}
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
namespace Geometry
{
class LineSegment
{
Vector3 A;
Vector3 B;
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <J3ML/Geometry/AABB.h>
#include "Polyhedron.h"
namespace Geometry {
class OBB
{
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
Vector3 axis[3];
OBB() {}
OBB(const Vector3& pos, const Vector3& radii, const Vector3& axis0, const Vector3& axis1, const Vector3& axis2);
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;
AABB MinimalEnclosingAABB() const;
Sphere MinimalEnclosingSphere() const;
Sphere MaximalContainedSphere() const;
Vector3 Size() const;
Vector3 HalfSize() const;
Vector3 Diagonal() const;
Vector3 HalfDiagonal() const;
bool IsFinite() const;
bool IsDegenerate() const;
Vector3 CenterPoint() const;
Vector3 Centroid() const;
Vector3 AnyPointFast() const;
float Volume();
float SurfaceArea();
LineSegment Edge(int edgeIndex) const;
Vector3 CornerPoint(int cornerIndex) const;
};
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
using namespace LinearAlgebra;
class Plane
{
public:
Vector3 Position;
Vector3 Normal;
float distance = 0.f;
};

View File

@@ -0,0 +1,7 @@
#pragma once
namespace Geometry {
class Polygon {
};
}

View File

@@ -0,0 +1,8 @@
#pragma once
namespace Geometry
{
class Polyhedron {
};
}

View File

@@ -0,0 +1,358 @@
#pragma once
#include <vector>
#include <cstdint>
#include <J3ML/LinearAlgebra/Vector2.h>
#include "AABB2D.h"
namespace Geometry {
using LinearAlgebra::Vector2;
template<typename T>
class QuadTree {
public:
struct Node {
Node *parent;
uint32_t childIndex;
std::vector<T> objects;
Vector2 center;
Vector2 radius;
bool IsLeaf() const { return childIndex == 0xFFFFFFFF; }
uint32_t TopLeftChildIndex() const { return childIndex; }
uint32_t TopRightChildIndex() const { return childIndex + 1; }
uint32_t BottomLeftChildIndex() const { return childIndex + 2; }
uint32_t BottomRightChildIndex() const { return childIndex + 3; }
/// This assumes that the QuadTree contains unique objects and never duplicates
void Remove(const T &object) {
for (size_t i = 0; i < objects.size(); ++i) {
if (objects[i] == object) {
AssociateQuadTreeNode(object,
0); // Mark in the object that it has been removed from the QuadTree.
std::swap(objects[i], objects.back());
objects.pop_back();
return;
}
}
}
AABB2D ComputeAABB() {}
float DistanceSq(const Vector2 &point) const {
Vector2 centered = point - center;
Vector2 closestPoint = centered.Clamp(-radius, radius);
return closestPoint.DistanceSq(centered);
}
};
// Helper struct used when traversing through the tree
struct TraversalStackItem {
Node *node;
};
QuadTree() :
rootNodeIndex(-1),
boundingAABB(Vector2(0, 0), Vector2(0, 0)) {
// TODO: currently storing persistent raw pointers to this array outside the array
nodes.reserve(200000);
}
/// Removes all nodes and objects in this tree and reintializes the tree to a single root node.
void Clear(const Vector2 &minXY = Vector2(-1, -1), const Vector2 &maxXY = Vector2(1, 1));
/// Places the given object onto the proper (leaf) node of the tree. After placing, if the leaf split rule is
/// satisfied, subdivides the leaf node into 4 subquadrants and reassings the objects to the new leaves.
void Add(const T &object);
/// Removes the given object from this tree.
/// To call this function, you must define a function QuadTree<T>::Node *GetQuadTreeNode(const T& object)
/// which returns the node of this quadtree where the object resides in.
void Remove(const T &object);
/// @return The bounding rectangle for the whole tree.
/// @note This bounding rectangle does not tightly bound the objects themselves, only the root node of the tree.
AABB2D BoundingAABB() const { return boundingAABB; }
/// @return The topmost node in the tree.
Node *Root();
const Node *Root() const;
/// Returns the total number of nodes (all nodes, i.e. inner nodes + leaves) in the tree.
/// Runs in constant time.
int NumNodes() const;
/// Returns the total number of leaf nodes in the tree.
/// @warning Runs in time linear 'O(n)' to the number of nodes in the tree.
int NumLeaves() const;
/// Returns the total number of inner nodes in the tree.
/// @warning Runs in time linear 'O(n)' to the number of nodes in the tree.
int NumInnerNodes() const;
/// Returns the total number of objects stored in the tree.
/// @warning Runs in time linear 'O(n)' to the number of nodes in the tree.
int NumObjects() const;
/// Returns the maximum height of the whole tree (the path from the root to the farthest leaf node).
int TreeHeight() const;
/// Returns the height of the subtree rooted at 'node'.
int TreeHeight(const Node *node) const;
/// Performs an AABB intersection query in this Quadtreee, and calls the given callback function for each non-empty
/// node of the tree which intersects the given AABB.
/** @param aabb The axis-aligned bounding box to intersect this QuadTree with.
@param callback A function or a function object of prototype
bool callbackFunction(QuadTree<T> &tree, const AABB2D &queryAABB, QuadTree<T>::Node &node);
If the callback function returns true, the execution of the query is stopped and this function immediately
returns afterwards. If the callback function returns false, the execution of the query continues. */
template<typename Func>
inline void AABBQuery(const AABB2D &aabb, Func &callback);
/// Finds all object pairs inside the given AABB which have colliding AABBs. For each such pair, calls the
/// specified callback function.
template<typename Func>
inline void CollidingPairsQuery(const AABB2D &aabb, Func &callback);
/// Performs various consistency checks on the given node. Use only for debugging purposes.
void DebugSanityCheckNode(Node *n);
private:
void Add(const T &object, Node *n);
/// Allocates a sequential 4-tuple of QuadtreeNodes, contiguous in memory.
int AllocateNodeGroup(Node *parent);
void SplitLeaf(Node *leaf);
std::vector<Node> nodes;
int rootNodeIndex;
AABB2D boundingAABB;
void GrowRootTopLeft();
void GrowRootTopRight();
void GrowRootBottomLeft();
void GrowRootBottomRight();
void GrowImpl(int quadrantForRoot);
};
// NOTE: Keep members defined here. Template-parameterized classes
// can't be split across header and implementation files
// but the presence of the implementation file is a requirement
template<typename T>
void QuadTree<T>::Clear(const Vector2 &minXY, const Vector2 &maxXY) {
nodes.clear();
boundingAABB.minPoint = minXY;
boundingAABB.maxPoint = maxXY;
rootNodeIndex = AllocateNodeGroup(0);
Node *root = Root();
root->center = (minXY + maxXY) * 0.5f;
root->radius = maxXY - root->center;
}
template<typename T>
void QuadTree<T>::Add(const T &object) {
Node *n = Root();
AABB2D objectAABB = GetAABB2D(object);
// Ramen Noodle Bowls of nested if statements are generally bad practice
// Unfortunately, sometimes the problem domain makes this unavoidable
if (objectAABB.minPoint.x >= boundingAABB.minPoint.x) {
// object fits left.
if (objectAABB.maxPoint.x <= boundingAABB.maxPoint.x) {
// object fits left and right.
if (objectAABB.minPoint.y >= boundingAABB.minPoint.y) {
// Object fits left, right, and top.
if (objectAABB.maxPoint.y <= boundingAABB.maxPoint.y) {
// Object fits the whole root AABB. Can safely add into the existing tree size.
AddObject(object, n);
return;
} else {
// Object fits left, right, and top, but not bottom.
GrowRootBottomRight(); // Could grow bottom-left as well, wouldn't matter here.
}
} else {
// Object fits left and right, but not to top.
GrowRootTopRight(); // Could grow top-left as well, wouldn't matter here.
}
} else {
// Object fits left, but not to right. We must grow right. Check whether to grow top or bottom;
if (objectAABB.minPoint.y < boundingAABB.minPoint.y)
GrowRootTopRight();
else
GrowRootBottomRight();
}
} else {
// We must grow left. Check whether to grow top or bottom.
if (objectAABB.minPoint.y < boundingAABB.minPoint.y)
GrowRootTopLeft();
else
GrowRootBottomLeft();
}
// Now that we have grown the tree root node, try adding again.
Add(object);
}
template<typename T>
void QuadTree<T>::Remove(const T &object) {
Node *n = GetQuadTreeNode(object);
if (n) {
n->Remove(object);
}
}
template<typename T>
void QuadTree<T>::Add(const T &object, Node *n) {
for (;;) {
// Traverse the QuadTree to decide which quad to place this object on.
float left = n->center.x - MinX(object); // If left > 0.f, then the object overlaps with the left quadrant.
float right = MaxX(object) - n->center.x; // If right > 0.f, then the object overlaps with the right quadrant.
float top = n->center.y - MinY(object); // If top > 0.f, then the object overlaps with the top quadrant
float bottom =
MaxY(object) - n->center.y; // If bottom > 0.f, then the object overlaps with the bottom quadrant
float leftAndRight = std::min(left, right); // If > 0.f, then the object straddles left-right halves.
float topAndBottom = std::min(top, bottom); // If > 0.f, then the object straddles top-bottom halves.
float straddledEitherOne = std::max(leftAndRight,
topAndBottom); // If > 0.f, thne the object is in two or more quadrants.
// Note: It can happen that !left && !right, or !top && !bottom.
// but the if()s are setup below so that right/bottom is taken if no left/top, so that is ok.
// We must put the object onto this node if
// a) the object straddled the parent->child split lines.
// b) this object is a leaf
if (straddledEitherOne > 0.f) {
n->objects.push_back(object);
AssociateQuadTreeNode(object, n);
return;
}
if (n->IsLeaf()) {
n->objects.push_back(object);
AssociateQuadTreeNode(object, n);
if ((int) n->objects.size() > minQuadTreeNodeObjectCount &&
std::min(n->radius.x, n->radius.y) >= minQuadTreeQuadrantSize) {
SplitLeaf(n);
}
return;
}
if (left > 0.f) {
if (top > 0.f) {
n = &nodes[n->TopLeftChildIndex()];
} else {
n = &nodes[n->BottomLeftChildIndex()];
}
} else {
if (top > 0.f) {
n = &nodes[n->TopRightChildIndex()];
} else {
n = &nodes[n->BottomRightChildIndex()];
}
}
}
}
template<typename T>
typename QuadTree<T>::Node *QuadTree<T>::Root() {
return nodes.empty() ? 0 : &nodes[rootNodeIndex];
}
template<typename T>
const typename QuadTree<T>::Node *QuadTree<T>::Root() const {
return nodes.empty() ? 0 : &nodes[rootNodeIndex];
}
template <typename T>
int QuadTree<T>::AllocateNodeGroup(Node* parent) {
size_t oldCap = nodes.capacity();
int index = (int)nodes.size();
Node n;
n.parent = parent;
n.childIndex = 0xFFFFFFFF;
if (parent) {
n.radius = parent->radius * 0.5f;
n.center = parent->center - n.radius;
}
nodes.push_back(n);
if (parent)
n.center.x = parent->center.x + n.radius.x;
nodes.push_back(n);
if (parent) {
n.center.x = parent->center.x - n.radius.x;
n.center.y = parent->center.y + n.radius.y;
}
nodes.push_back(n);
if (parent)
n.center.x = parent->center.x + n.radius.x;
nodes.push_back(n);
return index;
}
template <typename T>
void QuadTree<T>::SplitLeaf(Node *leaf) {
leaf->childIndex = AllocateNodeGroup(leaf);
size_t i = 0;
while (i < leaf->objects.size()) {
const T& object = leaf->objects[i];
// Traverse the QuadTree to decide which quad to place this object into
float left = leaf->center.x - MinX(object);
float right = MaxX(object) - leaf->center;
float top = leaf->center.y - MinY(object);
float bottom = MaxY(object) - leaf->center.y;
float leftAndRight = std::min(left, right);
float topAndBottom = std::min(top, bottom);
float straddledEitherOne = std::max(leftAndRight, topAndBottom);
if (straddledEitherOne > 0.f) {
++i;
continue;
}
if (left > 0.f) {
if (top > 0.f) {
Add(object, &nodes[leaf->TopLeftChildIndex()]);
} else {
Add(object, &nodes[leaf->BottomLeftChildIndex()]);
}
} else {
if (top > 0.f) {
Add(object, &nodes[leaf->TopRightChildIndex()]);
} else {
Add(object, &nodes[leaf->BottomRightChildIndex()]);
}
}
// Remove the object we added to a child from this node.
leaf->objects[i] = leaf->objects.back();
leaf->objects.pop_back();
}
}
}

View File

@@ -0,0 +1,16 @@
//
// Created by dawsh on 1/25/24.
//
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
namespace Geometry
{
class Ray
{
Vector3 Origin;
Vector3 Direction;
};
}

View File

@@ -0,0 +1,9 @@
#pragma once
namespace Geometry
{
class Sphere
{
};
}

View File

@@ -0,0 +1,9 @@
#pragma once
namespace Geometry
{
class Triangle
{
};
}

View File

@@ -0,0 +1,8 @@
//
// Created by dawsh on 1/25/24.
//
#ifndef J3ML_TRIANGLE2D_H
#define J3ML_TRIANGLE2D_H
#endif //J3ML_TRIANGLE2D_H

View File

@@ -0,0 +1,9 @@
#pragma once
namespace Geometry
{
class TriangleMesh
{
};
}

View File

@@ -1,8 +1,17 @@
//
// Created by josh on 12/25/2023.
//
#include <cstdint>
#ifndef J3ML_J3ML_H
#define J3ML_J3ML_H
namespace J3ML
{
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
#endif //J3ML_J3ML_H
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
}

View File

@@ -13,6 +13,12 @@ namespace LinearAlgebra
float angle;
public:
AxisAngle();
AxisAngle(const Vector3& axis, float angle);
AxisAngle(const Vector3 &axis, float angle);
EulerAngle ToEulerAngleXYZ() const;
Quaternion ToQuaternion() const;
static AxisAngle FromEulerAngleXYZ(const EulerAngle&);
};
}

View File

@@ -11,8 +11,13 @@ public:
EulerAngle();
EulerAngle(float pitch, float yaw, float roll);
EulerAngle(const Vector3& vec) : pitch(vec.x), yaw(vec.y), roll(vec.z) {}
static EulerAngle FromRadians(float radians);
static EulerAngle FromDegrees(float degrees);
AxisAngle ToAxisAngle() const;
explicit EulerAngle(const Quaternion& orientation);
explicit EulerAngle(const AxisAngle& orientation);
/// TODO: Implement separate upper and lower bounds
/// Preserves internal value of euler angles, normalizes and clamps the output.
/// This does not solve gimbal lock!!!

View File

@@ -43,16 +43,40 @@ namespace LinearAlgebra {
Vector3 GetRow(int index) const;
Vector3 GetColumn(int index) const;
float At(int x, int y) const;
/// Creates a new M3x3 that rotates about the given axis by the given angle
static Matrix3x3 RotateAxisAngle(const Vector3& rhs);
void SetRotatePart(const Vector3& a, float angle);
static Matrix3x3 RotateFromTo(const Vector3& source, const Vector3& direction);
/// 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)
{
}
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);
}
static Matrix3x3 LookAt(const Vector3& forward, const Vector3& target, const Vector3& localUp, const Vector3& worldUp);
static Matrix3x3 FromQuat(const Quaternion& orientation);
static Matrix3x3 FromQuat(const Quaternion& orientation)
{
return Matrix3x3(orientation);
}
Quaternion ToQuat() const;
@@ -62,14 +86,20 @@ namespace LinearAlgebra {
// Transforming a vector v using this matrix computes the vector
// v' == M * v == R*S*v == (R * (S * v)) which means the scale operation
// is applied to the vector first, followed by rotation, and finally translation
static Matrix3x3 FromRS(const Quaternion& rotate, const Matrix3x3& scale);
static Matrix3x3 FromRS(const Matrix3x3 &rotate, const Matrix3x3& scale);
static Matrix3x3 FromRS(const Quaternion& rotate, const Vector3& scale)
{
return Matrix3x3(rotate) * Matrix3x3::Scale(scale);
}
static Matrix3x3 FromRS(const Matrix3x3 &rotate, const Vector3& scale)
{
return rotate * Matrix3x3::Scale(scale);
}
/// 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 Matrix3x3& scale);
static Matrix3x3 Scale(const Vector3& scale);
/// Returns the main diagonal.
Vector3 Diagonal() const;
@@ -102,6 +132,7 @@ namespace LinearAlgebra {
Vector2 Transform(const Vector2& rhs) const;
Vector3 Transform(const Vector3& rhs) const;
Vector2 operator * (const Vector2& rhs) const;
Vector3 operator * (const Vector3& rhs) const;
Matrix3x3 operator * (const Matrix3x3& rhs) const;

View File

@@ -1,42 +1,79 @@
#pragma once
#include <J3ML/LinearAlgebra.h>
#include <J3ML/LinearAlgebra/Quaternion.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,
* including perspective projections, which a 4-by-3 cannot store,
* and translations, which a 3-by-3 cannot represent.
* 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, xm_23,
* m_30, m_31, m_32, m_33
*
* The element m_yx is the value on the row y and column x.
* You can access m_yx using the double-bracket notation m[y][x]
*/
/* This matrix can represent the most generic form of transformations for 3D objects,
* including perspective projections, which a 4-by-3 cannot store,
* and translations, which a 3-by-3 cannot represent.
* 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, am_23,
* m_30, m_31, m_32, m_33
*
* The element m_yx is the value on the row y and column x.
* You can access m_yx using the double-bracket notation m[y][x]
*/
class Matrix4x4 {
public:
enum { Rows = 4 };
enum { Cols = 4 };
// A constant matrix that has zeroes in all its entries
static const Matrix4x4 Zero;
// A constant matrix that is the identity.
static const Matrix4x4 Identity;
// A compile-time constant float4x4 which has NaN in each element.
// For this constant, each element has the value of quet NaN, or Not-A-Number.
// Never compare a matrix to this value. Due to how IEEE floats work, "nan == nan" returns false!
static const Matrix4x4 NaN;
Matrix4x4() {}
Matrix4x4(float val);
Matrix4x4(const Matrix3x3&);
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);
Vector3 GetTranslatePart() const;
Matrix3x3 GetRotatePart() const
{
return Matrix3x3 {
At(0, 0), At(0, 1), At(0, 2),
At(1, 0), At(1, 1), At(1, 2),
At(2, 0), At(2, 1), At(2, 2)
};
}
void SetTranslatePart(float translateX, float translateY, float translateZ);
void SetTranslatePart(const Vector3& offset);
void SetRotatePart(const Quaternion& q);
void SetRow(int row, const Vector3& rowVector, float m_r3);
void SetRow(int row, const Vector4& rowVector);
void SetRow(int row, float m_r0, float m_r1, float m_r2, float m_r3);
Matrix4x4(const Quaternion& orientation, const Vector3& translation);
Vector4 GetRow(int index) const;
Vector4 GetColumn(int index) const;
float At(int x, int y) const;
float At(int x, int y) const
{
return elems[x][y];
}
Vector4 Diagonal() const;
Vector4 WorldX() const;
@@ -47,7 +84,10 @@ namespace LinearAlgebra {
// If the determinant is nonzero, this matrix is invertible.
float Determinant() const;
Matrix4x4 Inverse() const;
Matrix4x4 Inverse() const
{
}
Matrix4x4 Transpose() const;
@@ -55,12 +95,31 @@ namespace LinearAlgebra {
Vector3 Transform(const Vector3& rhs) const;
Vector4 Transform(const Vector4& rhs) const;
static Matrix4x4 D3DOrthoProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
static Matrix4x4 D3DOrthoProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize);
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);
Vector3 GetTranslationComponent() const;
Matrix3x3 GetRotationComponent() const;
Vector4 GetRow() const;
Vector4 GetColumn() const;
Vector4 operator[](int row)
{
return Vector4{elems[row][0], elems[row][1], elems[row][2], elems[row][3]};
}
protected:
float elems[4][4];
void SetMatrixRotatePart(Matrix4x4 &m, const Quaternion &q);
};
}

View File

@@ -37,6 +37,20 @@ namespace LinearAlgebra
Vector3 GetWorldY() const;
Vector3 GetWorldZ() const;
Vector3 GetAxis() const
{
float rcpSinAngle = 1 - (std::sqrt(1 - w * w));
return Vector3(x, y, z) * rcpSinAngle;
}
float GetAngle() const
{
return std::acos(w) * 2.f;
}
Matrix3x3 ToMatrix3x3() const;
Vector3 Transform(const Vector3& vec) const;
@@ -57,7 +71,6 @@ namespace LinearAlgebra
// TODO: Document (But do not override!) certain math functions that are the same for Vec4
// TODO: Double Check which operations need to be overriden for correct behavior!
// Multiplies two quaternions together.
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
// The rotation q2 is applied first before q1.
@@ -71,9 +84,9 @@ namespace LinearAlgebra
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
Quaternion operator / (const Quaternion& rhs) const;
Quaternion operator +(const Quaternion& rhs) const;
Quaternion operator +() const;
Quaternion operator -() const;
Quaternion operator + (const Quaternion& rhs) const;
Quaternion operator + () const;
Quaternion operator - () const;
float Dot(const Quaternion &quaternion) const;
float Angle() const;

View File

@@ -8,12 +8,33 @@ namespace LinearAlgebra {
protected:
Matrix3x3 transformation;
public:
const static Transform2D Identity;
const static Transform2D FlipX;
const static Transform2D FlipY;
Transform2D(float rotation, const Vector2& pos);
Transform2D(float px, float py, float sx, float sy, float ox, float oy, float kx, float ky, float rotation)
{
transformation = Matrix3x3(px, py, rotation, sx, sy, ox, oy, kx, ky);
}
Transform2D(const Vector2& pos, const Vector2& scale, const Vector2& origin, const Vector2& skew, float rotation);
Transform2D(const Matrix3x3& transform);
Transform2D Translate(const Vector2& offset) const;
Transform2D Translate(float x, float y) const;
Transform2D Scale(float scale); // Perform Uniform Scale
Transform2D Scale(float x, float y); // Perform Nonunform Scale
Transform2D Scale(float scale); // Perform Uniform Scale
Transform2D Scale(float x, float y); // Perform Nonunform Scale
Transform2D Scale(const Vector2& scales); // Perform Nonuniform Scale
Transform2D Rotate();
Vector2 Transform(const Vector2& input) const;
Transform2D Inverse() const;
Transform2D AffineInverse() const;
float Determinant() const;
Vector2 GetOrigin() const;
float GetRotation() const;
Vector2 GetScale() const;
float GetSkew() const;
Transform2D OrthoNormalize();
};
}

View File

@@ -3,12 +3,12 @@
#include <cstddef>
namespace LinearAlgebra {
// A 2D (x, y) ordered pair.
/// A 2D (x, y) ordered pair.
class Vector2 {
public:
// Default Constructor - Initializes values to zero
/// Default Constructor - Initializes values to zero
Vector2();
// Constructs a new Vector2 with the value (X, Y)
/// Constructs a new Vector2 with the value (X, Y)
Vector2(float X, float Y);
Vector2(const Vector2& rhs); // Copy Constructor
//Vector2(Vector2&&) = default; // Move Constructor
@@ -44,73 +44,90 @@ namespace LinearAlgebra {
Vector2 Clamp(const Vector2& min, const Vector2& max) const;
static Vector2 Clamp(const Vector2& min, const Vector2& middle, const Vector2& max);
// Returns the magnitude between the two vectors.
/// Returns the magnitude between the two vectors.
float Distance(const Vector2& to) const;
static float Distance(const Vector2& from, const Vector2& to);
float DistanceSq(const Vector2& to) const;
static float DistanceSq(const Vector2& from, const Vector2& to);
float MinElement() const;
float MaxElement() const;
float Length() const;
static float Length(const Vector2& of);
float LengthSquared() const;
static float LengthSquared(const Vector2& of);
// Returns the length of the vector, which is sqrt(x^2 + y^2)
/// Returns the length of the vector, which is sqrt(x^2 + y^2)
float Magnitude() const;
static float Magnitude(const Vector2& of);
// Returns a float value equal to the magnitudes of the two vectors multiplied together and then multiplied by the cosine of the angle between them.
// For normalized vectors, dot returns 1 if they point in exactly the same direction,
// -1 if they point in completely opposite directions, and 0 if the vectors are perpendicular.
bool IsFinite() const;
static bool IsFinite(const Vector2& v);
/// Returns a float value equal to the magnitudes of the two vectors multiplied together and then multiplied by the cosine of the angle between them.
/// For normalized vectors, dot returns 1 if they point in exactly the same direction,
/// -1 if they point in completely opposite directions, and 0 if the vectors are perpendicular.
float Dot(const Vector2& rhs) const;
static float Dot(const Vector2& lhs, const Vector2& rhs);
// Projects one vector onto another and returns the result. (IDK)
/// Projects one vector onto another and returns the result. (IDK)
Vector2 Project(const Vector2& rhs) const;
// @see Project
/// @see Project
static Vector2 Project(const Vector2& lhs, const Vector2& rhs);
// Returns a copy of this vector, resized to have a magnitude of 1, while preserving "direction"
/// Returns a copy of this vector, resized to have a magnitude of 1, while preserving "direction"
Vector2 Normalize() const;
static Vector2 Normalize(const Vector2& of);
// Linearly interpolates between two points.
// Interpolates between the points and b by the interpolant t.
// The parameter is (TODO: SHOULD BE!) clamped to the range[0, 1].
// This is most commonly used to find a point some fraction of the wy along a line between two endpoints (eg. to move an object gradually between those points).
/// Linearly interpolates between two points.
/// Interpolates between the points and b by the interpolant t.
/// The parameter is (TODO: SHOULD BE!) clamped to the range[0, 1].
/// This is most commonly used to find a point some fraction of the wy along a line between two endpoints (eg. to move an object gradually between those points).
Vector2 Lerp(const Vector2& rhs, float alpha) const;
// @see Lerp
/// @see Lerp
static Vector2 Lerp(const Vector2& lhs, const Vector2& rhs, float alpha);
// Note: Input vectors MUST be normalized first!
/// Note: Input vectors MUST be normalized first!
float AngleBetween(const Vector2& rhs) const;
static float AngleBetween(const Vector2& lhs, const Vector2& rhs);
// Adds two vectors.
/// Adds two vectors.
Vector2 operator +(const Vector2& rhs) const;
Vector2 Add(const Vector2& rhs) const;
static Vector2 Add(const Vector2& lhs, const Vector2& rhs);
// Subtracts two vectors.
/// Subtracts two vectors.
Vector2 operator -(const Vector2& rhs) const;
Vector2 Sub(const Vector2& rhs) const;
static Vector2 Sub(const Vector2& lhs, const Vector2& rhs);
// Multiplies this vector by a scalar value.
/// Multiplies this vector by a scalar value.
Vector2 operator *(float rhs) const;
Vector2 Mul(float scalar) const;
static Vector2 Mul(const Vector2& lhs, float rhs);
// Divides this vector by a scalar.
/// 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.
Vector2 Mul(const Vector2& v) const;
/// Divides this vector by a scalar.
Vector2 operator /(float rhs) const;
Vector2 Div(float scalar) const;
static Vector2 Div(const Vector2& lhs, float rhs);
// Unary operator +
/// 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;
/// Unary operator +
Vector2 operator +() const; // TODO: Implement
Vector2 operator -() const;
// Assigns a vector to another
/// 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*=(float scalar);

View File

@@ -30,6 +30,38 @@ public:
static const Vector3 Backward;
static const Vector3 NaN;
static void Orthonormalize(Vector3& a, Vector3& b)
{
a = a.Normalize();
b = b - b.ProjectToNorm(a);
b = b.Normalize();
}
//Returns the DirectionVector for a given angle.
static Vector3 Direction(const Vector3 &rhs) ;
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)
{
}
Vector3 ProjectToNorm(const Vector3& direction)
{
return direction * this->Dot(direction);
}
float GetX() const;
float GetY() const;
float GetZ() const;

View File

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

View File

@@ -0,0 +1,5 @@
#include <J3ML/Geometry/AABB.h>
namespace Geometry {
}

View File

@@ -0,0 +1,6 @@
#include <J3ML/Geometry/Capsule.h>
namespace Geometry
{
}

View File

@@ -0,0 +1 @@
#include <J3ML/Geometry/Frustum.h>

View File

@@ -0,0 +1,3 @@
//
// Created by dawsh on 1/25/24.
//

View File

@@ -0,0 +1,3 @@
//
// Created by dawsh on 1/25/24.
//

View File

@@ -0,0 +1 @@
#include <J3ML/Geometry/Plane.h>

View File

@@ -0,0 +1,5 @@
#include <J3ML/Geometry/Polygon.h>
namespace Geometry {
}

View File

@@ -0,0 +1,6 @@
#include <J3ML/Geometry/Polyhedron.h>
namespace Geometry
{
}

View File

@@ -0,0 +1,6 @@
#include <J3ML/Geometry/QuadTree.h>
namespace Geometry
{
}

View File

@@ -0,0 +1,6 @@
#include <J3ML/Geometry/Ray.h>
namespace Geometry
{
}

View File

@@ -0,0 +1 @@
#include <J3ML/Geometry/Sphere.h>

View File

@@ -0,0 +1 @@
#include <J3ML/Geometry/TriangleMesh.h>

View File

@@ -1,8 +1,17 @@
#include <J3ML/LinearAlgebra/AxisAngle.h>
#include <J3ML/LinearAlgebra/Quaternion.h>
namespace LinearAlgebra {
AxisAngle::AxisAngle() : axis(Vector3::Zero) {}
AxisAngle::AxisAngle(const Vector3 &axis, float angle) : axis(axis), angle(angle) {}
Quaternion AxisAngle::ToQuaternion() const {
return {
axis.x * std::sin(angle/2),
axis.y * std::sin(angle/2),
axis.z * std::sin(angle/2),
std::cos(angle/2)
};
}
}

View File

@@ -37,9 +37,14 @@ namespace LinearAlgebra {
return {x, y, z};
}
float Matrix3x3::At(int x, int y) const {
return this->elems[x][y];
}
void Matrix3x3::SetAt(int x, int y, float value)
{
this->elems[x][y] = value;
}
Vector3 Matrix3x3::operator*(const Vector3 &rhs) const {
@@ -196,5 +201,100 @@ namespace LinearAlgebra {
};
}
void Matrix3x3::SetRotatePart(const Vector3 &a, float angle) {
float s = std::sin(angle);
float c = std::cos(angle);
const float c1 = 1.f - c;
elems[0][0] = c+c1*a.x*a.x;
elems[1][0] = c1*a.x*a.y+s*a.z;
elems[2][0] = c1*a.x*a.z-s*a.y;
elems[0][1] = c1*a.x*a.y-s*a.z;
elems[1][1] = c+c1*a.y*a.y;
elems[2][1] = c1*a.y*a.z+s*a.x;
elems[0][2] = c1*a.x*a.z+s*a.y;
elems[1][2] = c1*a.y*a.z-s*a.x;
elems[2][2] = c+c1*a.z*a.z;
}
Matrix3x3 Matrix3x3::RotateAxisAngle(const Vector3 &axis, float angleRadians) {
Matrix3x3 r;
r.SetRotatePart(axis, angleRadians);
return r;
}
void Matrix3x3::SetRow(int i, const Vector3 &vec) {
elems[i][0] = vec.x;
elems[i][1] = vec.y;
elems[i][2] = vec.z;
}
void Matrix3x3::SetColumn(int i, const Vector3& vec)
{
elems[0][i] = vec.x;
elems[1][i] = vec.y;
elems[2][i] = vec.z;
}
Matrix3x3
Matrix3x3::LookAt(const Vector3 &forward, const Vector3 &target, const Vector3 &localUp, const Vector3 &worldUp) {
// User must input proper normalized input direction vectors
// In the local space, the forward and up directions must be perpendicular to be well-formed.
// In the world space, the target direction and world up cannot be degenerate (co-linear)
// Generate the third basis vector in the local space;
Vector3 localRight = localUp.Cross(forward).Normalize();
// A. Now we have an orthonormal linear basis {localRight, localUp, forward} for the object local space.
// Generate the third basis vector for the world space
Vector3 worldRight = worldUp.Cross(target).Normalize();
// Since the input worldUp vector is not necessarily perpendicular to the target direction vector
// We need to compute the real world space up vector that the "head" of the object will point
// towards when the model is looking towards the desired target direction
Vector3 perpWorldUp = target.Cross(worldRight).Normalize();
// B. Now we have an orthonormal linear basis {worldRight, perpWorldUp, targetDirection } for the desired target orientation.
// We want to build a matrix M that performs the following mapping:
// 1. localRight must be mapped to worldRight. (M * localRight = worldRight)
// 2. localUp must be mapped to perpWorldUp. (M * localUp = perpWorldUp)
// 3. localForward must be mapped to targetDirection. (M * localForward = targetDirection)
// i.e. we want to map the basis A to basis B.
// This matrix M exists, and it is an orthonormal rotation matrix with a determinant of +1, because
// the bases A and B are orthonormal with the same handedness.
// Below, use the notation that (a,b,c) is a 3x3 matrix with a as its first column, b second, and c third.
// By algebraic manipulation, we can rewrite conditions 1, 2 and 3 in a matrix form:
// M * (localRight, localUp, localForward) = (worldRight, perpWorldUp, targetDirection)
// or M = (worldRight, perpWorldUp, targetDirection) * (localRight, localUp, localForward)^{-1}.
// or M = m1 * m2, where
// m1 equals (worldRight, perpWorldUp, target):
Matrix3x3 m1(worldRight, perpWorldUp, target);
// and m2 equals (localRight, localUp, localForward)^{-1}:
Matrix3x3 m2;
m2.SetRow(0, localRight);
m2.SetRow(1, localUp);
m2.SetRow(2, forward);
// Above we used the shortcut that for an orthonormal matrix M, M^{-1} = M^T. So set the rows
// and not the columns to directly produce the transpose, i.e. the inverse of (localRight, localUp, localForward).
// Compute final M.
m2 = m1 * m2;
// And fix any numeric stability issues by re-orthonormalizing the result.
m2.Orthonormalize(0, 1, 2);
return m2;
}
Vector3 Matrix3x3::Diagonal() const {
return {elems[0][0],
elems[1][1],
elems[2][2]
};
}
}

View File

@@ -77,4 +77,95 @@ namespace LinearAlgebra {
Matrix4x4::Matrix4x4(const Quaternion &orientation) {
}
void Matrix4x4::SetTranslatePart(float translateX, float translateY, float translateZ) {
elems[0][3] = translateX;
elems[1][3] = translateY;
elems[2][3] = translateZ;
}
void Matrix4x4::SetTranslatePart(const Vector3 &offset) {
elems[0][3] = offset.x;
elems[1][3] = offset.y;
elems[2][3] = offset.z;
}
void Matrix4x4::SetRotatePart(const Quaternion &q) {
SetMatrixRotatePart(*this, q);
}
void Matrix4x4::SetMatrixRotatePart(Matrix4x4 &m, const Quaternion& q)
{
// See e.g. http://www.geometrictools.com/Documentation/LinearAlgebraicQuaternions.pdf .
const float x = q.x;
const float y = q.y;
const float z = q.z;
const float w = q.w;
elems[0][0] = 1 - 2*(y*y + z*z); elems[0][1] = 2*(x*y - z*w); elems[0][2] = 2*(x*z + y*w);
elems[1][0] = 2*(x*y + z*w); elems[1][1] = 1 - 2*(x*x + z*z); elems[1][2] = 2*(y*z - x*w);
elems[2][0] = 2*(x*z - y*w); elems[2][1] = 2*(y*z + x*w); elems[2][2] = 1 - 2*(x*x + y*y);
}
void Matrix4x4::SetRow(int row, const Vector3 &rowVector, float m_r3) {
SetRow(row, rowVector.x, rowVector.y, rowVector.z, m_r3);
}
void Matrix4x4::SetRow(int row, const Vector4 &rowVector) {
SetRow(row, rowVector.x, rowVector.y, rowVector.z, rowVector.w);
}
void Matrix4x4::SetRow(int row, float m_r0, float m_r1, float m_r2, float m_r3) {
elems[row][0] = m_r0;
elems[row][1] = m_r1;
elems[row][2] = m_r2;
elems[row][3] = m_r3;
}
Matrix4x4::Matrix4x4(const Quaternion &orientation, const Vector3 &translation) {
SetRotatePart(orientation);
SetTranslatePart(translation);
SetRow(3, 0, 0, 0, 1);
}
Matrix4x4 Matrix4x4::D3DOrthoProjLH(float n, float f, float h, float v) {
float p00 = 2.f / h; float p01 = 0; float p02 = 0; float p03 = 0.f;
float p10 = 0; float p11 = 2.f / v; float p12 = 0; float p13 = 0.f;
float p20 = 0; float p21 = 0; float p22 = 1.f / (f-n); float p23 = n / (n-f);
float p30 = 0; float p31 = 0; float p32 = 0.f; float p33 = 1.f;
return {p00, p01, p02, p03, p10, p11, p12, p13, p20, p21, p22, p23, p30, p31, p32, p33};
}
/** This function generates an orthographic projection matrix that maps from
the Direct3D view space to the Direct3D normalized viewport space as follows:
In Direct3D view space, we assume that the camera is positioned at the origin (0,0,0).
The camera looks directly towards the positive Z axis (0,0,1).
The -X axis spans to the left of the screen, +X goes to the right.
-Y goes to the bottom of the screen, +Y goes to the top.
After the transformation, we're in the Direct3D normalized viewport space as follows:
(-1,-1,0) is the bottom-left corner of the viewport at the near plane.
(1,1,0) is the top-right corner of the viewport at the near plane.
(0,0,0) is the center point at the near plane.
Coordinates with z=1 are at the far plane.
Examples:
(0,0,n) maps to (0,0,0).
(0,0,f) maps to (0,0,1).
(-h/2, -v/2, n) maps to (-1, -1, 0).
(h/2, v/2, f) maps to (1, 1, 1).
*/
Matrix4x4 Matrix4x4::D3DOrthoProjRH(float n, float f, float h, float v)
{
// D3DOrthoProjLH and D3DOrthoProjRH differ from each other in that the third column is negated.
// This corresponds to LH = RH * In, where In is a diagonal matrix with elements [1 1 -1 1].
float p00 = 2.f / h; float p01 = 0; float p02 = 0; float p03 = 0.f;
float p10 = 0; float p11 = 2.f / v; float p12 = 0; float p13 = 0.f;
float p20 = 0; float p21 = 0; float p22 = 1.f / (n-f); float p23 = n / (n-f);
float p30 = 0; float p31 = 0; float p32 = 0.f; float p33 = 1.f;
}
}

View File

@@ -60,16 +60,14 @@ namespace LinearAlgebra {
Quaternion::Quaternion(float X, float Y, float Z, float W) : Vector4(X,Y,Z,W) {}
// TODO: implement
float Quaternion::Dot(const Quaternion &quaternion) const {}
float Quaternion::Dot(const Quaternion &rhs) const {
return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w;
}
Quaternion::Quaternion(Vector4 vector4) {
}
float Quaternion::Angle() const {
return std::acos(w) * 2.f;
}
Quaternion Quaternion::Normalize() const {
float length = Length();
if (length < 1e-4f)

View File

@@ -2,4 +2,29 @@
namespace 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);
const Transform2D Transform2D::FlipY = Transform2D({0, 0}, {1, -1}, {0,0}, {0,0}, 0);
Vector2 Transform2D::Transform(const Vector2 &input) const {
return transformation * input;
}
Transform2D::Transform2D(const Matrix3x3 &transform) : transformation(transform) { }
Transform2D::Transform2D(const Vector2& pos, const Vector2& scale, const Vector2& origin, const Vector2& skew, float rotation) {
transformation = Matrix3x3(pos.x, pos.y, rotation, scale.x, scale.y, origin.x, origin.y, skew.x, skew.y);
}
Transform2D Transform2D::Translate(float x, float y) const {
auto copy = Matrix3x3(transformation);
copy.SetAt(0, 0, transformation.At(0, 0) + x);
copy.SetAt(0, 1, transformation.At(0, 1) + y);
return Transform2D(copy);
}
Transform2D Transform2D::Translate(const LinearAlgebra::Vector2 &input) const {
return Translate(input.x, input.y);
}
}

View File

@@ -231,5 +231,21 @@ namespace LinearAlgebra {
return *this / scalar;
}
bool Vector2::IsFinite(const Vector2 &v) {
return v.IsFinite();
}
float Vector2::MinElement() const {
return std::min(x, y);
}
float Vector2::MaxElement() const {
return std::max(x, y);
}
Vector2 Vector2::Mul(const Vector2 &v) const {
return {this->x*v.x, this->y*v.y};
}
}

View File

@@ -301,5 +301,12 @@ namespace LinearAlgebra {
return lhs.AngleBetween(rhs);
}
Vector3 Vector3::Direction(const Vector3 &rhs) {
float x = (cos(Math::Radians(rhs.y)) * cos(Math::Radians(rhs.x)));
float y = -sin(Math::Radians(rhs.x));
float z = (sin(Math::Radians(rhs.y)) * cos(Math::Radians(rhs.x)));
return {x, y, z};
}
#pragma endregion
}

View File

@@ -1,3 +1,106 @@
//
// Created by josh on 12/26/2023.
//
#include <gtest/gtest.h>
#include <J3ML/LinearAlgebra/Vector4.h>
using Vector4 = LinearAlgebra::Vector4;
void EXPECT_V4_EQ(const Vector4& lhs, const Vector4& rhs)
{
}
TEST(Vector4Test, V4_Constructor_Default)
{
EXPECT_V4_EQ(Vector4(), Vector4::Zero);
}
TEST(Vector4Test, V4_Constructor_XYZW)
{
Vector4 Input {0, 1, 0, 1};
EXPECT_FLOAT_EQ(Input.x, 0);
EXPECT_FLOAT_EQ(Input.y, 1);
EXPECT_FLOAT_EQ(Input.z, 0);
EXPECT_FLOAT_EQ(Input.w, 1);
}
TEST(Vector4Test, V4_Addition_Op) {
Vector4 A {1, 1, 1, 1};
Vector4 B {2, 2, 2, 2};
Vector4 ExpectedResult {3, 3, 3, 3};
EXPECT_V4_EQ(A + B, ExpectedResult);
}
TEST(Vector4Test, V4_Addition_Method)
{
}
TEST(Vector4Test, V4_Addition_Static)
{
}
TEST(Vector4Test, V4_Subtract_Op)
{
}
TEST(Vector4Test, V4_Subtract_Method)
{
}
TEST(Vector4Test, V4_Subtract_Static)
{
}
TEST(Vector4Test, V4_Scalar_Mult_Op)
{
}
TEST(Vector4Test, V4_Scalar_Mult_Method)
{
}
TEST(Vector4Test, V4_Scalar_Mult_Static)
{
}
TEST(Vector4Test, V4_Scalar_Div_Op)
{
}
TEST(Vector4Test, V4_Scalar_Div_Method)
{
}
TEST(Vector4Test, V4_Scalar_Div_Static)
{
}
TEST(Vector4Test, V4_Sizeof)
{
}
TEST(Vector4Test, V4_NaN)
{
}
TEST(Vector4Tests, V4_Min) {}
TEST(Vector4Tests, V4_Max) {}
TEST(Vector4Tests, V4_Clamp) {}
TEST(Vector4Tests, V4_DotProduct) {}
TEST(Vector4Tests, V4_CrossProduct) {}
TEST(Vector4Tests, V4_Project) {}
TEST(Vector4Test, V4_Normalize) {}