From 67e7582d6314029cacbbfdb20378720827a678de Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Mon, 24 Feb 2025 09:37:22 +0300 Subject: [PATCH] Add line, plane, ray and segments, split math unit test Added some basic geometric primitives such as planes, rays, segments and lines (plus some extra functions like xProject, xBarycentric, Lerpf), as well as some intersection tests between them. Additionally, I split massive math test into smaller ones and tweaked unit test library (testing no longer stops after first failure). --- include/BH/Math.h | 505 +++++++++++++++++++- src/Math.c | 553 +++++++++++++++++++++- test/reference/matrix3f.wxmx | Bin 5986 -> 6738 bytes test/reference/matrix4f.wxmx | Bin 7100 -> 8271 bytes test/reference/plane.wxmx | Bin 0 -> 72771 bytes test/reference/point2f.wxmx | Bin 8202 -> 9961 bytes test/reference/point3f.wxmx | Bin 8290 -> 10396 bytes test/reference/point4f.wxmx | Bin 8910 -> 10865 bytes test/src/TestLine.c | 84 ++++ test/src/TestMat3f.c | 316 +++++++++++++ test/src/TestMat4f.c | 404 ++++++++++++++++ test/src/TestMath.c | 889 +---------------------------------- test/src/TestPlane.c | 96 ++++ test/src/TestQuat.c | 131 ++++++ test/src/TestRay2f.c | 196 ++++++++ test/src/TestRay3f.c | 99 ++++ test/src/TestVec2f.c | 284 +++++++++++ test/src/TestVec2i.c | 147 ++++++ test/src/TestVec3f.c | 309 ++++++++++++ test/src/TestVec3i.c | 157 +++++++ test/src/TestVec4f.c | 313 ++++++++++++ test/src/TestVec4i.c | 167 +++++++ unit/include/BH/Unit.h | 10 +- unit/src/Unit.c | 10 +- 24 files changed, 3755 insertions(+), 915 deletions(-) create mode 100644 test/reference/plane.wxmx create mode 100644 test/src/TestLine.c create mode 100644 test/src/TestMat3f.c create mode 100644 test/src/TestMat4f.c create mode 100644 test/src/TestPlane.c create mode 100644 test/src/TestQuat.c create mode 100644 test/src/TestRay2f.c create mode 100644 test/src/TestRay3f.c create mode 100644 test/src/TestVec2f.c create mode 100644 test/src/TestVec2i.c create mode 100644 test/src/TestVec3f.c create mode 100644 test/src/TestVec3i.c create mode 100644 test/src/TestVec4f.c create mode 100644 test/src/TestVec4i.c diff --git a/include/BH/Math.h b/include/BH/Math.h index 5ce9540..bda5b6d 100644 --- a/include/BH/Math.h +++ b/include/BH/Math.h @@ -106,6 +106,19 @@ void BH_Vec4fNormal(const float *in, float *out); +/** + * Computes normal vector from the \a in stores result into \a out and returns + * source length of the vector. + * + * \param in Input 4D vector + * \param out Output 4D vector + * + * \return Returns length prior to normalization + */ +float BH_Vec4fNormalEx(const float *in, + float *out); + + /** * Computes minimum vector from the \a a and \a b vectors and stores result * into \a out. @@ -147,6 +160,39 @@ void BH_Vec4fLerp(const float *a, float *out); +/** + * Projects \a a vector onto \a b vector and stores result into \a out. + * + * \param a A 4D vector + * \param b B 4D vector + * \param out Output 4D vector + */ +void BH_Vec4fProject(const float *a, + const float *b, + float *out); + + +/** + * Computes point from barycentric coordiantes \a v, \a w and points \a a, + * \a b and \a c vectors. + * + * Output vector is calculated as A + v*(B-A) + w*(C-A). + * + * \param a A 4D vector + * \param b B 4D vector + * \param c C 4D vector + * \param v V barycentric coordinate + * \param w W barycentric coordinate + * \param out Output 4D vector + */ +void BH_Vec4fBarycentric(const float *a, + const float *b, + const float *c, + float v, + float w, + float *out); + + /** * Adds \a a and \a b floating point vectors and stores result into \a out. * @@ -262,6 +308,19 @@ void BH_Vec3fNormal(const float *in, float *out); +/** + * Computes normal vector from the \a in stores result into \a out and returns + * source length of the vector. + * + * \param in Input 3D vector + * \param out Output 3D vector + * + * \return Returns length prior to normalization + */ +float BH_Vec3fNormalEx(const float *in, + float *out); + + /** * Computes minimum vector from the \a a and \a b vectors and stores result into * \a out. @@ -303,6 +362,39 @@ void BH_Vec3fLerp(const float *a, float *out); +/** + * Projects \a a vector onto \a b vector and stores result into \a out. + * + * \param a A 3D vector + * \param b B 3D vector + * \param out Output 3D vector + */ +void BH_Vec3fProject(const float *a, + const float *b, + float *out); + + +/** + * Computes point from barycentric coordiantes \a v, \a w and points \a a, + * \a b and \a c vectors. + * + * Output vector is calculated as A + v*(B-A) + w*(C-A). + * + * \param a A 3D vector + * \param b B 3D vector + * \param c C 3D vector + * \param v V barycentric coordinate + * \param w W barycentric coordinate + * \param out Output 3D vector + */ +void BH_Vec3fBarycentric(const float *a, + const float *b, + const float *c, + float v, + float w, + float *out); + + /** * Adds \a a and \a b floating point vectors and stores result into \a out. * @@ -416,6 +508,19 @@ void BH_Vec2fNormal(const float *in, float *out); +/** + * Computes normal vector from the \a in stores result into \a out and returns + * source length of the vector. + * + * \param in Input 2D vector + * \param out Output 2D vector + * + * \return Returns length prior to normalization + */ +float BH_Vec2fNormalEx(const float *in, + float *out); + + /** * Computes minimum vector from the \a a and \a b vectors and stores result into * \a out. @@ -457,6 +562,51 @@ void BH_Vec2fLerp(const float *a, float *out); +/** + * Projects \a a vector onto \a b vector and stores result into \a out. + * + * \param a A 2D vector + * \param b B 2D vector + * \param out Output 2D vector + */ +void BH_Vec2fProject(const float *a, + const float *b, + float *out); + + +/** + * Computes point from barycentric coordiantes \a v, \a w and points \a a, + * \a b and \a c vectors. + * + * Output vector is calculated as A + v*(B-A) + w*(C-A). + * + * \param a A 2D vector + * \param b B 2D vector + * \param c C 2D vector + * \param v V barycentric coordinate + * \param w W barycentric coordinate + * \param out Output 2D vector + */ +void BH_Vec2fBarycentric(const float *a, + const float *b, + const float *c, + float u, + float v, + float *out); + + +/** + * Interpolates between \a a and \a b values by \a t amount and returns the + * result. + * + * \param a A 2D vector + * \param b B 2D vector + * \param t Amount + * \param out Output 2D vector + */ +float BH_Lerpf(float a, float b, float t); + + /** * Adds \a a and \a b integer vectors and stores result into \a out. * @@ -1205,12 +1355,12 @@ void BH_Mat4fFromFrustum(float fov, /** * Computes camera view matrix and stores result into \a out. * - * \param pos Position vector - * \param at Target vector - * \param up Up vector - * \param out Output 4x4 matrix + * \param position Position vector + * \param at Target vector + * \param up Up vector + * \param out Output 4x4 matrix */ -void BH_Mat4fFromLookAt(const float *pos, +void BH_Mat4fFromLookAt(const float *position, const float *at, const float *up, float *out); @@ -1397,4 +1547,349 @@ void BH_Mat3fApplyVec2f(float *a, float *out); +/** + * Computes plane from points \a a, \a b, \a c and stores result into \a out. + * + * X, Y, Z components of the \a out vector are plane normal, W component is a + * distance from the origin (0,0,0) to the plane. + * + * It is assumed, that points provided in clockwise order. + * + * If points form degenerate triangle, this function will return error. + * + * \param a A 3D vector + * \param b B 3D vector + * \param c C 3D vector + * \param out Output 4D vector + * + * \return On success, returns zero. + * \return On failure, returns error-code. + */ +int BH_PlaneFromPoints(const float *a, + const float *b, + const float *c, + float *out); + + +/** + * Computes distance from \a plane to the \a point and returns result. + * + * \param plane Plane 4D vector + * \param point Point 3D vector + * + * \return Returns distance from plane to point. + */ +float BH_PlaneDistance(const float *plane, + const float *point); + + +/** + * Computes closest point on the \a plane to the \a point and stores result + * into \a out. + * + * \param plane Plane 4D vector + * \param point Point 3D vector + * \param out Output 3D vector + */ +void BH_PlaneClosestPoint(const float *plane, + const float *point, + float *out); + + +/** + * Computes time of intersection \a t between ray (given \a start and + * \a direction) and \a plane and stores intersection point into \a out. + * + * The returned intersection time \a t is for the ray. + * + * \param start Start 3D vector + * \param direction Direction 3D vector + * \param plane Plane 4D vector + * \param t Time of intersection + * \param out Output 3D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Ray3fIntersectPlane(const float *start, + const float *direction, + const float *plane, + float *t, + float *out); + + +/** + * Computes time of intersection \a t between ray (given \a start and + * \a direction) and triangle formed by \a a, \a b, \a c points and intersection + * point into \a out. + * + * The returned intersection time \a t is for the ray. + * + * \param start Start 3D vector + * \param direction Direction 3D vector + * \param a A 3D vector + * \param b B 3D vector + * \param c C 3D vector + * \param t Time of intersection + * \param out Output 3D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Ray3fIntersectTriangle(const float *start, + const float *direction, + const float *a, + const float *b, + const float *c, + float *t, + float *out); + + +/** + * Computes time of intesection \a t between line segment (given \a a and \a b + * points) and \a plane and stores intersection point into \a out. + * + * The returned intersection time \a t is for the segment. + * + * \param a A 3D vector + * \param b B 3D vector + * \param plane Plane 4D vector + * \param t Time of intersection + * \param out Output 3D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Segment3fIntersectPlane(const float *start, + const float *end, + const float *plane, + float *t, + float *out); + + +/** + * Computes time of intersection \a t between segment (given \a start and + * \a end) and triangle formed by \a a, \a b, \a c points and intersection + * point into \a out. + * + * The returned intersection time \a t is for the segment. + * + * \param start Start 3D vector + * \param direction Direction 3D vector + * \param a A 3D vector + * \param b B 3D vector + * \param c C 3D vector + * \param t Time of intersection + * \param out Output 3D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Segment3fIntersectTriangle(const float *start, + const float *end, + const float *a, + const float *b, + const float *c, + float *t, + float *out); + + +/** + * Computes barycentric coordinates from \a point and triangle made of \a a, + * \a b, \a c points and stores result into \a out. + * + * \param a A 3D vector + * \param b B 3D vector + * \param c C 3D vector + * \param point Point 3D vector + * \param out Output 3D vector + */ +void BH_Triangle3fBarycentric(const float *a, + const float *b, + const float *c, + const float *point, + float *out); + + +/** + * Computes line from \a a and \a b points and stores result into \a out. + * + * \param a A 2D vector + * \param b B 2D vector + * \param out Output 3D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_LineFromPoints(const float *a, + const float *b, + float *out); + + +/** + * Computes distance from \a line to the \a point and returns result. + * + * \param line Line 3D vector + * \param point Point 2D vector + * + * \return Returns distance from plane to point. + */ +float BH_LineDistance(const float *line, + const float *point); + + +/** + * Computes closest point on the \a line to the \a point and stores result into + * \a out. + * + * \param line Line 3D vector + * \param point Point 2D vector + * \param out Output 2D vector + */ +void BH_LineClosestPoint(const float *line, + const float *point, + float *out); + + +/** + * Computes time of intersection \a t between ray (given \a start and + * \a direction) and \a line and stores intersection point into \a out. + * + * The returned intersection time \a t is for the ray. + * + * \param start Start 2D vector + * \param direction Direction 2D vector + * \param line Line 3D vector + * \param t Time of intersection + * \param out Output 2D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Ray2fIntersectLine(const float *start, + const float *direction, + const float *line, + float *t, + float *out); + + +/** + * Computes time of intersection \a time1 and \a time2 between one line (given + * \a startA and \a directionA) and other line (given \a startB and + * \a directionB). + * + * \param startA A Start 2D vector + * \param directionA A Direction 2D vector + * \param startB B Start 2D vector + * \param directionB B Direction 2D vector + * \param time1 Time of intersection of first line + * \param time2 Time of intersection of second line + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Ray2fIntersectTime(const float *startA, + const float *directionA, + const float *startB, + const float *directionB, + float *time1, + float *time2); + + +/** + * Computes time of intersection \a t between one ray (given \a startA and + * \a directionA) and other ray (given \a startB and \a directionB) and stores + * intersection point into \a out. + * + * The returned intersection time \a t is for the first ray. + * + * \param startA A Start 2D vector + * \param directionA A Direction 2D vector + * \param startB B Start 2D vector + * \param directionB B Direction 2D vector + * \param t Time of intersection + * \param out Output 2D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Ray2fIntersectRay(const float *startA, + const float *directionA, + const float *startB, + const float *directionB, + float *t, + float *out); + + +/** + * Computes time of intersection \a t between one ray (given \a startA and + * \a directionA) and segment (given \a startB and \a endB) and stores result + * into \a out. + * + * The returned intersection time \a t is for the ray. + * + * \param startA A Start 2D vector + * \param directionA A Direction 2D vector + * \param startB B Start 2D vector + * \param endB B End 2D vector + * \param t Time of intersection + * \param out Output 2D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Ray2fIntersectSegment(const float *startA, + const float *directionA, + const float *startB, + const float *endB, + float *t, + float *out); + + +/** + * Computes time of intersection \a t between segment (given \a start and + * \a end) and \a line and stores intersection point into \a out. + * + * The returned intersection time \a t is for the segment. + * + * \param start Start 2D vector + * \param end End 2D vector + * \param line Line 3D vector + * \param t Time of intersection + * \param out Output 2D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Segment2fIntersectLine(const float *start, + const float *end, + const float *line, + float *t, + float *out); + + +/** + * Computes time of intersection \a t between one segment (given \a startA and + * \a endA) and other sergment (given \a startB and \a endB) and stores + * intersection point into \a out. + * + * The returned intersection time \a t is for the first segment. + * + * \param startA A Start 2D vector + * \param endA A End 2D vector + * \param startB B Start 2D vector + * \param endB B End 2D vector + * \param t Time of intersection + * \param out Output 2D vector + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int BH_Segment2fIntersectSegment(const float *startA, + const float *endA, + const float *startB, + const float *endB, + float *t, + float *out); + #endif /* BH_MATH_H */ diff --git a/src/Math.c b/src/Math.c index 58ebcd3..3d37354 100644 --- a/src/Math.c +++ b/src/Math.c @@ -1,4 +1,5 @@ #include + #include #include @@ -89,6 +90,17 @@ void BH_Vec4fNormal(const float *in, } +float BH_Vec4fNormalEx(const float *in, + float *out) +{ + float length; + + length = BH_Vec4fLength(in); + BH_Vec4fScale(in, 1.0f / length, out); + return length; +} + + void BH_Vec4fMin(const float *a, const float *b, float *out) @@ -124,6 +136,34 @@ void BH_Vec4fLerp(const float *a, } +void BH_Vec4fProject(const float *a, + const float *b, + float *out) +{ + float amount; + + amount = BH_Vec4fDot(a, b) / BH_Vec4fDot(b, b); + BH_Vec4fScale(b, amount, out); +} + + +void BH_Vec4fBarycentric(const float *a, + const float *b, + const float *c, + float v, + float w, + float *out) +{ + float tmp1[4], tmp2[4]; + float u; + + u = 1.0f - v - w; + tmp1[0] = tmp1[1] = tmp1[2] = tmp1[3] = u; BH_Vec4fMul(a, tmp1, tmp2); + tmp1[0] = tmp1[1] = tmp1[2] = tmp1[3] = v; BH_Vec4fMulAdd(b, tmp1, tmp2, tmp2); + tmp1[0] = tmp1[1] = tmp1[2] = tmp1[3] = w; BH_Vec4fMulAdd(c, tmp1, tmp2, out); +} + + void BH_Vec3fAdd(const float *a, const float *b, float *out) @@ -217,6 +257,17 @@ void BH_Vec3fNormal(const float *in, } +float BH_Vec3fNormalEx(const float *in, + float *out) +{ + float length; + + length = BH_Vec3fLength(in); + BH_Vec3fScale(in, 1.0f / length, out); + return length; +} + + void BH_Vec3fMin(const float *a, const float *b, float *out) @@ -250,6 +301,34 @@ void BH_Vec3fLerp(const float *a, } +void BH_Vec3fProject(const float *a, + const float *b, + float *out) +{ + float amount; + + amount = BH_Vec3fDot(a, b) / BH_Vec3fDot(b, b); + BH_Vec3fScale(b, amount, out); +} + + +void BH_Vec3fBarycentric(const float *a, + const float *b, + const float *c, + float v, + float w, + float *out) +{ + float tmp1[3], tmp2[3]; + float u; + + u = 1.0f - v - w; + tmp1[0] = tmp1[1] = tmp1[2] = u; BH_Vec3fMul(a, tmp1, tmp2); + tmp1[0] = tmp1[1] = tmp1[2] = v; BH_Vec3fMulAdd(b, tmp1, tmp2, tmp2); + tmp1[0] = tmp1[1] = tmp1[2] = w; BH_Vec3fMulAdd(c, tmp1, tmp2, out); +} + + void BH_Vec2fAdd(const float *a, const float *b, float *out) @@ -331,6 +410,17 @@ void BH_Vec2fNormal(const float *in, } +float BH_Vec2fNormalEx(const float *in, + float *out) +{ + float length; + + length = BH_Vec2fLength(in); + BH_Vec2fScale(in, 1.0f / length, out); + return length; +} + + void BH_Vec2fMin(const float *a, const float *b, float *out) @@ -362,6 +452,40 @@ void BH_Vec2fLerp(const float *a, } +void BH_Vec2fProject(const float *a, + const float *b, + float *out) +{ + float amount; + + amount = BH_Vec2fDot(a, b) / BH_Vec2fDot(b, b); + BH_Vec2fScale(b, amount, out); +} + + +void BH_Vec2fBarycentric(const float *a, + const float *b, + const float *c, + float v, + float w, + float *out) +{ + float tmp1[2], tmp2[2]; + float u; + + u = 1.0f - v - w; + tmp1[0] = tmp1[1] = u; BH_Vec2fMul(a, tmp1, tmp2); + tmp1[0] = tmp1[1] = v; BH_Vec2fMulAdd(b, tmp1, tmp2, tmp2); + tmp1[0] = tmp1[1] = w; BH_Vec2fMulAdd(c, tmp1, tmp2, out); +} + + +float BH_Lerpf(float a, float b, float t) +{ + return a + (b - a) * t; +} + + void BH_Vec4iAdd(const int *a, const int *b, int *out) @@ -1176,37 +1300,37 @@ void BH_Mat4fFromFrustum(float fov, } -void BH_Mat4fFromLookAt(const float *pos, +void BH_Mat4fFromLookAt(const float *position, const float *at, const float *up, float *out) { - float cdir[3], cright[3], cup[3]; + float cameraDir[3], cameraRight[3], cameraUp[3]; - BH_Vec3fSub(pos, at, cdir); - BH_Vec3fNormal(cdir, cdir); - BH_Vec3fCross(up, cdir, cright); - BH_Vec3fNormal(cright, cright); - BH_Vec3fCross(cdir, cright, cup); + BH_Vec3fSub(position, at, cameraDir); + BH_Vec3fNormal(cameraDir, cameraDir); + BH_Vec3fCross(up, cameraDir, cameraRight); + BH_Vec3fNormal(cameraRight, cameraRight); + BH_Vec3fCross(cameraDir, cameraRight, cameraUp); - out[0] = cright[0]; - out[1] = cup[0]; - out[2] = cdir[0]; + out[0] = cameraRight[0]; + out[1] = cameraUp[0]; + out[2] = cameraDir[0]; out[3] = 0.0f; - out[4] = cright[1]; - out[5] = cup[1]; - out[6] = cdir[1]; + out[4] = cameraRight[1]; + out[5] = cameraUp[1]; + out[6] = cameraDir[1]; out[7] = 0.0f; - out[8] = cright[2]; - out[9] = cup[2]; - out[10] = cdir[2]; + out[8] = cameraRight[2]; + out[9] = cameraUp[2]; + out[10] = cameraDir[2]; out[11] = 0.0f; - out[12] = -BH_Vec3fDot(cright, pos); - out[13] = -BH_Vec3fDot(cup, pos); - out[14] = -BH_Vec3fDot(cdir, pos); + out[12] = -BH_Vec3fDot(cameraRight, position); + out[13] = -BH_Vec3fDot(cameraUp, position); + out[14] = -BH_Vec3fDot(cameraDir, position); out[15] = 1.0f; } @@ -1447,3 +1571,394 @@ void BH_Mat3fApplyVec2f(float *a, memcpy(out, tmp, sizeof(float) * 2); } + + +int BH_PlaneFromPoints(const float *a, + const float *b, + const float *c, + float *out) +{ + float tmp1[3], tmp2[3]; + + BH_Vec3fSub(b, a, tmp1); + BH_Vec3fSub(c, a, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fNormalEx(tmp1, tmp1) == 0.0f) + return BH_ERROR; + + out[3] = BH_Vec3fDot(a, tmp1); + memcpy(out, tmp1, sizeof(tmp1)); + return BH_OK; +} + + +float BH_PlaneDistance(const float *plane, + const float *point) +{ + return BH_Vec3fDot(plane, point) - plane[3]; +} + + +void BH_PlaneClosestPoint(const float *plane, + const float *point, + float *out) +{ + float tmp[3]; + + BH_Vec3fScale(plane, BH_PlaneDistance(plane, point), tmp); + BH_Vec3fSub(point, tmp, out); +} + + +int BH_Ray3fIntersectPlane(const float *start, + const float *direction, + const float *plane, + float *t, + float *out) +{ + float tmp1[3]; + float denom, time; + + /* Calculate intersection time */ + denom = BH_Vec3fDot(direction, plane); + time = (plane[3] - BH_Vec3fDot(plane, start)) / denom; + + /* Check for ray/plane parallel to each other or point is behing the ray. */ + if (denom == 0.0f || time < 0.0f) + return BH_ERROR; + + /* Compute intersection point */ + BH_Vec3fScale(direction, time, tmp1); + BH_Vec3fAdd(start, tmp1, out); + *t = time; + return BH_OK; +} + + +int BH_Ray3fIntersectTriangle(const float *start, + const float *direction, + const float *a, + const float *b, + const float *c, + float *t, + float *out) +{ + float plane[4]; + float tmp1[3], tmp2[3], tmp3[3]; + float time; + + /* Compute plane */ + if (BH_PlaneFromPoints(a, b, c, plane)) + return BH_ERROR; + + /* Compute intersection point in ray against plane */ + if (BH_Ray3fIntersectPlane(start, direction, plane, &time, tmp3)) + return BH_ERROR; + + /* Check if point inside rectangle */ + BH_Vec3fSub(b, a, tmp1); + BH_Vec3fSub(tmp3, a, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fDot(tmp1, plane) < 0.0f) + return BH_ERROR; + + BH_Vec3fSub(c, b, tmp1); + BH_Vec3fSub(tmp3, b, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fDot(tmp1, plane) < 0.0f) + return BH_ERROR; + + BH_Vec3fSub(a, c, tmp1); + BH_Vec3fSub(tmp3, c, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fDot(tmp1, plane) < 0.0f) + return BH_ERROR; + + /* Copy intersection point */ + memcpy(out, tmp3, sizeof(tmp3)); + *t = time; + return BH_OK; +} + + +int BH_Segment3fIntersectPlane(const float *start, + const float *end, + const float *plane, + float *t, + float *out) +{ + float tmp[3]; + float denom, time; + + /* Calculate intersection time */ + BH_Vec3fSub(end, start, tmp); + denom = BH_Vec3fDot(tmp, plane); + time = (plane[3] - BH_Vec3fDot(plane, start)) / denom; + + /* Check for ray/plane parallel to each other or point is behing the ray. */ + if (denom == 0.0f || time < 0.0f || time > 1.0f) + return BH_ERROR; + + /* Compute intersection point */ + BH_Vec3fScale(tmp, time, tmp); + BH_Vec3fAdd(start, tmp, out); + *t = time; + return BH_OK; +} + + +int BH_Segment3fIntersectTriangle(const float *start, + const float *end, + const float *a, + const float *b, + const float *c, + float *t, + float *out) +{ + float plane[4]; + float tmp1[3], tmp2[3], tmp3[3]; + float time; + + /* Compute plane */ + if (BH_PlaneFromPoints(a, b, c, plane)) + return BH_ERROR; + + /* Compute intersection point in ray against plane */ + if (BH_Segment3fIntersectPlane(start, end, plane, &time, tmp3)) + return BH_ERROR; + + /* Check if point inside rectangle */ + BH_Vec3fSub(b, a, tmp1); + BH_Vec3fSub(tmp3, a, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fDot(tmp1, plane) < 0.0f) + return BH_ERROR; + + BH_Vec3fSub(c, b, tmp1); + BH_Vec3fSub(tmp3, b, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fDot(tmp1, plane) < 0.0f) + return BH_ERROR; + + BH_Vec3fSub(a, c, tmp1); + BH_Vec3fSub(tmp3, c, tmp2); + BH_Vec3fCross(tmp2, tmp1, tmp1); + if (BH_Vec3fDot(tmp1, plane) < 0.0f) + return BH_ERROR; + + /* Copy intersection point */ + memcpy(out, tmp3, sizeof(tmp3)); + *t = time; + return BH_OK; +} + + +void BH_Triangle3fBarycentric(const float *a, + const float *b, + const float *c, + const float *point, + float *out) +{ + float tmp1[3], tmp2[3], tmp3[3]; + float t11, t12, t22, t31, t32, denom; + + BH_Vec3fSub(b, a, tmp1); + BH_Vec3fSub(c, a, tmp2); + BH_Vec3fSub(point, a, tmp3); + + t11 = BH_Vec3fDot(tmp1, tmp1); + t12 = BH_Vec3fDot(tmp1, tmp2); + t22 = BH_Vec3fDot(tmp2, tmp2); + t31 = BH_Vec3fDot(tmp3, tmp1); + t32 = BH_Vec3fDot(tmp3, tmp2); + denom = 1.0f / (t11 * t22 - t12 * t12); + + out[1] = (t22 * t31 - t12 * t32) * denom; + out[2] = (t11 * t32 - t12 * t31) * denom; + out[0] = 1.0f - out[1] - out[2]; +} + + +int BH_LineFromPoints(const float *a, + const float *b, + float *out) +{ + float tmp[2]; + + tmp[0] = a[1] - b[1]; + tmp[1] = b[0] - a[0]; + if (BH_Vec2fNormalEx(tmp, tmp) == 0.0f) + return BH_ERROR; + + out[2] = BH_Vec2fDot(tmp, a); + memcpy(out, tmp, sizeof(tmp)); + return BH_OK; +} + + +float BH_LineDistance(const float *line, + const float *point) +{ + return BH_Vec2fDot(line, point) - line[2]; +} + + +void BH_LineClosestPoint(const float *line, + const float *point, + float *out) +{ + float tmp[2]; + + BH_Vec2fScale(line, BH_LineDistance(line, point), tmp); + BH_Vec2fSub(point, tmp, out); +} + + +int BH_Ray2fIntersectLine(const float *start, + const float *direction, + const float *line, + float *t, + float *out) +{ + float tmp1[2]; + float denom, time; + + /* Calculate intersection time */ + denom = BH_Vec2fDot(direction, line); + time = (line[2] - BH_Vec2fDot(line, start)) / denom; + + /* Check for ray/plane parallel to each other or point is behing the ray. */ + if (denom == 0.0f || time < 0.0f) + return BH_ERROR; + + /* Compute intersection point */ + BH_Vec2fScale(direction, time, tmp1); + BH_Vec2fAdd(start, tmp1, out); + *t = time; + return BH_OK; +} + + +int BH_Ray2fIntersectTime(const float *startA, + const float *directionA, + const float *startB, + const float *directionB, + float *time1, + float *time2) +{ + float tmp1[2], tmp2[2], tmp3[2]; + float denom; + + /* Rotate directions by 90 degrees and caluclate denom */ + tmp1[0] = -directionA[1]; tmp1[1] = directionA[0]; + tmp2[0] = -directionB[1]; tmp2[1] = directionB[0]; + denom = BH_Vec2fDot(tmp1, directionB); + + if (denom == 0.0f) + return BH_ERROR; + + /* Calculate segments offset and intersection times */ + BH_Vec2fSub(startA, startB, tmp3); + *time1 = BH_Vec2fDot(tmp3, tmp2) / denom; + *time2 = BH_Vec2fDot(tmp3, tmp1) / denom; + + return BH_OK; +} + + +int BH_Ray2fIntersectRay(const float *startA, + const float *directionA, + const float *startB, + const float *directionB, + float *t, + float *out) +{ + float tmp[2]; + float time1, time2; + + if (BH_Ray2fIntersectTime(startA, directionA, startB, directionB, &time1, &time2)) + return BH_ERROR; + + if (time1 < 0.0f || time2 < 0.0f) + return BH_ERROR; + + BH_Vec2fScale(directionA, time1, tmp); + BH_Vec2fAdd(startA, tmp, out); + *t = time1; + return BH_OK; +} + + +int BH_Ray2fIntersectSegment(const float *startA, + const float *directionA, + const float *startB, + const float *endB, + float *t, + float *out) +{ + float tmp[2]; + float time1, time2; + + BH_Vec2fSub(endB, startB, tmp); + if (BH_Ray2fIntersectTime(startA, directionA, startB, tmp, &time1, &time2)) + return BH_ERROR; + + if (time1 < 0.0f || time2 < 0.0f || time2 > 1.0f) + return BH_ERROR; + + BH_Vec2fScale(directionA, time1, tmp); + BH_Vec2fAdd(startA, tmp, out); + *t = time1; + return BH_OK; +} + + +int BH_Segment2fIntersectLine(const float *start, + const float *end, + const float *line, + float *t, + float *out) +{ + float tmp[2]; + float denom, time; + + /* Calculate intersection time */ + BH_Vec2fSub(end, start, tmp); + denom = BH_Vec2fDot(tmp, line); + time = (line[2] - BH_Vec2fDot(line, start)) / denom; + + /* Check for ray/plane parallel to each other or point is behing the ray. */ + if (denom == 0.0f || time < 0.0f || time > 1.0f) + return BH_ERROR; + + /* Compute intersection point */ + BH_Vec2fScale(tmp, time, tmp); + BH_Vec2fAdd(start, tmp, out); + *t = time; + return BH_OK; +} + + +int BH_Segment2fIntersectSegment(const float *startA, + const float *endA, + const float *startB, + const float *endB, + float *t, + float *out) +{ + float tmp1[2], tmp2[2]; + float time1, time2; + + BH_Vec2fSub(endA, startA, tmp1); + BH_Vec2fSub(endB, startB, tmp2); + if (BH_Ray2fIntersectTime(startA, tmp1, startB, tmp2, &time1, &time2)) + return BH_ERROR; + + if (time1 < 0.0f || time1 > 1.0f || time2 < 0.0f || time2 > 1.0f) + return BH_ERROR; + + BH_Vec2fLerp(startA, endA, time1, out); + *t = time1; + + return BH_OK; +} diff --git a/test/reference/matrix3f.wxmx b/test/reference/matrix3f.wxmx index 592ce6142da6322395cd433b453bfde3212e11a2..cbc4cc58b95d31a00d773cf54c2ea849305a688c 100644 GIT binary patch delta 622 zcmaE)cgch|z?+#xgaHIN7Wz-*HDU%*8=WHA_<(d&#o}XSw}luOZVOFb$##5mJ^MDM z$)`DlgUhTGqK$MEbPaVBOk%BV^m8)vQth~Gz*KxuYH?{!iLsSJqFxzxIU|r6#q!OU;` z%8ZO=K&Jj?FF}1qMspz3adLow@MHxZp2@d`UNc%uJ|SGkXgS$Kq>jnZVDbr(d?rIf zu-GPZJ_qVg3Z(~va(23JN%={>iiXcqYpWvrQHd hVwqeaW(i6zledT&gXvddrs&B=S6rWMsw60R0RXOrz=Hq) delta 246 zcmca)@<@+2z?+#xgaHIjZnB=pYs3tsHabPJ@d4?mf9tPbf6dRp@S1<}O19&C3W>=j znPsWTsW~~eN`{;B*|#%Ip2n5UXtbG;+lFy6lZ3(M93FAT$xnfdO}yfaOopbD8M*Z) zpW+st{9n*;vJL-pCPOoj(B>ckWuUY#zroU9<4&tzn@d7`L2K3s;bJ^^H_`Xr3Y&;MykGkJefs^r_v`QN&mLT3S8jXz zqaO0~zq`A`=Daw3ym5bk!*Olz={i@A(Ag)(A3BW6nr zibV4Pg@$!YZ!`@%KQC(2&e=7Y)QV0Kl`^7J%+XcGD=Go&7vNPOAF%6n!>(>ErZiaP z#{F7Kow!^_**t7>)3DAD!E$U24me+XBPcOlx=iV^OBYVlun|PC93J20LQsoE;Qde( zcs2t2>`V5_nPS`HJZdB9mLV~m?a+V*5I37tkyIrhNFwkQOt_8 z7`|)N*whT2fG`*hV7sdnx)puM40R#f(#*zIz0m=`4|86GvIHb;XfMRpdKEFe*#SSr zu_8k7IgVmov5Ic%R;_AkrcD}~hNKY2SPrIy|6ugI7z@I4!Pkdmcp+Q~cLl4aqg(FY zjs%YB{Gh$WB?xin+IP-BVc>U|=bcG^N<8_>!PeuCoK9;{VN%2sR5nz_5{+uZUQF3m zQ%|XiO4ii%{bimB!l$yAjGW=+i~5($c2}V3sD& z@F`Ir$x;=(F;O$@rTSKDF*R{K+?L?00VME@&Et; delta 359 zcmX@_u*aM?z?+#xgaHK3ZnB=pYs3tsHabPJ@d4?m1H7dku0UB=kw_l29tmD3IX+j^a8Pv%;XJphL_UEhqa(f>;I|5C<6=Og<;64RRx2W?orpQ8C1DL&MDu nQui1qbID6jZjhG)h12AP^2T8Ly1Xe`cukg4&}aKE1qxRHJ*a33 diff --git a/test/reference/plane.wxmx b/test/reference/plane.wxmx new file mode 100644 index 0000000000000000000000000000000000000000..8f63ee1a044cbb875306b99ffb5fba618fd68e0a GIT binary patch literal 72771 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
eOMX4_2(3qkGD6bYozs;Lgwi1{!(4!22Syf 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 PiTt;SAd#adBgKCJ$63x3 literal 0 HcmV?d00001 diff --git a/test/reference/point2f.wxmx b/test/reference/point2f.wxmx index 76cb0240d44be9a268b829c5993d9d4cd45084a6..1188b9d277031485e416d387754a43a1d471bdf2 100644 GIT binary patch delta 1043 zcmb7EF>KR76s_%~P1-nWle9@g!A_)t6GEChpR3kMD}=<-3RH;!Xp1U41PEAh6f6W1 z0*MVI5cCTHf{lp<31x(xm8fDYVnVPn!@}L!Es%-<5B_)m|M&j;zJI=V^xFw}WmyWP z2H10Q_4FedOVqqMc6KmM?dn>0{dGqf80aYQYVdtrwd!8;TBF`@oP|8e^sZeYHh}8wi=7WHEwt=_H*%`WY~`E4_oFMDnV~3d3%kN z^mBa#b(4BYzn9bM{~>|=OTr)J;AhK|Z9AvpF=yXTmmb?@tg=l_Jn|29o$=gvQ*csioaZM6tIVh93 z3!{@5evHT9a{`39fGP#=k~f8UPSr}?7g2==D&tQ?#L6OysKZuzdicES@gfMr^NAvJ z!Ow}|DAEfmJp|ix+Ojn+l)lAllDVH(l=iiix(IGcIK;4!DsX7JQo#fw*;Bj8wx7JCeN#lf@TdD8iP?dRhr(s>ux!qKZ{=5BA LnHek{WdDF)vM?`! delta 522 zcmaFq+vUI;;LXe;!T4JlU)@|CnxYWG8#{Q$h(=z$OK3kPd>rt&1Gb&$*E+gp_*?rnNdJ=vH|}|ZX+|8 zklAJ?0S#tGqsbekOeV((cTPT_Vm#SaoM*DV1lwe7k>1G@R81$p5t+bbYydWJgJ>U< zu_2gQEH)iz*#ll9p!#IqlKlLflFR~IrTCQmlF1h&)VY8(*ji)b$>+tj_z?<{i}H($ zK}sgeNpLY4n@rY{a1}sE=cMMPmt=tCc_B=QDW;PbN*E$3%gZmyP0Rr*Gh#9}13JiX zvZ>@uBwKP)iwY7!rf`E<5JSM;OyG5uj0Xv307F3m*@jCiK-@(=SWXtGJyn9vdlCl6HpKtZPt|yU}7`@hLP#y&GMF$|EpSpqH?me WnlYFzR5L}7(k*KGY`*fKs0RS0v8^Bg diff --git a/test/reference/point3f.wxmx b/test/reference/point3f.wxmx index a8c30c8b0d3b35a53eccdb054ce9a0a94246cdcc..d02b2d75b36801d6e9e2e8e17e4c79367045e632 100644 GIT binary patch delta 1086 zcmb7EOKTHR6i#l^XiX+*n^!~Lh1f~WW9HsFc{ixwrf!Tbqz$1ap@o)^l$wQtQ1>oW z4!CmRLh-e+#ErX(OaFo316M^wbm`9fNP?zroPoJ#&i&>)=X~eP{e1dpQ!>?TvQ76x;0dntipkq^iwKE16YW?3qlK;gA{yNB>7Y68nF-wX zVQ7Tva7n3{vG5-#Z95^!>4mDA=n(=b=X*P$qjx%(bEy_Y41w*;JwPPK%wEA{a!#Dq1Lc|p41b!5>l#zPcra7Aq0Dq$MKnW)=Jhla(-V3_gYd5*x9iP6%4$;6AX0BKqZ4v{ZDDe1u=t56B>@+-&{i{ zXLAm1$tPOp76NIxLNR9;dLC1qO%AJkM$e2L`6AIZRYHDU%*8=WHA_<(ej?zK9X*Afg2uO&9GWP8mr`6f>m zqv2!+RpZH=%2ODPz^wJMk(0GmtQn2LJTCr3P7_T|B|8n(e3Qv#{C7D`p*+*gaRM65 zlkHVaChr!W$7nwJfw=AD86q1QEuhATOr9hnyoSI&TRTf+9HCdf>w}9Em}5@N+l*W28s_X z6$QOn{*!o6FTM~&>P^v$AYP>6#f$Xf%_4}mqT)Q()}$af%kIqo{r`OX?KiXY?!dPr z%EY*%+22Il_o<`H3UZL{>HaCFm-NRjFT52uw5FyFZRMd8?>JPND;LLRa#r=6k%?J* z`cvuNL~gI8^HH9RvLk)0NE+FyMZ6YmD~6;ix@cvljU-Cqq-B1fLOo`nydQP>sU4g2I4 zc;AlUqFiNhr{WY4Lw@-EP*zS*v?Z^NCAuq^;X~d9x2d}sV!{jWCwJP8(ku5`*F`d+ z+ievJtYCmlc|;L-;&TauBr@QO?>0kT`dz}1{i^c}c^r_0VTP;+IC4vs;BFAXNA)VL zP|C%VR;|dW4Z~)WVJ!$WD-|j=o0!iqqk9;rHSaV{#F`g0n-MkJbo{;1m)UXCu>#W`Ng)pP5nV71dj>E*Mf z;vYK1KId@PYY|cX7{+5iMZ_Jy6rY8)NLvTnsl?M*tx_q~=H^E=zIh=Y?vYuNy$rGS zXvQg&hBMKCNV;X&*PN#DQanJGF&;#W3~eVPNt+CDGLx!tpcH}$uS&VLC>?~7a2eo{ zvxIfE-6VEA5D|3@L%`n-_aiFYko|%IcRKswjgl1+w~Z!!WFyi0dBkLD@OCwLQw`>7 z_`bLjRiWTjVX;evgI*W(1e1(`ZodX|%uNQ9Gq9C#5Oe7F5Y>M_i7yfR52Q6y8Q<8FmsIn delta 788 zcmewua?X`Ez?+#xgaHJ4$}A`H8ZiT@jZTqld_X$NW~u&%aA^jHaOuq}*^F5ytMi&n z{;X`qY-naM`J$Tf)EBtJi=B(uO)DLyeJWpbm4`s54396)1C!N%~2l=C1|6qim`RMg}u zO@bO~HhH7S1tb%4OLIUbZ~!A_bZ-YkGnk(ZjDSOPLioe#o<7-nuTIZy)GX({<7AVu6j z8mhu@a)6{Jl3hRL%#9|ml+;3UadJ_9aq;Afl17~7#+nK?`Z-BClOKqP zOqQ3*Lvl?{YF>It2G}(aCe$vVzod+i)aB(D>pz^w|WJK$!plxe5Z% diff --git a/test/src/TestLine.c b/test/src/TestLine.c new file mode 100644 index 0000000..ce9ced7 --- /dev/null +++ b/test/src/TestLine.c @@ -0,0 +1,84 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(FromPoints) +{ + float a[2], b[2], r[3]; + + a[0] = -8.0f; a[1] = 1.0f; + b[0] = -2.0f; b[1] = 3.0f; + + BH_VERIFY(BH_LineFromPoints(a, b, r) == BH_OK); + BH_VERIFY_DELTA(r[0],-0.3162f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.9487f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.4785f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Degenerate) +{ + float a[2], b[2], r[3]; + + a[0] = -8.0f; a[1] = 1.0f; + b[0] = -8.0f; b[1] = 1.0f; + + BH_VERIFY(BH_LineFromPoints(a, b, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(Distance) +{ + float a[2], b[2], r[3]; + + a[0] = -8.0f; a[1] = 1.0f; + b[0] = -2.0f; b[1] = 3.0f; + + BH_VERIFY(BH_LineFromPoints(a, b, r) == BH_OK); + BH_VERIFY_DELTA(BH_LineDistance(r, a), 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(BH_LineDistance(r, b), 0.0000f, ACCEPTABLE_DELTA); + + a[0] = -7.0f; a[1] = 4.0f; + BH_VERIFY_DELTA(BH_LineDistance(r, a), 2.5298f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(ClosestPoint) +{ + float a[2], b[2], r[3]; + + a[0] = -8.0f; a[1] = 1.0f; + b[0] = -2.0f; b[1] = 3.0f; + + BH_VERIFY(BH_LineFromPoints(a, b, r) == BH_OK); + + a[0] = -7.0f; a[1] = 4.0f; + BH_LineClosestPoint(r, a, b); + BH_VERIFY_DELTA(b[0],-6.2000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[1], 1.6000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(FromPoints); + BH_UNIT_ADD(Degenerate); + BH_UNIT_ADD(Distance); + BH_UNIT_ADD(ClosestPoint); + + return BH_UnitRun(); +} diff --git a/test/src/TestMat3f.c b/test/src/TestMat3f.c new file mode 100644 index 0000000..7d71502 --- /dev/null +++ b/test/src/TestMat3f.c @@ -0,0 +1,316 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(Identity) +{ + float r[9]; + + BH_Mat3fIdentity(r); + BH_VERIFY(r[0] == 1.0f); + BH_VERIFY(r[1] == 0.0f); + BH_VERIFY(r[2] == 0.0f); + + BH_VERIFY(r[3] == 0.0f); + BH_VERIFY(r[4] == 1.0f); + BH_VERIFY(r[5] == 0.0f); + + BH_VERIFY(r[6] == 0.0f); + BH_VERIFY(r[7] == 0.0f); + BH_VERIFY(r[8] == 1.0f); + + return 0; +} + + +BH_UNIT_TEST(Add) +{ + float a[9], b[9], r[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + b[0] = 2.0f; b[3] = 1.0f; b[6] = 2.0f; + b[1] = 3.0f; b[4] = 1.0f; b[7] = 0.0f; + b[2] = 4.0f; b[5] = 2.0f; b[8] = 4.0f; + + BH_Mat3fAdd(a, b, r); + BH_VERIFY_DELTA(r[0], 7.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 5.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 8.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + float a[9], b[9], r[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + b[0] = 2.0f; b[3] = 1.0f; b[6] = 2.0f; + b[1] = 3.0f; b[4] = 1.0f; b[7] = 0.0f; + b[2] = 4.0f; b[5] = 2.0f; b[8] = 4.0f; + + BH_Mat3fSub(a, b, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], -1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 1.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + float a[9], b[9], r[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + b[0] = 2.0f; b[3] = 1.0f; b[6] = 2.0f; + b[1] = 3.0f; b[4] = 1.0f; b[7] = 0.0f; + b[2] = 4.0f; b[5] = 2.0f; b[8] = 4.0f; + + BH_Mat3fMul(a, b, r); + BH_VERIFY_DELTA(r[0], 21.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 27.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 12.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 18.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 18.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + float a[9], r[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + BH_Mat3fScale(a, 10, r); + BH_VERIFY_DELTA(r[0], 50.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 30.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 30.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 20.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 40.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Transpose) +{ + float a[9], r[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + BH_Mat3fTranspose(a, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 2.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Trace) +{ + float a[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + BH_VERIFY_DELTA(BH_Mat3fTrace(a), 9.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Det) +{ + float a[9], b[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + b[0] = 2.0f; b[3] = 1.0f; b[6] = 2.0f; + b[1] = 3.0f; b[4] = 1.0f; b[7] = 0.0f; + b[2] = 4.0f; b[5] = 2.0f; b[8] = 4.0f; + + BH_VERIFY_DELTA(BH_Mat3fDet(a), 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(BH_Mat3fDet(b), 0.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Inverse) +{ + float a[9], b[9], r[9]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + b[0] = 2.0f; b[3] = 1.0f; b[6] = 2.0f; + b[1] = 3.0f; b[4] = 1.0f; b[7] = 0.0f; + b[2] = 4.0f; b[5] = 2.0f; b[8] = 4.0f; + + BH_VERIFY(BH_Mat3fInverse(a, r) == BH_OK); + BH_VERIFY_DELTA(r[0], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 1.5000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 0.3333f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], -2.3333f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], -0.5000f, ACCEPTABLE_DELTA); + + BH_VERIFY(BH_Mat3fInverse(b, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(Translation) +{ + float r[9]; + + BH_Mat3fFromTranslation(1.0f, 2.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scaling) +{ + float r[9]; + + BH_Mat3fFromScale(1.0f, 2.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[4], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[8], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Apply) +{ + float a[9]; + float b[3]; + + a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; + a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; + a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; + + b[0] = 2.0f; b[1] = -1.0f; b[2] = 4.0f; + + BH_Mat3fApplyVec3f(a, b, b); + BH_VERIFY_DELTA(b[0], 17.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[2], 15.0000f, ACCEPTABLE_DELTA); + + b[0] = 2.0f; b[1] = -1.0f; + BH_Mat3fApplyVec2f(a, b, b); + BH_VERIFY_DELTA(b[0], 11.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[1], 6.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Identity); + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(Transpose); + BH_UNIT_ADD(Trace); + BH_UNIT_ADD(Det); + BH_UNIT_ADD(Inverse); + BH_UNIT_ADD(Translation); + BH_UNIT_ADD(Scaling); + BH_UNIT_ADD(Apply); + + return BH_UnitRun(); +} diff --git a/test/src/TestMat4f.c b/test/src/TestMat4f.c new file mode 100644 index 0000000..d4ada01 --- /dev/null +++ b/test/src/TestMat4f.c @@ -0,0 +1,404 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(Identity) +{ + float r[16]; + + BH_Mat4fIdentity(r); + BH_VERIFY(r[0] == 1.0f); + BH_VERIFY(r[1] == 0.0f); + BH_VERIFY(r[2] == 0.0f); + BH_VERIFY(r[3] == 0.0f); + + BH_VERIFY(r[4] == 0.0f); + BH_VERIFY(r[5] == 1.0f); + BH_VERIFY(r[6] == 0.0f); + BH_VERIFY(r[7] == 0.0f); + + BH_VERIFY(r[8] == 0.0f); + BH_VERIFY(r[9] == 0.0f); + BH_VERIFY(r[10] == 1.0f); + BH_VERIFY(r[11] == 0.0f); + + BH_VERIFY(r[12] == 0.0f); + BH_VERIFY(r[13] == 0.0f); + BH_VERIFY(r[14] == 0.0f); + BH_VERIFY(r[15] == 1.0f); + + return 0; +} + + +BH_UNIT_TEST(Add) +{ + float a[16], b[16], r[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + b[0] = 5.0f; b[4] = 1.0f; b[8] = 2.0f; b[12] = 7.0f; + b[1] = 3.0f; b[5] = 1.0f; b[9] = 0.0f; b[13] = 2.0f; + b[2] = 4.0f; b[6] = 2.0f; b[10] = 4.0f; b[14] = 5.0f; + b[3] = 6.0f; b[7] = 2.0f; b[11] = 0.0f; b[15] = 4.0f; + + BH_Mat4fAdd(a, b, r); + BH_VERIFY_DELTA(r[0], 10.0f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 2.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 8.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 14.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 7.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + float a[16], b[16], r[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + b[0] = 5.0f; b[4] = 1.0f; b[8] = 2.0f; b[12] = 7.0f; + b[1] = 3.0f; b[5] = 1.0f; b[9] = 0.0f; b[13] = 2.0f; + b[2] = 4.0f; b[6] = 2.0f; b[10] = 4.0f; b[14] = 5.0f; + b[3] = 6.0f; b[7] = 2.0f; b[11] = 0.0f; b[15] = 4.0f; + + BH_Mat4fSub(a, b, r); + BH_VERIFY_DELTA(r[0], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], -4.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], -1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], -2.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], -1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + float a[16], b[16], r[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + b[0] = 5.0f; b[4] = 1.0f; b[8] = 2.0f; b[12] = 7.0f; + b[1] = 3.0f; b[5] = 1.0f; b[9] = 0.0f; b[13] = 2.0f; + b[2] = 4.0f; b[6] = 2.0f; b[10] = 4.0f; b[14] = 5.0f; + b[3] = 6.0f; b[7] = 2.0f; b[11] = 0.0f; b[15] = 4.0f; + + BH_Mat4fMul(a, b, r); + BH_VERIFY_DELTA(r[0], 78.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 27.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 60.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 28.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 24.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 7.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 22.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 8.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 18.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 18.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 4.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 75.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 29.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 53.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 26.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + float a[16], r[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + BH_Mat4fScale(a, 10, r); + BH_VERIFY_DELTA(r[0], 50.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 30.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 20.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 30.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 20.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 40.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 70.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 20.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 50.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 30.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Transpose) +{ + float a[16], r[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + BH_Mat4fTranspose(a, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 7.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 2.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 5.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 3.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Trace) +{ + float a[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + BH_VERIFY_DELTA(BH_Mat4fTrace(a), 12.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Det) +{ + float a[16], b[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + b[0] = 5.0f; b[4] = 1.0f; b[8] = 2.0f; b[12] = 7.0f; + b[1] = 3.0f; b[5] = 1.0f; b[9] = 0.0f; b[13] = 2.0f; + b[2] = 4.0f; b[6] = 2.0f; b[10] = 4.0f; b[14] = 5.0f; + b[3] = 6.0f; b[7] = 2.0f; b[11] = 0.0f; b[15] = 4.0f; + + BH_VERIFY_DELTA(BH_Mat4fDet(a), 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(BH_Mat4fDet(b), 0.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Inverse) +{ + float a[16], b[16], r[16]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + b[0] = 5.0f; b[4] = 1.0f; b[8] = 2.0f; b[12] = 7.0f; + b[1] = 3.0f; b[5] = 1.0f; b[9] = 0.0f; b[13] = 2.0f; + b[2] = 4.0f; b[6] = 2.0f; b[10] = 4.0f; b[14] = 5.0f; + b[3] = 6.0f; b[7] = 2.0f; b[11] = 0.0f; b[15] = 4.0f; + + BH_VERIFY(BH_Mat4fInverse(a, r) == BH_OK); + BH_VERIFY_DELTA(r[0], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 1.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 0.6000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 1.8000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], -1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], -0.4000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], -0.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], -0.4000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 1.8000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], -2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 0.6000f, ACCEPTABLE_DELTA); + + BH_VERIFY(BH_Mat4fInverse(b, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(Translation) +{ + float r[16]; + + BH_Mat4fFromTranslation(1.0f, 2.0f, 3.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scaling) +{ + float r[16]; + + BH_Mat4fFromScale(1.0f, 2.0f, 3.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[5], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[10], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(r[12], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[13], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[14], 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[15], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Apply) +{ + float a[16]; + float b[4]; + + a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; + a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; + a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; + a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; + + b[0] = 2.0f; b[1] = -1.0f; b[2] = 4.0f; b[3] = 0.0f; + + BH_Mat4fApplyVec4f(a, b, b); + BH_VERIFY_DELTA(b[0], 17.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[2], 15.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[3], 4.0000f, ACCEPTABLE_DELTA); + + b[0] = 2.0f; b[1] = -1.0f; b[2] = 4.0f; + BH_Mat4fApplyVec3f(a, b, b); + BH_VERIFY_DELTA(b[0], 24.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[1], 8.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(b[2], 20.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Identity); + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(Transpose); + BH_UNIT_ADD(Trace); + BH_UNIT_ADD(Det); + BH_UNIT_ADD(Inverse); + BH_UNIT_ADD(Translation); + BH_UNIT_ADD(Scaling); + BH_UNIT_ADD(Apply); + + return BH_UnitRun(); +} diff --git a/test/src/TestMath.c b/test/src/TestMath.c index 90549fb..9896e6a 100644 --- a/test/src/TestMath.c +++ b/test/src/TestMath.c @@ -5,830 +5,10 @@ #define ACCEPTABLE_DELTA 0.0001f -static int checkVec4f(void) -{ - float a[4], b[4], c[4], r[4]; - float value; - - a[0] = 1.0f; a[1] = 2.0f; a[2] = 3.0f; a[3] = 4.0f; - b[0] = 5.0f; b[1] = 6.0f; b[2] = 7.0f; b[3] = 8.0f; - c[0] = 1.5f; c[1] = 2.5f; c[2] = 3.5f; c[3] = 4.5f; - - BH_Vec4fAdd(a, b, r); - BH_VERIFY_DELTA(r[0], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 8.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 12.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fSub(a, b, r); - BH_VERIFY_DELTA(r[0], -4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], -4.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fMul(a, b, r); - BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 12.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 21.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 32.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fScale(a, 10.0f, r); - BH_VERIFY_DELTA(r[0], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 20.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 30.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 40.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fMulAdd(a, b, c, r); - BH_VERIFY_DELTA(r[0], 6.5000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 14.5000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 24.5000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 36.5000f, ACCEPTABLE_DELTA); - - BH_Vec4fNegate(a, r); - BH_VERIFY_DELTA(r[0], -1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], -4.0000f, ACCEPTABLE_DELTA); - - value = BH_Vec4fDot(a, b); - BH_VERIFY_DELTA(value, 70.0000f, ACCEPTABLE_DELTA); - - value = BH_Vec4fLength(a); - BH_VERIFY_DELTA(value, sqrt(30.0f), ACCEPTABLE_DELTA); - - BH_Vec4fNormal(a, r); - BH_VERIFY_DELTA(r[0], 1.0f / sqrt(30.0f), ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f / sqrt(30.0f), ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 3.0f / sqrt(30.0f), ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 4.0f / sqrt(30.0f), ACCEPTABLE_DELTA); - - BH_Vec4fMin(a, b, r); - BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 4.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fMax(a, b, r); - BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fLerp(a, b, 0.0f, r); - BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 4.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fLerp(a, b, 0.5f, r); - BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 6.0000f, ACCEPTABLE_DELTA); - - BH_Vec4fLerp(a, b, 1.0f, r); - BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); - - return 0; -} - - -static int checkVec3f(void) -{ - float a[3], b[3], c[3], r[3]; - float value; - - a[0] = 1.0f; a[1] = 2.0f; a[2] = 3.0f; - b[0] = 5.0f; b[1] = 6.0f; b[2] = 7.0f; - c[0] = 1.5f; c[1] = 2.5f; c[2] = 3.5f; - - BH_Vec3fAdd(a, b, r); - BH_VERIFY_DELTA(r[0], 6.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 8.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 10.0f, ACCEPTABLE_DELTA); - - BH_Vec3fSub(a, b, r); - BH_VERIFY_DELTA(r[0], -4.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -4.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -4.0f, ACCEPTABLE_DELTA); - - BH_Vec3fMul(a, b, r); - BH_VERIFY_DELTA(r[0], 5.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 12.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 21.0f, ACCEPTABLE_DELTA); - - BH_Vec3fScale(a, 10.0f, r); - BH_VERIFY_DELTA(r[0], 10.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 20.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 30.0f, ACCEPTABLE_DELTA); - - BH_Vec3fMulAdd(a, b, c, r); - BH_VERIFY_DELTA(r[0], 6.5f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 14.5f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 24.5f, ACCEPTABLE_DELTA); - - BH_Vec3fNegate(a, r); - BH_VERIFY_DELTA(r[0], -1.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -2.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -3.0f, ACCEPTABLE_DELTA); - - value = BH_Vec3fDot(a, b); - BH_VERIFY_DELTA(value, 38.0f, ACCEPTABLE_DELTA); - - BH_Vec3fCross(a, b, r); - BH_VERIFY_DELTA(r[0], -4.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 8.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -4.0f, ACCEPTABLE_DELTA); - - value = BH_Vec3fLength(a); - BH_VERIFY_DELTA(value, sqrt(14.0f), ACCEPTABLE_DELTA); - - BH_Vec3fNormal(a, r); - BH_VERIFY_DELTA(r[0], 1.0f / sqrt(14.0f), ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f / sqrt(14.0f), ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 3.0f / sqrt(14.0f), ACCEPTABLE_DELTA); - - BH_Vec3fMin(a, b, r); - BH_VERIFY_DELTA(r[0], 1.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 3.0f, ACCEPTABLE_DELTA); - - BH_Vec3fMax(a, b, r); - BH_VERIFY_DELTA(r[0], 5.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 7.0f, ACCEPTABLE_DELTA); - - BH_Vec3fLerp(a, b, 0.0f, r); - BH_VERIFY_DELTA(r[0], 1.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 3.0f, ACCEPTABLE_DELTA); - - BH_Vec3fLerp(a, b, 0.5f, r); - BH_VERIFY_DELTA(r[0], 3.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 4.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 5.0f, ACCEPTABLE_DELTA); - - BH_Vec3fLerp(a, b, 1.0f, r); - BH_VERIFY_DELTA(r[0], 5.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 7.0f, ACCEPTABLE_DELTA); - - return 0; -} - - -static int checkVec2f(void) -{ - float a[2], b[2], c[2], r[2]; - float value; - - a[0] = 1.0f; a[1] = 2.0f; - b[0] = 5.0f; b[1] = 6.0f; - c[0] = 1.5f; c[1] = 2.5f; - - BH_Vec2fAdd(a, b, r); - BH_VERIFY_DELTA(r[0], 6.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 8.0f, ACCEPTABLE_DELTA); - - BH_Vec2fSub(a, b, r); - BH_VERIFY_DELTA(r[0], -4.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -4.0f, ACCEPTABLE_DELTA); - - BH_Vec2fMul(a, b, r); - BH_VERIFY_DELTA(r[0], 5.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 12.0f, ACCEPTABLE_DELTA); - - BH_Vec2fScale(a, 10.0f, r); - BH_VERIFY_DELTA(r[0], 10.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 20.0f, ACCEPTABLE_DELTA); - - BH_Vec2fMulAdd(a, b, c, r); - BH_VERIFY_DELTA(r[0], 6.5f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 14.5f, ACCEPTABLE_DELTA); - - BH_Vec2fNegate(a, r); - BH_VERIFY_DELTA(r[0], -1.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -2.0f, ACCEPTABLE_DELTA); - - value = BH_Vec2fDot(a, b); - BH_VERIFY_DELTA(value, 17.0f, ACCEPTABLE_DELTA); - - value = BH_Vec2fCross(a, b); - BH_VERIFY_DELTA(value, -4.0f, ACCEPTABLE_DELTA); - - value = BH_Vec2fLength(a); - BH_VERIFY_DELTA(value, sqrt(5.0f), ACCEPTABLE_DELTA); - - BH_Vec2fNormal(a, r); - BH_VERIFY_DELTA(r[0], 1.0f / sqrt(5.0f), ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f / sqrt(5.0f), ACCEPTABLE_DELTA); - - BH_Vec2fMin(a, b, r); - BH_VERIFY_DELTA(r[0], 1.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f, ACCEPTABLE_DELTA); - - BH_Vec2fMax(a, b, r); - BH_VERIFY_DELTA(r[0], 5.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0f, ACCEPTABLE_DELTA); - - BH_Vec2fLerp(a, b, 0.0f, r); - BH_VERIFY_DELTA(r[0], 1.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 2.0f, ACCEPTABLE_DELTA); - - BH_Vec2fLerp(a, b, 0.5f, r); - BH_VERIFY_DELTA(r[0], 3.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 4.0f, ACCEPTABLE_DELTA); - - BH_Vec2fLerp(a, b, 1.0f, r); - BH_VERIFY_DELTA(r[0], 5.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0f, ACCEPTABLE_DELTA); - - return 0; -} - - -static int checkVec4i(void) -{ - int a[4], b[4], c[4], r[4]; - - a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; - b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; - c[0] = 4; c[1] = 3; c[2] = 2; c[3] = 1; - - BH_Vec4iAdd(a, b, r); - BH_VERIFY(r[0] == 6); - BH_VERIFY(r[1] == 8); - BH_VERIFY(r[2] == 10); - BH_VERIFY(r[3] == 12); - - BH_Vec4iSub(a, b, r); - BH_VERIFY(r[0] == -4); - BH_VERIFY(r[1] == -4); - BH_VERIFY(r[2] == -4); - BH_VERIFY(r[3] == -4); - - BH_Vec4iMul(a, b, r); - BH_VERIFY(r[0] == 5); - BH_VERIFY(r[1] == 12); - BH_VERIFY(r[2] == 21); - BH_VERIFY(r[3] == 32); - - BH_Vec4iScale(a, 10, r); - BH_VERIFY(r[0] == 10); - BH_VERIFY(r[1] == 20); - BH_VERIFY(r[2] == 30); - BH_VERIFY(r[3] == 40); - - BH_Vec4iMulAdd(a, b, c, r); - BH_VERIFY(r[0] == 9); - BH_VERIFY(r[1] == 15); - BH_VERIFY(r[2] == 23); - BH_VERIFY(r[3] == 33); - - BH_Vec4iNegate(a, r); - BH_VERIFY(r[0] == -1); - BH_VERIFY(r[1] == -2); - BH_VERIFY(r[2] == -3); - BH_VERIFY(r[3] == -4); - - BH_Vec4iMin(a, b, r); - BH_VERIFY(r[0] == 1); - BH_VERIFY(r[1] == 2); - BH_VERIFY(r[2] == 3); - BH_VERIFY(r[3] == 4); - - BH_Vec4iMax(a, b, r); - BH_VERIFY(r[0] == 5); - BH_VERIFY(r[1] == 6); - BH_VERIFY(r[2] == 7); - BH_VERIFY(r[3] == 8); - - return 0; -} - - -static int checkVec3i(void) -{ - int a[3], b[3], c[3], r[3]; - - a[0] = 1; a[1] = 2; a[2] = 3; - b[0] = 5; b[1] = 6; b[2] = 7; - c[0] = 4; c[1] = 3; c[2] = 2; - - BH_Vec3iAdd(a, b, r); - BH_VERIFY(r[0] == 6); - BH_VERIFY(r[1] == 8); - BH_VERIFY(r[2] == 10); - - BH_Vec3iSub(a, b, r); - BH_VERIFY(r[0] == -4); - BH_VERIFY(r[1] == -4); - BH_VERIFY(r[2] == -4); - - BH_Vec3iMul(a, b, r); - BH_VERIFY(r[0] == 5); - BH_VERIFY(r[1] == 12); - BH_VERIFY(r[2] == 21); - - BH_Vec3iScale(a, 10, r); - BH_VERIFY(r[0] == 10); - BH_VERIFY(r[1] == 20); - BH_VERIFY(r[2] == 30); - - BH_Vec3iMulAdd(a, b, c, r); - BH_VERIFY(r[0] == 9); - BH_VERIFY(r[1] == 15); - BH_VERIFY(r[2] == 23); - - BH_Vec3iNegate(a, r); - BH_VERIFY(r[0] == -1); - BH_VERIFY(r[1] == -2); - BH_VERIFY(r[2] == -3); - - BH_Vec3iMin(a, b, r); - BH_VERIFY(r[0] == 1); - BH_VERIFY(r[1] == 2); - BH_VERIFY(r[2] == 3); - - BH_Vec3iMax(a, b, r); - BH_VERIFY(r[0] == 5); - BH_VERIFY(r[1] == 6); - BH_VERIFY(r[2] == 7); - - return 0; -} - - -static int checkVec2i(void) -{ - int a[2], b[2], c[2], r[2]; - - a[0] = 1; a[1] = 2; - b[0] = 5; b[1] = 6; - c[0] = 4; c[1] = 3; - - BH_Vec2iAdd(a, b, r); - BH_VERIFY(r[0] == 6); - BH_VERIFY(r[1] == 8); - - BH_Vec2iSub(a, b, r); - BH_VERIFY(r[0] == -4); - BH_VERIFY(r[1] == -4); - - BH_Vec2iMul(a, b, r); - BH_VERIFY(r[0] == 5); - BH_VERIFY(r[1] == 12); - - BH_Vec2iScale(a, 10, r); - BH_VERIFY(r[0] == 10); - BH_VERIFY(r[1] == 20); - - BH_Vec2iMulAdd(a, b, c, r); - BH_VERIFY(r[0] == 9); - BH_VERIFY(r[1] == 15); - - BH_Vec2iNegate(a, r); - BH_VERIFY(r[0] == -1); - BH_VERIFY(r[1] == -2); - - BH_Vec2iMin(a, b, r); - BH_VERIFY(r[0] == 1); - BH_VERIFY(r[1] == 2); - - BH_Vec2iMax(a, b, r); - BH_VERIFY(r[0] == 5); - BH_VERIFY(r[1] == 6); - - return 0; -} - - -static int checkQuat(void) -{ - float a[4], b[3], r[4]; - float roll, pitch, yaw, angle; - - a[0] = -0.9018f; a[1] = -0.0010f; a[2] = -0.4099f; a[3] = 0.1370f; - b[0] = -0.9104f; b[1] = -0.0010f; b[2] = -0.4138f; - - BH_Quat4fIdentity(r); - BH_VERIFY_DELTA(r[0], 0.000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 1.000f, ACCEPTABLE_DELTA); - - BH_Quat4fConjugate(a, r); - BH_VERIFY_DELTA(r[0], 0.9018f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0010f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.4099f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 0.1370f, ACCEPTABLE_DELTA); - - BH_Quat4fToEuler(a, &roll, &pitch, &yaw); - BH_VERIFY_DELTA(roll, -2.7671f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(pitch, -0.8324f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(yaw, -0.1649f, ACCEPTABLE_DELTA); - - BH_Quat4fFromEuler(-2.7671f, -0.8324f, -0.1649f, r); - BH_VERIFY_DELTA(r[0], -0.9018f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -0.0010f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -0.4099f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 0.1370f, ACCEPTABLE_DELTA); - - BH_Quat4fFromAxis(b, 2.8668f, r); - BH_VERIFY_DELTA(r[0], -0.9018f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -0.0010f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -0.4099f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 0.1370f, ACCEPTABLE_DELTA); - - BH_Quat4fToAxis(a, r, &angle); - BH_VERIFY_DELTA(r[0], -0.9104f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -0.0010f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -0.4138f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(angle, 2.8668f, ACCEPTABLE_DELTA); - - - BH_Quat4fInverse(a, r); - BH_Quat4fMul(a, r, r); - BH_VERIFY_DELTA(r[0], 0.000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 1.000f, ACCEPTABLE_DELTA); - - return 0; -} - - -static int checkMat4f(void) -{ - float a[16], b[16], r[16]; - float value; - - a[0] = 5.0f; a[4] = 1.0f; a[8] = 2.0f; a[12] = 7.0f; - a[1] = 3.0f; a[5] = 0.0f; a[9] = 0.0f; a[13] = 2.0f; - a[2] = 1.0f; a[6] = 3.0f; a[10] = 4.0f; a[14] = 5.0f; - a[3] = 2.0f; a[7] = 0.0f; a[11] = 0.0f; a[15] = 3.0f; - - b[0] = 5.0f; b[4] = 1.0f; b[8] = 2.0f; b[12] = 7.0f; - b[1] = 3.0f; b[5] = 1.0f; b[9] = 0.0f; b[13] = 2.0f; - b[2] = 4.0f; b[6] = 2.0f; b[10] = 4.0f; b[14] = 5.0f; - b[3] = 6.0f; b[7] = 2.0f; b[11] = 0.0f; b[15] = 4.0f; - - BH_Mat4fIdentity(r); - BH_VERIFY(r[0] == 1.0f); - BH_VERIFY(r[1] == 0.0f); - BH_VERIFY(r[2] == 0.0f); - BH_VERIFY(r[3] == 0.0f); - - BH_VERIFY(r[4] == 0.0f); - BH_VERIFY(r[5] == 1.0f); - BH_VERIFY(r[6] == 0.0f); - BH_VERIFY(r[7] == 0.0f); - - BH_VERIFY(r[8] == 0.0f); - BH_VERIFY(r[9] == 0.0f); - BH_VERIFY(r[10] == 1.0f); - BH_VERIFY(r[11] == 0.0f); - - BH_VERIFY(r[12] == 0.0f); - BH_VERIFY(r[13] == 0.0f); - BH_VERIFY(r[14] == 0.0f); - BH_VERIFY(r[15] == 1.0f); - - BH_Mat4fAdd(a, b, r); - BH_VERIFY_DELTA(r[0], 10.0f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 2.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 8.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 14.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 7.0000f, ACCEPTABLE_DELTA); - - BH_Mat4fSub(a, b, r); - BH_VERIFY_DELTA(r[0], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], -4.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], -1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], -2.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], -1.0000f, ACCEPTABLE_DELTA); - - BH_Mat4fMul(a, b, r); - BH_VERIFY_DELTA(r[0], 78.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 27.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 60.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 28.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 24.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 7.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 22.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 8.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 18.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 18.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 4.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 75.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 29.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 53.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 26.0000f, ACCEPTABLE_DELTA); - - BH_Mat4fScale(a, 10, r); - BH_VERIFY_DELTA(r[0], 50.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 30.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 20.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 30.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 20.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 40.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 70.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 20.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 50.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 30.0000f, ACCEPTABLE_DELTA); - - BH_Mat4fTranspose(a, r); - BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 7.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 2.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 5.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 3.0000f, ACCEPTABLE_DELTA); - - value = BH_Mat4fTrace(a); - BH_VERIFY_DELTA(value, 12.0000f, ACCEPTABLE_DELTA); - - value = BH_Mat4fDet(a); - BH_VERIFY_DELTA(value, 10.0000f, ACCEPTABLE_DELTA); - - value = BH_Mat4fDet(b); - BH_VERIFY_DELTA(value, 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY(BH_Mat4fInverse(a, r) == BH_OK); - BH_VERIFY_DELTA(r[0], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 1.5000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 0.6000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 1.8000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], -1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], -0.4000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], -0.5000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], -0.4000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 1.8000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], -2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 0.6000f, ACCEPTABLE_DELTA); - - BH_VERIFY(BH_Mat4fInverse(b, r) != BH_OK); - - BH_Mat4fFromTranslation(1.0f, 2.0f, 3.0f, r); - BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 1.0000f, ACCEPTABLE_DELTA); - - BH_Mat4fFromScale(1.0f, 2.0f, 3.0f, r); - BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[9], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[10], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[11], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[12], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[13], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[14], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[15], 1.0000f, ACCEPTABLE_DELTA); - - return 0; -} - - -static int checkMat3f(void) -{ - float a[9], b[9], r[9]; - float value; - - a[0] = 5.0f; a[3] = 1.0f; a[6] = 2.0f; - a[1] = 3.0f; a[4] = 0.0f; a[7] = 0.0f; - a[2] = 1.0f; a[5] = 3.0f; a[8] = 4.0f; - - b[0] = 2.0f; b[3] = 1.0f; b[6] = 2.0f; - b[1] = 3.0f; b[4] = 1.0f; b[7] = 0.0f; - b[2] = 4.0f; b[5] = 2.0f; b[8] = 4.0f; - - BH_Mat3fIdentity(r); - BH_VERIFY(r[0] == 1.0f); - BH_VERIFY(r[1] == 0.0f); - BH_VERIFY(r[2] == 0.0f); - - BH_VERIFY(r[3] == 0.0f); - BH_VERIFY(r[4] == 1.0f); - BH_VERIFY(r[5] == 0.0f); - - BH_VERIFY(r[6] == 0.0f); - BH_VERIFY(r[7] == 0.0f); - BH_VERIFY(r[8] == 1.0f); - - BH_Mat3fAdd(a, b, r); - BH_VERIFY_DELTA(r[0], 7.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 5.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 4.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 8.0000f, ACCEPTABLE_DELTA); - - BH_Mat3fSub(a, b, r); - BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], -1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 1.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 0.0000f, ACCEPTABLE_DELTA); - - BH_Mat3fMul(a, b, r); - BH_VERIFY_DELTA(r[0], 21.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 27.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 12.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 18.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 18.0000f, ACCEPTABLE_DELTA); - - BH_Mat3fScale(a, 10, r); - BH_VERIFY_DELTA(r[0], 50.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 30.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 10.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 30.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 20.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 40.0000f, ACCEPTABLE_DELTA); - - BH_Mat3fTranspose(a, r); - BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 2.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 4.0000f, ACCEPTABLE_DELTA); - - value = BH_Mat3fTrace(a); - BH_VERIFY_DELTA(value, 9.0000f, ACCEPTABLE_DELTA); - - value = BH_Mat3fDet(a); - BH_VERIFY_DELTA(value, 6.0000f, ACCEPTABLE_DELTA); - - value = BH_Mat3fDet(b); - BH_VERIFY_DELTA(value, 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY(BH_Mat3fInverse(a, r) == BH_OK); - BH_VERIFY_DELTA(r[0], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 1.5000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 0.3333f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 3.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], -2.3333f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], -0.5000f, ACCEPTABLE_DELTA); - - BH_VERIFY(BH_Mat3fInverse(b, r) != BH_OK); - - BH_Mat3fFromTranslation(1.0f, 2.0f, r); - BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 1.0000f, ACCEPTABLE_DELTA); - - BH_Mat3fFromScale(1.0f, 2.0f, r); - BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[1], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[2], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[3], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[4], 2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[5], 0.0000f, ACCEPTABLE_DELTA); - - BH_VERIFY_DELTA(r[6], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[7], 0.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(r[8], 1.0000f, ACCEPTABLE_DELTA); - - return 0; -} - - -int checkQuatMat4f(void) +BH_UNIT_TEST(QuatMat4fParity) { float a[16], b[16], c[4]; - + BH_Mat4fFromEuler(-2.7671f, -0.8324f, -0.1649f, a); BH_Quat4fFromEuler(-2.7671f, -0.8324f, -0.1649f, c); @@ -859,75 +39,12 @@ int checkQuatMat4f(void) } -int checkVecMat4f(void) -{ - float a[16] = - { - 5.0f, 3.0f, 1.0f, 2.0f, - 1.0f, 0.0f, 3.0f, 0.0f, - 2.0f, 0.0f, 4.0f, 0.0f, - 7.0f, 2.0f, 5.0f, 3.0f - }; - float b[4]; - - b[0] = 2.0f; b[1] = -1.0f; b[2] = 4.0f; b[3] = 0.0f; - BH_Mat4fApplyVec4f(a, b, b); - BH_VERIFY_DELTA(b[0], 17.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[1], 6.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[2], 15.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[3], 4.0000f, ACCEPTABLE_DELTA); - - b[0] = 2.0f; b[1] = -1.0f; b[2] = 4.0f; - BH_Mat4fApplyVec3f(a, b, b); - BH_VERIFY_DELTA(b[0], 24.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[1], 8.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[2], 20.0000f, ACCEPTABLE_DELTA); - - return 0; -} - - -int checkVecMat3f(void) -{ - float a[9] = - { - 0.0f, 1.0f, -3.0f, - -3.0f, -4.0f, 4.0f, - -2.0f, -2.0f, 1.0f, - }; - float b[3]; - - b[0] = 2.0f; b[1] = -1.0f; b[2] = 4.0f; - BH_Mat3fApplyVec3f(a, b, b); - BH_VERIFY_DELTA(b[0], -5.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[1], -2.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[2], -6.0000f, ACCEPTABLE_DELTA); - - b[0] = 2.0f; b[1] = -1.0f; - BH_Mat3fApplyVec2f(a, b, b); - BH_VERIFY_DELTA(b[0], 1.0000f, ACCEPTABLE_DELTA); - BH_VERIFY_DELTA(b[1], 4.0000f, ACCEPTABLE_DELTA); - - return 0; -} - int main(int argc, char **argv) { (void)argc; (void)argv; - BH_UnitAdd("Vec4f", checkVec4f); - BH_UnitAdd("Vec3f", checkVec3f); - BH_UnitAdd("Vec2f", checkVec2f); - BH_UnitAdd("Vec4i", checkVec4i); - BH_UnitAdd("Vec3i", checkVec3i); - BH_UnitAdd("Vec2i", checkVec2i); - BH_UnitAdd("Quat", checkQuat); - BH_UnitAdd("Mat4f", checkMat4f); - BH_UnitAdd("Mat3f", checkMat3f); - BH_UnitAdd("QuatMat4f", checkQuatMat4f); - BH_UnitAdd("VecMat4f", checkVecMat4f); - BH_UnitAdd("VecMat3f", checkVecMat3f); + BH_UNIT_ADD(QuatMat4fParity); return BH_UnitRun(); } diff --git a/test/src/TestPlane.c b/test/src/TestPlane.c new file mode 100644 index 0000000..f405a69 --- /dev/null +++ b/test/src/TestPlane.c @@ -0,0 +1,96 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(FromPoints) +{ + float a[3], b[3], c[3], r[4]; + + a[0] = 1; a[1] = -2; a[2] = 1; + b[0] = 4; b[1] = -2; b[2] = -2; + c[0] = 4; c[1] = 1; c[2] = 4; + + BH_VERIFY(BH_PlaneFromPoints(a, b, c, r) == BH_OK); + + BH_VERIFY_DELTA(r[0],-0.4082f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.8165f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2],-0.4082f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3],-2.4495f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Degenerate) +{ + float a[3], b[3], c[3], r[4]; + + a[0] = 1; a[1] = -2; a[2] = 1; + b[0] = 1; b[1] = -2; b[2] = 1; + c[0] = 4; c[1] = 1; c[2] = 4; + + BH_VERIFY(BH_PlaneFromPoints(a, b, c, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(Distance) +{ + float a[3], b[3], c[3], r[4]; + + a[0] = 1; a[1] = -2; a[2] = 1; + b[0] = 4; b[1] = -2; b[2] = -2; + c[0] = 4; c[1] = 1; c[2] = 4; + + BH_VERIFY(BH_PlaneFromPoints(a, b, c, r) == BH_OK); + + BH_VERIFY_DELTA(BH_PlaneDistance(r, a), 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(BH_PlaneDistance(r, b), 0.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(BH_PlaneDistance(r, c), 0.0000f, ACCEPTABLE_DELTA); + + a[0] = 3; a[1] = 4; a[2] = 5; + b[0] = 2; b[1] = -1; b[2] = 6; + BH_VERIFY_DELTA(BH_PlaneDistance(r, a), 2.4495f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(BH_PlaneDistance(r, b), -1.6330f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(ClosestPoint) +{ + float a[3], b[3], c[3], r[4]; + + a[0] = 1; a[1] = -2; a[2] = 1; + b[0] = 4; b[1] = -2; b[2] = -2; + c[0] = 4; c[1] = 1; c[2] = 4; + + BH_VERIFY(BH_PlaneFromPoints(a, b, c, r) == BH_OK); + + b[0] = 3; b[1] = 4; b[2] = 5; + + BH_PlaneClosestPoint(r, b, c); + BH_VERIFY_DELTA(c[0], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(c[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(c[2], 6.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(FromPoints); + BH_UNIT_ADD(Degenerate); + BH_UNIT_ADD(Distance); + BH_UNIT_ADD(ClosestPoint); + + return BH_UnitRun(); +} diff --git a/test/src/TestQuat.c b/test/src/TestQuat.c new file mode 100644 index 0000000..55e86d5 --- /dev/null +++ b/test/src/TestQuat.c @@ -0,0 +1,131 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(Identity) +{ + float r[4]; + + BH_Quat4fIdentity(r); + BH_VERIFY_DELTA(r[0], 0.000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 1.000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Conjugate) +{ + float a[4], r[4]; + + a[0] = -0.9018f; a[1] = -0.0010f; a[2] = -0.4099f; a[3] = 0.1370f; + + BH_Quat4fConjugate(a, r); + BH_VERIFY_DELTA(r[0], 0.9018f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.0010f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.4099f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.1370f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(ToEuler) +{ + float a[4]; + float roll, pitch, yaw; + + a[0] = -0.9018f; a[1] = -0.0010f; a[2] = -0.4099f; a[3] = 0.1370f; + + BH_Quat4fToEuler(a, &roll, &pitch, &yaw); + BH_VERIFY_DELTA(roll, -2.7671f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(pitch, -0.8324f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(yaw, -0.1649f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(FromEuler) +{ + float r[4]; + + BH_Quat4fFromEuler(-2.7671f, -0.8324f, -0.1649f, r); + BH_VERIFY_DELTA(r[0], -0.9018f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -0.0010f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -0.4099f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.1370f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(ToAxis) +{ + float a[4], r[3]; + float angle; + + a[0] = -0.9018f; a[1] = -0.0010f; a[2] = -0.4099f; a[3] = 0.1370f; + + BH_Quat4fToAxis(a, r, &angle); + BH_VERIFY_DELTA(r[0], -0.9104f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -0.0010f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -0.4138f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(angle, 2.8668f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(FromAxis) +{ + float a[3], r[4]; + a[0] = -0.9104f; a[1] = -0.0010f; a[2] = -0.4138f; + + BH_Quat4fFromAxis(a, 2.8668f, r); + BH_VERIFY_DELTA(r[0], -0.9018f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -0.0010f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -0.4099f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.1370f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(InverseMultiply) +{ + float a[4], r[4]; + + a[0] = -0.9018f; a[1] = -0.0010f; a[2] = -0.4099f; a[3] = 0.1370f; + + BH_Quat4fInverse(a, r); + BH_Quat4fMul(a, r, r); + BH_VERIFY_DELTA(r[0], 0.000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 0.000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 0.000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 1.000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Identity); + BH_UNIT_ADD(Conjugate); + BH_UNIT_ADD(ToEuler); + BH_UNIT_ADD(FromEuler); + BH_UNIT_ADD(ToAxis); + BH_UNIT_ADD(FromAxis); + BH_UNIT_ADD(InverseMultiply); + + return BH_UnitRun(); +} diff --git a/test/src/TestRay2f.c b/test/src/TestRay2f.c new file mode 100644 index 0000000..811cd96 --- /dev/null +++ b/test/src/TestRay2f.c @@ -0,0 +1,196 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(RayIntersectLine) +{ + float a[2], b[2], p[2], d[2], r[3]; + float time; + + a[0] = 4.0000f; a[1] = 9.0000f; + b[0] = 2.0000f; b[1] = 5.0000f; + + p[0] = -5.0000f; p[1] = 3.0000f; + d[0] = 0.8944f; d[1] = 0.4472f; + + BH_VERIFY(BH_LineFromPoints(a, b, r) == BH_OK); + BH_VERIFY(BH_Ray2fIntersectLine(p, d, r, &time, r) == BH_OK); + + BH_VERIFY_DELTA(time, 8.9443f, ACCEPTABLE_DELTA * 10); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fScale(d, time, r); + BH_Vec2fAdd(p, r, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + d[0] = -2.0000f; d[1] = -4.0000f; + BH_VERIFY(BH_LineFromPoints(a, b, r) == BH_OK); + BH_VERIFY(BH_Ray2fIntersectLine(p, d, r, &time, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(RayIntersectRay) +{ + float p1[2], d1[2], p2[2], d2[2], r[2]; + float time; + + p1[0] = -5.0000f; p1[1] = 3.0000f; + d1[0] = 0.8944f; d1[1] = 0.4472f; + + p2[0] = 4.0000f; p2[1] = 9.0000f; + d2[0] = -0.4472f; d2[1] =-0.8944f; + + BH_VERIFY(BH_Ray2fIntersectRay(p1, d1, p2, d2, &time, r) == BH_OK); + + BH_VERIFY_DELTA(time, 8.9443f, ACCEPTABLE_DELTA * 10); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fScale(d1, time, r); + BH_Vec2fAdd(p1, r, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_VERIFY(BH_Ray2fIntersectRay(p1, d1, p2, d1, &time, r) != BH_OK); + + d1[0] = 0.0000f; d1[1] = 1.0000f; + BH_VERIFY(BH_Ray2fIntersectRay(p1, d1, p2, d2, &time, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(RayIntersectSegment) +{ + float p[2], d[2], a[2], b[2], r[2]; + float time; + + p[0] = -5.0000f; p[1] = 3.0000f; + d[0] = 0.8944f; d[1] = 0.4472f; + + a[0] = 4.0000f; a[1] = 9.0000f; + b[0] = 2.0000f; b[1] = 5.0000f; + + BH_VERIFY(BH_Ray2fIntersectSegment(p, d, a, b, &time, r) == BH_OK); + + BH_VERIFY_DELTA(time, 8.9443f, ACCEPTABLE_DELTA * 10); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fScale(d, time, r); + BH_Vec2fAdd(p, r, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + d[0] = -2.0000f; d[1] = -4.0000f; + BH_VERIFY(BH_Ray2fIntersectSegment(p, d, a, b, &time, r) != BH_OK); + + d[0] = 1.0000f; d[1] = 0.0000f; + BH_VERIFY(BH_Ray2fIntersectSegment(p, d, a, b, &time, r) != BH_OK); + + return 0; +} + + +BH_UNIT_TEST(SegmentIntersectLine) +{ + float a1[2], b1[2], a2[2], b2[2], r[3]; + float time; + + a1[0] = -5.0000f; a1[1] = 3.0000f; + b1[0] = 5.0000f; b1[1] = 8.0000f; + + a2[0] = 4.0000f; a2[1] = 9.0000f; + b2[0] = 2.0000f; b2[1] = 5.0000f; + + BH_VERIFY(BH_LineFromPoints(a2, b2, r) == BH_OK); + BH_VERIFY(BH_Segment2fIntersectLine(a1, b1, r, &time, r) == BH_OK); + + BH_VERIFY_DELTA(time, 0.8000f, ACCEPTABLE_DELTA * 10); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fLerp(a1, b1, time, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(SegmentIntersectSegment) +{ + float a1[2], b1[2], a2[2], b2[2], r[2]; + float time; + + a1[0] = -5.0000f; a1[1] = 3.0000f; + b1[0] = 5.0000f; b1[1] = 8.0000f; + + a2[0] = 4.0000f; a2[1] = 9.0000f; + b2[0] = 2.0000f; b2[1] = 5.0000f; + + BH_VERIFY(BH_Segment2fIntersectSegment(a1, b1, a2, b2, &time, r) == BH_OK); + + BH_VERIFY_DELTA(time, 0.8000f, ACCEPTABLE_DELTA * 10); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fLerp(a1, b1, time, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Time) +{ + float a1[2], b1[2], a2[2], b2[2], r[2]; + float time1, time2; + + a1[0] = -5.0000f; a1[1] = 3.0000f; + b1[0] = 10.0000f; b1[1] = 5.0000f; + + a2[0] = 4.0000f; a2[1] = 9.0000f; + b2[0] = -2.0000f; b2[1] =-4.0000f; + + BH_VERIFY(BH_Ray2fIntersectTime(a1, b1, a2, b2, &time1, &time2) == BH_OK); + + BH_VERIFY_DELTA(time1, 0.8000f, ACCEPTABLE_DELTA * 10); + BH_VERIFY_DELTA(time2, 0.5000f, ACCEPTABLE_DELTA * 10); + + BH_Vec2fScale(b1, time1, r); + BH_Vec2fAdd(a1, r, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fScale(b2, time2, r); + BH_Vec2fAdd(a2, r, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 7.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(RayIntersectLine); + BH_UNIT_ADD(RayIntersectRay); + BH_UNIT_ADD(RayIntersectSegment); + BH_UNIT_ADD(SegmentIntersectLine); + BH_UNIT_ADD(SegmentIntersectSegment); + BH_UNIT_ADD(Time); + + return BH_UnitRun(); +} diff --git a/test/src/TestRay3f.c b/test/src/TestRay3f.c new file mode 100644 index 0000000..6a80270 --- /dev/null +++ b/test/src/TestRay3f.c @@ -0,0 +1,99 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(RayIntersectTriangle) +{ + float a[3], b[3], c[3], p[3], d[3], out[3], t; + + a[0] =-3.0000f; a[1] = 1.0000f; a[2] = 2.0000f; + b[0] =-5.0000f; b[1] =-2.0000f; b[2] = 0.0000f; + c[0] =-6.0000f; c[1] = 2.5000f; c[2] =-1.0000f; + + p[0] =-1.5000f; p[1] = 1.0000f; p[2] = 1.0000f; + d[0] =-1.0000f; d[1] = 0.0000f; d[2] = 0.0000f; + + BH_VERIFY(BH_Ray3fIntersectTriangle(p, d, a, b, c, &t, out) == BH_OK); + + BH_VERIFY_DELTA(t, 2.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[0],-4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[1], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[2], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(SegmentIntersectTriangle) +{ + float a[3], b[3], c[3], d[3], f[3], out[3], t; + + a[0] =-3.0000f; a[1] = 1.0000f; a[2] = 2.0000f; + b[0] =-5.0000f; b[1] =-2.0000f; b[2] = 0.0000f; + c[0] =-6.0000f; c[1] = 2.5000f; c[2] =-1.0000f; + + d[0] =-1.5000f; d[1] = 1.0000f; d[2] = 1.0000f; + f[0] =-6.0000f; f[1] = 1.0000f; f[2] = 1.0000f; + + BH_VERIFY(BH_Segment3fIntersectTriangle(d, f, a, b, c, &t, out) == BH_OK); + BH_VERIFY_DELTA(out[0],-4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[1], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[2], 1.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fLerp(d, f, t, out); + BH_VERIFY_DELTA(out[0],-4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[1], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[2], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Barycentric) +{ + float a[3], b[3], c[3], p[3], d[3], out[3], t; + + a[0] =-3.0000f; a[1] = 1.0000f; a[2] = 2.0000f; + b[0] =-5.0000f; b[1] =-2.0000f; b[2] = 0.0000f; + c[0] =-6.0000f; c[1] = 2.5000f; c[2] =-1.0000f; + + p[0] =-1.5000f; p[1] = 1.0000f; p[2] = 1.0000f; + d[0] =-1.0000f; d[1] = 0.0000f; d[2] = 0.0000f; + + BH_VERIFY(BH_Ray3fIntersectTriangle(p, d, a, b, c, &t, out) == BH_OK); + (void)t; + + BH_Triangle3fBarycentric(a, b, c, out, out); + BH_VERIFY(out[0] >= 0.0f); + BH_VERIFY(out[1] >= 0.0f); + BH_VERIFY(out[2] >= 0.0f); + + BH_VERIFY(out[0] <= 1.0f); + BH_VERIFY(out[1] <= 1.0f); + BH_VERIFY(out[2] <= 1.0f); + + BH_VERIFY_DELTA(out[0] + out[1] + out[2], 1.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fBarycentric(a, b, c, out[1], out[2], out); + BH_VERIFY_DELTA(out[0],-4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[1], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(out[2], 1.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(RayIntersectTriangle); + BH_UNIT_ADD(SegmentIntersectTriangle); + BH_UNIT_ADD(Barycentric); + + return BH_UnitRun(); +} diff --git a/test/src/TestVec2f.c b/test/src/TestVec2f.c new file mode 100644 index 0000000..92db45f --- /dev/null +++ b/test/src/TestVec2f.c @@ -0,0 +1,284 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(Add) +{ + float a[2], b[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + + BH_Vec2fAdd(a, b, r); + BH_VERIFY_DELTA(r[0], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 8.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + float a[2], b[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + + BH_Vec2fSub(a, b, r); + BH_VERIFY_DELTA(r[0], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + float a[2], b[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + + BH_Vec2fMul(a, b, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 12.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + float a[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + + BH_Vec2fScale(a, 10.0000f, r); + BH_VERIFY_DELTA(r[0], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 20.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(MulAdd) +{ + float a[2], b[2], c[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + c[0] = 1.5000f; c[1] = 2.5000f; + + BH_Vec2fMulAdd(a, b, c, r); + BH_VERIFY_DELTA(r[0], 6.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 14.5000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Negate) +{ + float a[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + + BH_Vec2fNegate(a, r); + BH_VERIFY_DELTA(r[0], -1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Dot) +{ + float a[2], b[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + BH_VERIFY_DELTA(BH_Vec2fDot(a, b), 17.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Cross) +{ + float a[2], b[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + BH_VERIFY_DELTA(BH_Vec2fCross(a, b), -4.0f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Length) +{ + float a[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + BH_VERIFY_DELTA(BH_Vec2fLength(a), sqrt(5.0000f), ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Normal) +{ + float a[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + + BH_Vec2fNormal(a, r); + BH_VERIFY_DELTA(r[0], 1.0000f / sqrt(5.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f / sqrt(5.0000f), ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(BH_Vec2fNormalEx(a, r), sqrt(5.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[0], 1.0000f / sqrt(5.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f / sqrt(5.0000f), ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Min) +{ + float a[2], b[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + + BH_Vec2fMin(a, b, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fMin(b, a, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Max) +{ + float a[2], b[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + + BH_Vec2fMax(a, b, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fMax(b, a, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Lerp) +{ + float a[2], b[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + + BH_Vec2fLerp(a, b, 0.0000f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fLerp(a, b, 0.5000f, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 4.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fLerp(a, b, 1.0000f, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Project) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 4.0000f; b[1] = 3.0000f; + + BH_Vec2fProject(a, b, r); + BH_VERIFY_DELTA(r[0], 1.6000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 1.2000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Barycentric) +{ + float a[2], b[2], c[2], r[2]; + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; + c[0] = 1.5000f; c[1] = 2.5000f; + + BH_Vec2fBarycentric(a, b, c, 0.5f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 4.2500f, ACCEPTABLE_DELTA); + + BH_Vec2fBarycentric(a, b, c, 0.25f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 2.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.2500f, ACCEPTABLE_DELTA); + + a[0] = 1.0000f; a[1] = 2.0000f; + b[0] = 4.0000f; b[1] = 3.0000f; + c[0] = 4.0000f; c[1] = 4.0000f; + + BH_Vec2fBarycentric(a, b, c, 0.0f, 0.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + + BH_Vec2fBarycentric(a, b, c, 0.5f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.5000f, ACCEPTABLE_DELTA); + + BH_Vec2fBarycentric(a, b, c, 0.25f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.2500f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(MulAdd); + BH_UNIT_ADD(Negate); + BH_UNIT_ADD(Dot); + BH_UNIT_ADD(Cross); + BH_UNIT_ADD(Length); + BH_UNIT_ADD(Normal); + BH_UNIT_ADD(Min); + BH_UNIT_ADD(Max); + BH_UNIT_ADD(Lerp); + BH_UNIT_ADD(Project); + BH_UNIT_ADD(Barycentric); + + return BH_UnitRun(); +} diff --git a/test/src/TestVec2i.c b/test/src/TestVec2i.c new file mode 100644 index 0000000..cf216d8 --- /dev/null +++ b/test/src/TestVec2i.c @@ -0,0 +1,147 @@ +#include +#include + + +BH_UNIT_TEST(Add) +{ + int a[2], b[2], r[2]; + + a[0] = 1; a[1] = 2; + b[0] = 5; b[1] = 6; + + BH_Vec2iAdd(a, b, r); + BH_VERIFY(r[0] == 6); + BH_VERIFY(r[1] == 8); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + int a[2], b[2], r[2]; + + a[0] = 1; a[1] = 2; + b[0] = 5; b[1] = 6; + + BH_Vec2iSub(a, b, r); + BH_VERIFY(r[0] == -4); + BH_VERIFY(r[1] == -4); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + int a[2], b[2], r[2]; + + a[0] = 1; a[1] = 2; + b[0] = 5; b[1] = 6; + + BH_Vec2iMul(a, b, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 12); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + int a[2], r[2]; + + a[0] = 1; a[1] = 2; + + BH_Vec2iScale(a, 10, r); + BH_VERIFY(r[0] == 10); + BH_VERIFY(r[1] == 20); + + return 0; +} + + +BH_UNIT_TEST(MulAdd) +{ + int a[2], b[2], c[2], r[2]; + + a[0] = 1; a[1] = 2; + b[0] = 5; b[1] = 6; + c[0] = 4; c[1] = 3; + + BH_Vec2iMulAdd(a, b, c, r); + BH_VERIFY(r[0] == 9); + BH_VERIFY(r[1] == 15); + + return 0; +} + + +BH_UNIT_TEST(Negate) +{ + int a[2], r[2]; + + a[0] = 1; a[1] = 2; + + BH_Vec2iNegate(a, r); + BH_VERIFY(r[0] == -1); + BH_VERIFY(r[1] == -2); + + return 0; +} + + +BH_UNIT_TEST(Min) +{ + int a[2], b[2], r[2]; + + a[0] = 1; a[1] = 2; + b[0] = 5; b[1] = 6; + + BH_Vec2iMin(a, b, r); + BH_VERIFY(r[0] == 1); + BH_VERIFY(r[1] == 2); + + BH_Vec2iMin(b, a, r); + BH_VERIFY(r[0] == 1); + BH_VERIFY(r[1] == 2); + + return 0; +} + + +BH_UNIT_TEST(Max) +{ + int a[2], b[2], r[2]; + + a[0] = 1; a[1] = 2; + b[0] = 5; b[1] = 6; + + BH_Vec2iMax(a, b, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 6); + + BH_Vec2iMax(b, a, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 6); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(MulAdd); + BH_UNIT_ADD(Negate); + BH_UNIT_ADD(Min); + BH_UNIT_ADD(Max); + + return BH_UnitRun(); +} diff --git a/test/src/TestVec3f.c b/test/src/TestVec3f.c new file mode 100644 index 0000000..9e07aeb --- /dev/null +++ b/test/src/TestVec3f.c @@ -0,0 +1,309 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(Add) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fAdd(a, b, r); + BH_VERIFY_DELTA(r[0], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 8.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fSub(a, b, r); + BH_VERIFY_DELTA(r[0], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fMul(a, b, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 12.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 21.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + float a[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + + BH_Vec3fScale(a, 10.0000f, r); + BH_VERIFY_DELTA(r[0], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 20.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 30.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(MulAdd) +{ + float a[3], b[3], c[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + c[0] = 1.5000f; c[1] = 2.5000f; c[2] = 3.5000f; + + BH_Vec3fMulAdd(a, b, c, r); + BH_VERIFY_DELTA(r[0], 6.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 14.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 24.5000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Negate) +{ + float a[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + + BH_Vec3fNegate(a, r); + BH_VERIFY_DELTA(r[0], -1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Dot) +{ + float a[3], b[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + BH_VERIFY_DELTA(BH_Vec3fDot(a, b), 38.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Cross) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fCross(a, b, r); + BH_VERIFY_DELTA(r[0], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 8.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Length) +{ + float a[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + BH_VERIFY_DELTA(BH_Vec3fLength(a), sqrt(14.0000f), ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Normal) +{ + float a[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + + BH_Vec3fNormal(a, r); + BH_VERIFY_DELTA(r[0], 1.0000f / sqrt(14.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f / sqrt(14.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f / sqrt(14.0000f), ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(BH_Vec3fNormalEx(a, r), sqrt(14.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[0], 1.0000f / sqrt(14.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f / sqrt(14.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f / sqrt(14.0000f), ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Min) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fMin(a, b, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fMin(b, a, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Max) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fMax(a, b, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fMax(b, a, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Lerp) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + + BH_Vec3fLerp(a, b, 0.0000f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fLerp(a, b, 0.5000f, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fLerp(a, b, 1.0000f, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Project) +{ + float a[3], b[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 4.0000f; b[1] = 3.0000f; b[2] = 2.0000f; + + BH_Vec3fProject(a, b, r); + BH_VERIFY_DELTA(r[0], 2.2069f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 1.6552f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 1.1034f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Barycentric) +{ + float a[3], b[3], c[3], r[3]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; + c[0] = 1.5000f; c[1] = 2.5000f; c[2] = 3.5000f; + + BH_Vec3fBarycentric(a, b, c, 0.5f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 4.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 5.2500f, ACCEPTABLE_DELTA); + + BH_Vec3fBarycentric(a, b, c, 0.25f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 2.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 4.2500f, ACCEPTABLE_DELTA); + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; + b[0] = 4.0000f; b[1] = 3.0000f; b[2] = 2.0000f; + c[0] = 4.0000f; c[1] = 4.0000f; c[2] = 4.0000f; + + BH_Vec3fBarycentric(a, b, c, 0.0f, 0.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fBarycentric(a, b, c, 0.5f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + + BH_Vec3fBarycentric(a, b, c, 0.25f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.2500f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(MulAdd); + BH_UNIT_ADD(Negate); + BH_UNIT_ADD(Dot); + BH_UNIT_ADD(Cross); + BH_UNIT_ADD(Length); + BH_UNIT_ADD(Normal); + BH_UNIT_ADD(Min); + BH_UNIT_ADD(Max); + BH_UNIT_ADD(Lerp); + BH_UNIT_ADD(Project); + BH_UNIT_ADD(Barycentric); + + return BH_UnitRun(); +} diff --git a/test/src/TestVec3i.c b/test/src/TestVec3i.c new file mode 100644 index 0000000..f8ab9ee --- /dev/null +++ b/test/src/TestVec3i.c @@ -0,0 +1,157 @@ +#include +#include + + +BH_UNIT_TEST(Add) +{ + int a[3], b[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + b[0] = 5; b[1] = 6; b[2] = 7; + + BH_Vec3iAdd(a, b, r); + BH_VERIFY(r[0] == 6); + BH_VERIFY(r[1] == 8); + BH_VERIFY(r[2] == 10); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + int a[3], b[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + b[0] = 5; b[1] = 6; b[2] = 7; + + BH_Vec3iSub(a, b, r); + BH_VERIFY(r[0] == -4); + BH_VERIFY(r[1] == -4); + BH_VERIFY(r[2] == -4); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + int a[3], b[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + b[0] = 5; b[1] = 6; b[2] = 7; + + BH_Vec3iMul(a, b, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 12); + BH_VERIFY(r[2] == 21); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + int a[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + + BH_Vec3iScale(a, 10, r); + BH_VERIFY(r[0] == 10); + BH_VERIFY(r[1] == 20); + BH_VERIFY(r[2] == 30); + + return 0; +} + + +BH_UNIT_TEST(MulAdd) +{ + int a[3], b[3], c[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + b[0] = 5; b[1] = 6; b[2] = 7; + c[0] = 4; c[1] = 3; c[2] = 2; + + BH_Vec3iMulAdd(a, b, c, r); + BH_VERIFY(r[0] == 9); + BH_VERIFY(r[1] == 15); + BH_VERIFY(r[2] == 23); + + return 0; +} + + +BH_UNIT_TEST(Negate) +{ + int a[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + + BH_Vec3iNegate(a, r); + BH_VERIFY(r[0] == -1); + BH_VERIFY(r[1] == -2); + BH_VERIFY(r[2] == -3); + + return 0; +} + + +BH_UNIT_TEST(Min) +{ + int a[3], b[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + b[0] = 5; b[1] = 6; b[2] = 7; + + BH_Vec3iMin(a, b, r); + BH_VERIFY(r[0] == 1); + BH_VERIFY(r[1] == 2); + BH_VERIFY(r[2] == 3); + + BH_Vec3iMin(b, a, r); + BH_VERIFY(r[0] == 1); + BH_VERIFY(r[1] == 2); + BH_VERIFY(r[2] == 3); + + return 0; +} + + +BH_UNIT_TEST(Max) +{ + int a[3], b[3], r[3]; + + a[0] = 1; a[1] = 2; a[2] = 3; + b[0] = 5; b[1] = 6; b[2] = 7; + + BH_Vec3iMax(a, b, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 6); + BH_VERIFY(r[2] == 7); + + BH_Vec3iMax(b, a, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 6); + BH_VERIFY(r[2] == 7); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(MulAdd); + BH_UNIT_ADD(Negate); + BH_UNIT_ADD(Min); + BH_UNIT_ADD(Max); + + return BH_UnitRun(); +} diff --git a/test/src/TestVec4f.c b/test/src/TestVec4f.c new file mode 100644 index 0000000..3e0e877 --- /dev/null +++ b/test/src/TestVec4f.c @@ -0,0 +1,313 @@ +#include +#include + + +#define ACCEPTABLE_DELTA 0.0001f + + +BH_UNIT_TEST(Add) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + + BH_Vec4fAdd(a, b, r); + BH_VERIFY_DELTA(r[0], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 8.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 12.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + + BH_Vec4fSub(a, b, r); + BH_VERIFY_DELTA(r[0], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], -4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + + BH_Vec4fMul(a, b, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 12.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 21.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 32.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + float a[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + + BH_Vec4fScale(a, 10.0000f, r); + BH_VERIFY_DELTA(r[0], 10.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 20.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 30.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 40.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(MulAdd) +{ + float a[4], b[4], c[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + c[0] = 1.5000f; c[1] = 2.5000f; c[2] = 3.5000f; c[3] = 4.5000f; + + BH_Vec4fMulAdd(a, b, c, r); + BH_VERIFY_DELTA(r[0], 6.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 14.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 24.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 36.5000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Negate) +{ + float a[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + + BH_Vec4fNegate(a, r); + BH_VERIFY_DELTA(r[0], -1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], -2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], -3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], -4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Dot) +{ + float a[4], b[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + BH_VERIFY_DELTA(BH_Vec4fDot(a, b), 70.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Length) +{ + float a[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + BH_VERIFY_DELTA(BH_Vec4fLength(a), sqrt(30.0000f), ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Normal) +{ + float a[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + + BH_Vec4fNormal(a, r); + BH_VERIFY_DELTA(r[0], 1.0f / sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0f / sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0f / sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 4.0f / sqrt(30.0000f), ACCEPTABLE_DELTA); + + BH_VERIFY_DELTA(BH_Vec4fNormalEx(a, r), sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[0], 1.0000f / sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f / sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f / sqrt(30.0000f), ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 4.0000f / sqrt(30.0000f), ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Min) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + + BH_Vec4fMin(a, b, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 4.0000f, ACCEPTABLE_DELTA); + + BH_Vec4fMin(b, a, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 4.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Max) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + + BH_Vec4fMax(a, b, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); + + BH_Vec4fMax(b, a, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Lerp) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + + BH_Vec4fLerp(a, b, 0.0000f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 4.0000f, ACCEPTABLE_DELTA); + + BH_Vec4fLerp(a, b, 0.5000f, r); + BH_VERIFY_DELTA(r[0], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 6.0000f, ACCEPTABLE_DELTA); + + BH_Vec4fLerp(a, b, 1.0000f, r); + BH_VERIFY_DELTA(r[0], 5.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 6.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 7.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 8.0000f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Project) +{ + float a[4], b[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 4.0000f; b[1] = 3.0000f; b[2] = 2.0000f; b[3] = 1.0000f; + + BH_Vec4fProject(a, b, r); + BH_VERIFY_DELTA(r[0], 2.6667f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 1.3333f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 0.6667f, ACCEPTABLE_DELTA); + + return 0; +} + + +BH_UNIT_TEST(Barycentric) +{ + float a[4], b[4], c[4], r[4]; + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 5.0000f; b[1] = 6.0000f; b[2] = 7.0000f; b[3] = 8.0000f; + c[0] = 1.5000f; c[1] = 2.5000f; c[2] = 3.5000f; c[3] = 4.5000f; + + BH_Vec4fBarycentric(a, b, c, 0.5f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 4.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 5.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 6.2500f, ACCEPTABLE_DELTA); + + BH_Vec4fBarycentric(a, b, c, 0.25f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 2.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 4.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 5.2500f, ACCEPTABLE_DELTA); + + a[0] = 1.0000f; a[1] = 2.0000f; a[2] = 3.0000f; a[3] = 4.0000f; + b[0] = 4.0000f; b[1] = 3.0000f; b[2] = 2.0000f; b[3] = 1.0000f; + c[0] = 4.0000f; c[1] = 4.0000f; c[2] = 4.0000f; c[3] = 4.0000f; + + BH_Vec4fBarycentric(a, b, c, 0.0f, 0.0f, r); + BH_VERIFY_DELTA(r[0], 1.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 2.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 4.0000f, ACCEPTABLE_DELTA); + + BH_Vec4fBarycentric(a, b, c, 0.5f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 4.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.5000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.0000f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 2.5000f, ACCEPTABLE_DELTA); + + BH_Vec4fBarycentric(a, b, c, 0.25f, 0.5f, r); + BH_VERIFY_DELTA(r[0], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[1], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[2], 3.2500f, ACCEPTABLE_DELTA); + BH_VERIFY_DELTA(r[3], 3.2500f, ACCEPTABLE_DELTA); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(MulAdd); + BH_UNIT_ADD(Negate); + BH_UNIT_ADD(Dot); + BH_UNIT_ADD(Length); + BH_UNIT_ADD(Normal); + BH_UNIT_ADD(Min); + BH_UNIT_ADD(Max); + BH_UNIT_ADD(Lerp); + BH_UNIT_ADD(Project); + BH_UNIT_ADD(Barycentric); + + return BH_UnitRun(); +} diff --git a/test/src/TestVec4i.c b/test/src/TestVec4i.c new file mode 100644 index 0000000..ebd682c --- /dev/null +++ b/test/src/TestVec4i.c @@ -0,0 +1,167 @@ +#include +#include + + +BH_UNIT_TEST(Add) +{ + int a[4], b[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; + + BH_Vec4iAdd(a, b, r); + BH_VERIFY(r[0] == 6); + BH_VERIFY(r[1] == 8); + BH_VERIFY(r[2] == 10); + BH_VERIFY(r[3] == 12); + + return 0; +} + + +BH_UNIT_TEST(Sub) +{ + int a[4], b[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; + + BH_Vec4iSub(a, b, r); + BH_VERIFY(r[0] == -4); + BH_VERIFY(r[1] == -4); + BH_VERIFY(r[2] == -4); + BH_VERIFY(r[3] == -4); + + return 0; +} + + +BH_UNIT_TEST(Mul) +{ + int a[4], b[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; + + BH_Vec4iMul(a, b, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 12); + BH_VERIFY(r[2] == 21); + BH_VERIFY(r[3] == 32); + + return 0; +} + + +BH_UNIT_TEST(Scale) +{ + int a[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + + BH_Vec4iScale(a, 10, r); + BH_VERIFY(r[0] == 10); + BH_VERIFY(r[1] == 20); + BH_VERIFY(r[2] == 30); + BH_VERIFY(r[3] == 40); + + return 0; +} + + +BH_UNIT_TEST(MulAdd) +{ + int a[4], b[4], c[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; + c[0] = 4; c[1] = 3; c[2] = 2; c[3] = 1; + + BH_Vec4iMulAdd(a, b, c, r); + BH_VERIFY(r[0] == 9); + BH_VERIFY(r[1] == 15); + BH_VERIFY(r[2] == 23); + BH_VERIFY(r[3] == 33); + + return 0; +} + + +BH_UNIT_TEST(Negate) +{ + int a[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + + BH_Vec4iNegate(a, r); + BH_VERIFY(r[0] == -1); + BH_VERIFY(r[1] == -2); + BH_VERIFY(r[2] == -3); + BH_VERIFY(r[3] == -4); + + return 0; +} + + +BH_UNIT_TEST(Min) +{ + int a[4], b[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; + + BH_Vec4iMin(a, b, r); + BH_VERIFY(r[0] == 1); + BH_VERIFY(r[1] == 2); + BH_VERIFY(r[2] == 3); + BH_VERIFY(r[3] == 4); + + BH_Vec4iMin(b, a, r); + BH_VERIFY(r[0] == 1); + BH_VERIFY(r[1] == 2); + BH_VERIFY(r[2] == 3); + BH_VERIFY(r[3] == 4); + + return 0; +} + + +BH_UNIT_TEST(Max) +{ + int a[4], b[4], r[4]; + + a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; + b[0] = 5; b[1] = 6; b[2] = 7; b[3] = 8; + + BH_Vec4iMax(a, b, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 6); + BH_VERIFY(r[2] == 7); + BH_VERIFY(r[3] == 8); + + BH_Vec4iMax(b, a, r); + BH_VERIFY(r[0] == 5); + BH_VERIFY(r[1] == 6); + BH_VERIFY(r[2] == 7); + BH_VERIFY(r[3] == 8); + + return 0; +} + + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + BH_UNIT_ADD(Add); + BH_UNIT_ADD(Sub); + BH_UNIT_ADD(Mul); + BH_UNIT_ADD(Scale); + BH_UNIT_ADD(MulAdd); + BH_UNIT_ADD(Negate); + BH_UNIT_ADD(Min); + BH_UNIT_ADD(Max); + + return BH_UnitRun(); +} diff --git a/unit/include/BH/Unit.h b/unit/include/BH/Unit.h index 3b3b2b6..c715c23 100644 --- a/unit/include/BH/Unit.h +++ b/unit/include/BH/Unit.h @@ -28,12 +28,20 @@ typedef int (*BH_UnitCallback)(void); if (BH_VERIFY_DELTA < 0.0) \ BH_VERIFY_DELTA = -BH_VERIFY_DELTA; \ if (BH_VERIFY_DELTA > (e)) { \ - printf("%s:%d\t%s (differs by %f)\n", __FILE__, __LINE__, #x " == " #y, BH_VERIFY_DELTA); \ + printf("%s:%d\t%s (differs by %f)\n", \ + __FILE__, __LINE__, #x " == " #y, BH_VERIFY_DELTA); \ return -1; \ } \ } while(0) +#define BH_UNIT_TEST(name) \ + static int unit##name(void) + + +#define BH_UNIT_ADD(name) \ + BH_UnitAdd(#name, unit##name) + /** * Adds unit test \a cb with name \a name for the testing. diff --git a/unit/src/Unit.c b/unit/src/Unit.c index 57d80b1..a0b61e1 100644 --- a/unit/src/Unit.c +++ b/unit/src/Unit.c @@ -55,6 +55,7 @@ void BH_UnitAdd(const char *name, BH_UnitCallback cb) int BH_UnitRun(void) { BH_Unit *current; + int result = 0; printf("Running tests...\n"); current = root; @@ -64,15 +65,16 @@ int BH_UnitRun(void) if (current->cb()) { printf("\tFAIL\n"); - BH_UnitCleanup(); - return -1; + result = -1; } - printf("\tPASS\n"); + else + printf("\tPASS\n"); + fflush(stdout); current = current->next; } BH_UnitCleanup(); - return 0; + return result; }