Browse Source

frustum and tests

Kajetan Johannes Hammerle 3 years ago
parent
commit
799b6f2c32
9 changed files with 196 additions and 4 deletions
  1. 2 0
      Main.cpp
  2. 64 0
      math/Frustum.cpp
  3. 38 0
      math/Frustum.h
  4. 2 3
      math/Plane.cpp
  5. 1 1
      meson.build
  6. 67 0
      tests/FrustumTests.cpp
  7. 8 0
      tests/FrustumTests.h
  8. 4 0
      utils/Size.cpp
  9. 10 0
      utils/Size.h

+ 2 - 0
Main.cpp

@@ -11,6 +11,7 @@
 #include "tests/StackTests.h"
 #include "tests/MatrixStackTests.h"
 #include "tests/PlaneTests.h"
+#include "tests/FrustumTests.h"
 
 int main() {
     ArrayTests::test();
@@ -26,5 +27,6 @@ int main() {
     StackTests::test();
     MatrixStackTests::test();
     PlaneTests::test();
+    FrustumTests::test();
     return 0;
 }

+ 64 - 0
math/Frustum.cpp

@@ -0,0 +1,64 @@
+#include "math/Frustum.h"
+
+Frustum::Frustum(float fieldOfView, float nearClip, float farClip, const Size& size) : size(size),
+fieldOfView(fieldOfView), nearClip(nearClip), farClip(farClip) {
+}
+
+Matrix& Frustum::updateProjection() {
+    float tan = tanf(fieldOfView * (0.5f * M_PI / 180.0f));
+    float q = 1.0f / tan;
+    float aspect = static_cast<float> (size.width) / size.height;
+    float diff = 1.0f / (nearClip - farClip);
+
+    projection.set(0, Vector4(q / aspect, 0.0f, 0.0f, 0.0f));
+    projection.set(1, Vector4(0.0f, q, 0.0f, 0.0f));
+    projection.set(2, Vector4(0.0f, 0.0f, (nearClip + farClip) * diff, (2.0f * nearClip * farClip) * diff));
+    projection.set(3, Vector4(0.0f, 0.0f, -1.0f, 0.0f));
+    return projection;
+}
+
+void Frustum::updatePlanes(const Vector3& pos, const Vector3& right, const Vector3& up, const Vector3& front) {
+    float tan = tanf(fieldOfView * (0.5f * M_PI / 180.0f));
+    float aspect = static_cast<float> (size.width) / size.height;
+
+    float halfNearHeight = tan * nearClip;
+    float halfNearWidth = halfNearHeight * aspect;
+
+    float halfFarHeight = tan * farClip;
+    float halfFarWidth = halfFarHeight * aspect;
+
+    Vector3 farCenter = pos + front * farClip;
+    Vector3 farTopLeft = farCenter + (up * halfFarHeight) - (right * halfFarWidth);
+    Vector3 farTopRight = farCenter + (up * halfFarHeight) + (right * halfFarWidth);
+    Vector3 farBottomRight = farCenter - (up * halfFarHeight) + (right * halfFarWidth);
+
+    Vector3 nearCenter = pos + front * nearClip;
+    Vector3 nearTopLeft = nearCenter + (up * halfNearHeight) - (right * halfNearWidth);
+    Vector3 nearBottomLeft = nearCenter - (up * halfNearHeight) - (right * halfNearWidth);
+    Vector3 nearBottomRight = nearCenter - (up * halfNearHeight) + (right * halfNearWidth);
+
+    planes[0] = Plane(nearBottomRight, nearTopLeft, nearBottomLeft); // near plane
+    planes[1] = Plane(farTopRight, farBottomRight, farTopLeft); // far plane
+    planes[2] = Plane(nearBottomRight, nearBottomLeft, farBottomRight); // bottom plane
+    planes[3] = Plane(farTopLeft, nearTopLeft, farTopRight); // top plane
+    planes[4] = Plane(nearBottomLeft, nearTopLeft, farTopLeft); // left plane
+    planes[5] = Plane(farBottomRight, farTopRight, nearBottomRight); // right plane
+}
+
+bool Frustum::isInside(const Vector3& pos) const {
+    return planes[0].getSignedDistance(pos) >= 0.0f &&
+            planes[1].getSignedDistance(pos) >= 0.0f &&
+            planes[2].getSignedDistance(pos) >= 0.0f &&
+            planes[3].getSignedDistance(pos) >= 0.0f &&
+            planes[4].getSignedDistance(pos) >= 0.0f &&
+            planes[5].getSignedDistance(pos) >= 0.0f;
+}
+
+bool Frustum::isInside(const Vector3& pos, float radius) const {
+    return planes[0].getSignedDistance(pos) >= -radius &&
+            planes[1].getSignedDistance(pos) >= -radius &&
+            planes[2].getSignedDistance(pos) >= -radius &&
+            planes[3].getSignedDistance(pos) >= -radius &&
+            planes[4].getSignedDistance(pos) >= -radius &&
+            planes[5].getSignedDistance(pos) >= -radius;
+}

+ 38 - 0
math/Frustum.h

@@ -0,0 +1,38 @@
+#ifndef FRUSTUM_H
+#define FRUSTUM_H
+
+#include "math/Matrix.h"
+#include "utils/Size.h"
+#include "utils/Array.h"
+#include "math/Plane.h"
+#include "utils/StringBuffer.h"
+
+class Frustum final {
+    Matrix projection;
+    const Size& size;
+    Array<Plane, 6> planes;
+
+public:
+    float fieldOfView;
+    float nearClip;
+    float farClip;
+
+    Frustum(float fieldOfView, float nearClip, float farClip, const Size& size);
+    Matrix& updateProjection();
+    void updatePlanes(const Vector3& pos, const Vector3& right, const Vector3& up, const Vector3& front);
+    
+    bool isInside(const Vector3& pos) const;
+    bool isInside(const Vector3& pos, float radius) const;
+
+    template<int L>
+    void toString(StringBuffer<L>& s) const {
+        s.append("(fieldOfView = ").append(fieldOfView);
+        s.append(", nearClip = ").append(nearClip);
+        s.append(", farClip = ").append(farClip);
+        s.append(", width = ").append(size.width);
+        s.append(", height = ").append(size.height);
+        s.append(')');
+    }
+};
+
+#endif

+ 2 - 3
math/Plane.cpp

@@ -3,9 +3,8 @@
 Plane::Plane() : d(0) {
 }
 
-Plane::Plane(const Vector3& a, const Vector3& b, const Vector3& c) {
-    abc = static_cast<Vector3>(b - a).cross(c - a).normalize();
-    d = -abc.dot(a);
+Plane::Plane(const Vector3& a, const Vector3& b, const Vector3& c) :
+abc(static_cast<Vector3> (b - a).cross(c - a).normalize()), d(-abc.dot(b)) {
 }
 
 float Plane::getSignedDistance(const Vector3& v) const {

+ 1 - 1
meson.build

@@ -1,6 +1,6 @@
 project('gaming core tests', 'cpp')
 
-sources = ['Main.cpp', 'tests/Test.cpp', 'tests/ArrayTests.cpp', 'tests/HashMapTests.cpp', 'tests/ListTests.cpp', 'tests/BitArrayTests.cpp', 'tests/StringBufferTests.cpp', 'tests/RandomTests.cpp', 'utils/Random.cpp', 'tests/RingBufferTests.cpp', 'tests/SplitStringTests.cpp', 'tests/VectorTests.cpp', 'math/Vector.cpp', 'math/Matrix.cpp', 'tests/MatrixTests.cpp', 'tests/StackTests.cpp', 'tests/MatrixStackTests.cpp', 'tests/PlaneTests.cpp', 'math/Plane.cpp']
+sources = ['Main.cpp', 'tests/Test.cpp', 'tests/ArrayTests.cpp', 'tests/HashMapTests.cpp', 'tests/ListTests.cpp', 'tests/BitArrayTests.cpp', 'tests/StringBufferTests.cpp', 'tests/RandomTests.cpp', 'utils/Random.cpp', 'tests/RingBufferTests.cpp', 'tests/SplitStringTests.cpp', 'tests/VectorTests.cpp', 'math/Vector.cpp', 'math/Matrix.cpp', 'tests/MatrixTests.cpp', 'tests/StackTests.cpp', 'tests/MatrixStackTests.cpp', 'tests/PlaneTests.cpp', 'math/Plane.cpp', 'tests/FrustumTests.cpp', 'math/Frustum.cpp', 'utils/Size.cpp']
 
 executable('tests', 
     sources: sources,

+ 67 - 0
tests/FrustumTests.cpp

@@ -0,0 +1,67 @@
+#include "tests/FrustumTests.h"
+#include "tests/Test.h"
+#include "math/Frustum.h"
+#include "utils/StringBuffer.h"
+
+typedef StringBuffer<250> String;
+
+static void testToString(Test& test) {
+    Size size(200, 100);
+    Frustum f(60.0f, 0.1f, 1000.0f, size);
+    test.checkEqual(String("("
+            "fieldOfView = 60.00, "
+            "nearClip = 0.10, "
+            "farClip = 1000.00, "
+            "width = 200, "
+            "height = 100)"), String(f), "to string");
+}
+
+static void testPointIsInside(Test& test) {
+    Size size(200, 100);
+    Frustum f(60.0f, 0.1f, 1000.0f, size);
+    f.updatePlanes(Vector3(0.0f, 0.0f, 0.0f),
+            Vector3(1.0f, 0.0f, 0.0f),
+            Vector3(0.0f, 1.0f, 0.0f),
+            Vector3(0.0f, 0.0f, 1.0f));
+    
+    test.checkEqual(true, f.isInside(Vector3(0.0f, 0.0f, 5.0f)), "point is inside 1");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, 0.0f, 1004.0f)), "point is inside 2");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, 0.0f, -5.0f)), "point is inside 3");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, 50.0f, 5.0f)), "point is inside 4");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, -50.0f, 5.0f)), "point is inside 5");
+    test.checkEqual(false, f.isInside(Vector3(50.0f, 0.0f, 5.0f)), "point is inside 6");
+    test.checkEqual(false, f.isInside(Vector3(-50.0f, 0.0f, 5.0f)), "point is inside 7");
+}
+
+static void testSphereIsInside(Test& test) {
+    Size size(200, 100);
+    Frustum f(60.0f, 0.1f, 1000.0f, size);
+    f.updatePlanes(Vector3(0.0f, 0.0f, 0.0f),
+            Vector3(1.0f, 0.0f, 0.0f),
+            Vector3(0.0f, 1.0f, 0.0f),
+            Vector3(0.0f, 0.0f, 1.0f));
+    
+    test.checkEqual(true, f.isInside(Vector3(0.0f, 0.0f, 5.0f), 3.0f), "sphere is inside 1");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, 0.0f, 1004.0f), 3.0f), "sphere is inside 2");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, 0.0f, -5.0f), 3.0f), "sphere is inside 3");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, 50.0f, 5.0f), 3.0f), "sphere is inside 4");
+    test.checkEqual(false, f.isInside(Vector3(0.0f, -50.0f, 5.0f), 3.0f), "sphere is inside 5");
+    test.checkEqual(false, f.isInside(Vector3(50.0f, 0.0f, 5.0f), 3.0f), "sphere is inside 6");
+    test.checkEqual(false, f.isInside(Vector3(-50.0f, 0.0f, 5.0f), 3.0f), "sphere is inside 7");
+
+    test.checkEqual(true, f.isInside(Vector3(0.0f, 0.0f, 5.0f), 3.0f), "sphere is inside 8");
+    test.checkEqual(true, f.isInside(Vector3(0.0f, 0.0f, 1004.0f), 50.0f), "sphere is inside 9");
+    test.checkEqual(true, f.isInside(Vector3(0.0f, 0.0f, -5.0f), 50.0f), "sphere is inside 10");
+    test.checkEqual(true, f.isInside(Vector3(0.0f, 50.0f, 5.0f), 50.0f), "sphere is inside 11");
+    test.checkEqual(true, f.isInside(Vector3(0.0f, -50.0f, 5.0f), 50.0f), "sphere is inside 12");
+    test.checkEqual(true, f.isInside(Vector3(50.0f, 0.0f, 5.0f), 50.0f), "sphere is inside 13");
+    test.checkEqual(true, f.isInside(Vector3(-50.0f, 0.0f, 5.0f), 50.0f), "sphere is inside 14");
+}
+
+void FrustumTests::test() {
+    Test test("Frustum");
+    testToString(test);
+    testPointIsInside(test);
+    testSphereIsInside(test);
+    test.finalize();
+}

+ 8 - 0
tests/FrustumTests.h

@@ -0,0 +1,8 @@
+#ifndef FRUSTUMTESTS_H
+#define FRUSTUMTESTS_H
+
+namespace FrustumTests {
+    void test();
+}
+
+#endif

+ 4 - 0
utils/Size.cpp

@@ -0,0 +1,4 @@
+#include "utils/Size.h"
+
+Size::Size(int width, int height) : width(width), height(height) {
+}

+ 10 - 0
utils/Size.h

@@ -0,0 +1,10 @@
+#ifndef SIZE_H
+#define SIZE_H
+
+struct Size final {
+    int width;
+    int height;
+    Size(int width, int height);
+};
+
+#endif