Add tests for Frustum and fix bugs associated with it

pull/218/head
James Ball 2024-02-11 18:56:46 +00:00
rodzic 2c7d6398fd
commit 8ff283aba8
6 zmienionych plików z 130 dodań i 28 usunięć

Wyświetl plik

@ -1,39 +1,116 @@
#include <JuceHeader.h>
#include "shape/Point.h"
#include "obj/Frustum.h"
class MyTest : public juce::UnitTest {
class FrustumTest : public juce::UnitTest {
public:
MyTest() : juce::UnitTest("Foobar testing") {}
FrustumTest() : juce::UnitTest("Frustum Culling") {}
void runTest() override {
beginTest("Part 1");
double focalLength = 1;
double ratio = 1;
double nearDistance = 0.1;
double farDistance = 100;
expect(true);
Frustum frustum(focalLength, ratio, nearDistance, farDistance);
frustum.setCameraOrigin(Point(0, 0, -focalLength));
beginTest("Part 2");
beginTest("Focal Plane Frustum In-Bounds");
expect(true);
// Focal plane is at z = 0
Point points[][2] = {
{Point(0, 0, 0), Point(0, 0, 0)},
{Point(1, 1, 0), Point(1, 1, 0)},
{Point(-1, -1, 0), Point(-1, -1, 0)},
{Point(1, -1, 0), Point(1, -1, 0)},
{Point(-1, 1, 0), Point(-1, 1, 0)},
{Point(0.5, 0.5, 0), Point(0.5, 0.5, 0)},
};
testFrustumClippedEqualsExpected(points, frustum, 6);
beginTest("Focal Plane Frustum Out-Of-Bounds");
// Focal plane is at z = 0
Point points2[][2] = {
{Point(1.1, 1.1, 0), Point(1, 1, 0)},
{Point(-1.1, -1.1, 0), Point(-1, -1, 0)},
{Point(1.1, -1.1, 0), Point(1, -1, 0)},
{Point(-1.1, 1.1, 0), Point(-1, 1, 0)},
{Point(1.1, 0.5, 0), Point(1, 0.5, 0)},
{Point(-1.1, 0.5, 0), Point(-1, 0.5, 0)},
{Point(0.5, -1.1, 0), Point(0.5, -1, 0)},
{Point(0.5, 1.1, 0), Point(0.5, 1, 0)},
{Point(10, 10, 0), Point(1, 1, 0)},
{Point(-10, -10, 0), Point(-1, -1, 0)},
{Point(10, -10, 0), Point(1, -1, 0)},
{Point(-10, 10, 0), Point(-1, 1, 0)},
};
testFrustumClippedEqualsExpected(points2, frustum, 12);
beginTest("Behind Camera Out-Of-Bounds");
double minZWorldCoords = -focalLength + nearDistance;
Point points3[][2] = {
{Point(0, 0, -focalLength), Point(0, 0, minZWorldCoords)},
{Point(0, 0, -100), Point(0, 0, minZWorldCoords)},
{Point(0.5, 0.5, -focalLength), Point(0.1, 0.1, minZWorldCoords)},
{Point(10, -10, -focalLength), Point(0.1, -0.1, minZWorldCoords)},
{Point(-0.5, 0.5, -100), Point(-0.1, 0.1, minZWorldCoords)},
{Point(-10, 10, -100), Point(-0.1, 0.1, minZWorldCoords)},
};
testFrustumClippedEqualsExpected(points3, frustum, 6);
beginTest("3D Point Out-Of-Bounds");
Point points4[] = {
Point(1, 1, -0.1),
Point(-1, -1, -0.1),
Point(1, -1, -0.1),
Point(-1, 1, -0.1),
Point(0.5, 0.5, minZWorldCoords),
};
testFrustumClipOccurs(points4, frustum, 5);
}
Point project(Point& p, double focalLength) {
return Point(
p.x * focalLength / p.z,
p.y * focalLength / p.z,
0
);
}
juce::String errorMessage(Point& actual, Point& expected) {
return "Expected: " + expected.toString() + " Got: " + actual.toString();
}
void testFrustumClippedEqualsExpected(Point points[][2], Frustum& frustum, int length) {
for (int i = 0; i < length; i++) {
Point p = points[i][0];
frustum.clipToFrustum(p);
expect(p == points[i][1], errorMessage(p, points[i][1]));
}
}
void testFrustumClipOccurs(Point points[], Frustum& frustum, int length) {
for (int i = 0; i < length; i++) {
Point p = points[i];
frustum.clipToFrustum(p);
expect(p != points[i], errorMessage(p, points[i]));
}
}
};
class MyTest2 : public juce::UnitTest {
public:
MyTest2() : juce::UnitTest("Foobar testing 2") {}
void runTest() override {
beginTest("Part A");
expect(true);
beginTest("Part B");
expect(true);
}
};
static MyTest2 test2;
static FrustumTest frustumTest;
int main(int argc, char* argv[]) {
juce::UnitTestRunner runner;
runner.setAssertOnFailure(false);
runner.runAllTests();

Wyświetl plik

@ -68,8 +68,13 @@ Point PerspectiveEffect::apply(int index, Point input, const std::vector<double>
Point p = Point(x3, y3, z3);
Frustum frustum = Frustum(focalLength, 1.0, 0.1, 1000);
Point origin = Point(0, 0, -focalLength - depth);
frustum.setCameraOrigin(origin);
frustum.clipToFrustum(p);
// need to convert point from world space to camera space before projecting
p = p - origin;
return Point(
(1 - effectScale) * input.x + effectScale * (p.x * focalLength / p.z),
(1 - effectScale) * input.y + effectScale * (p.y * focalLength / p.z),

Wyświetl plik

@ -9,8 +9,7 @@ void Frustum::setCameraInternals(float focalLength, float ratio, float nearDista
this->farDistance = farDistance;
// compute width and height of the near section
float fov = 2 * std::atan(1 / (focalLength * 2));
origin = Point(0, 0, -focalLength);
float fov = 2 * std::atan(1 / focalLength);
tang = (float) std::tan(fov * 0.5);
height = nearDistance * tang;
width = height * ratio;
@ -29,7 +28,7 @@ void Frustum::clipToFrustum(Point &p) {
// compute and test the Y coordinate
pcy = v.innerProduct(Y);
aux = pcz * tang;
aux = std::abs(pcz * tang);
pcy = juce::jlimit(-aux, aux, pcy);
// compute and test the X coordinate
@ -42,5 +41,5 @@ void Frustum::clipToFrustum(Point &p) {
Point y = Y * pcy;
Point z = Z * pcz;
p = x + y + z;
p = x + y + z + origin;
}

Wyświetl plik

@ -9,7 +9,7 @@ class Frustum {
public:
float ratio, nearDistance, farDistance, width, height, tang;
Point origin = Point(0, 0, 0);
Point origin = Point(0, 0, -1);
Point X = Point(1, 0, 0);
Point Y = Point(0, 1, 0);
@ -20,6 +20,9 @@ public:
}
~Frustum() {};
void setCameraOrigin(Point& p) {
origin = p;
}
void setCameraInternals(float fov, float ratio, float nearD, float farD);
void clipToFrustum(Point &p);
};

Wyświetl plik

@ -1,5 +1,7 @@
#include "Point.h"
double Point::EPSILON = 0.0001;
Point::Point() : x(0), y(0), z(0) {}
Point::Point(double val) : x(val), y(val), z(0) {}
@ -78,6 +80,14 @@ Point& Point::operator=(const Point& other) {
return *this;
}
bool Point::operator==(const Point& other) {
return std::abs(x - other.x) < EPSILON && std::abs(y - other.y) < EPSILON && std::abs(z - other.z) < EPSILON;
}
bool Point::operator!=(const Point& other) {
return !(*this == other);
}
Point Point::operator+(const Point& other) {
return Point(x + other.x, y + other.y, z + other.z);
}
@ -97,3 +107,7 @@ Point Point::operator*(const Point& other) {
Point Point::operator*(double scalar) {
return Point(x * scalar, y * scalar, z * scalar);
}
std::string Point::toString() {
return std::string("(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ")");
}

Wyświetl plik

@ -23,8 +23,11 @@ public:
void normalize();
double innerProduct(Point& other);
// copy assignment operator
std::string toString();
Point& operator=(const Point& other);
bool operator==(const Point& other);
bool operator!=(const Point& other);
Point operator+(const Point& other);
Point operator-(const Point& other);
Point operator-();
@ -33,4 +36,5 @@ public:
double x, y, z;
static double EPSILON;
};