From b8ecfbefcd9efd68e12413166490d8c9e76c1feb Mon Sep 17 00:00:00 2001 From: Ange Yaghi Date: Sun, 27 Jan 2019 21:38:21 -0500 Subject: [PATCH] [#88] : Added kd tree implementation based on PBR --- demos/box_city_demo.cpp | 11 +- demos/complex_room_demo.cpp | 24 +- demos/demos.h | 1 + demos/main.cpp | 8 +- demos/pen_demo.cpp | 7 +- demos/stress_spiders.cpp | 9 +- demos/teapot_lamp_demo.cpp | 9 +- include/kd_tree.h | 173 +++++++ include/light_ray.h | 2 + include/manta_math.h | 6 + include/primitives.h | 90 ++++ project/mantaray/mantaray.vcxproj | 4 + project/mantaray/mantaray.vcxproj.filters | 18 + project/mantaray_test/mantaray_test.vcxproj | 1 + .../mantaray_test.vcxproj.filters | 3 + src/kd_tree.cpp | 425 ++++++++++++++++++ src/light_ray.cpp | 2 + src/manta_math_float_simd.cpp | 20 + src/primitives.cpp | 26 ++ test/geometry/cube.obj | 30 ++ test/geometry/two_cubes.obj | 58 +++ test/kdtree_tests.cpp | 207 +++++++++ test/primitives.cpp | 73 ++- tracking/BuildVersion.txt | 45 +- 24 files changed, 1229 insertions(+), 23 deletions(-) create mode 100644 include/kd_tree.h create mode 100644 include/primitives.h create mode 100644 src/kd_tree.cpp create mode 100644 src/primitives.cpp create mode 100644 test/geometry/cube.obj create mode 100644 test/geometry/two_cubes.obj create mode 100644 test/kdtree_tests.cpp diff --git a/demos/box_city_demo.cpp b/demos/box_city_demo.cpp index 94ed7010..a116aa64 100644 --- a/demos/box_city_demo.cpp +++ b/demos/box_city_demo.cpp @@ -69,7 +69,11 @@ void manta_demo::boxCityDemo(int samplesPerPixel, int resolutionX, int resolutio octree.initialize(100.0, math::loadVector(0, 0, 0)); octree.analyze(&boxCity, 25); - octree.writeToObjFile("../../workspace/test_results/box_city_octree.obj", nullptr); + KDTree kdtree; + kdtree.initialize(100.0, math::constants::Zero); + kdtree.analyze(&boxCity, 4); + + kdtree.writeToObjFile("../../workspace/test_results/box_city_kdtree.obj"); std::cout << "Scene vertices/faces: " << boxCity.getVertexCount() << "/" << boxCity.getFaceCount() << std::endl; std::cout << "Octree faces: " << octree.countFaces() << std::endl; @@ -78,7 +82,7 @@ void manta_demo::boxCityDemo(int samplesPerPixel, int resolutionX, int resolutio constexpr bool useOctree = true; SceneObject *boxCityObject = scene.createSceneObject(); - if (useOctree) boxCityObject->setGeometry(&octree); + if (useOctree) boxCityObject->setGeometry(&kdtree); else boxCityObject->setGeometry(&boxCity); boxCityObject->setDefaultMaterial(&wallMaterial); @@ -101,7 +105,7 @@ void manta_demo::boxCityDemo(int samplesPerPixel, int resolutionX, int resolutio math::Vector cameraPos = math::loadVector(15.4473, 4.59977, 13.2961); math::Vector target = math::loadVector(2.63987, 3.55547, 2.42282); - constexpr bool regularCamera = false; + constexpr bool regularCamera = true; CameraRayEmitterGroup *group; @@ -192,6 +196,7 @@ void manta_demo::boxCityDemo(int samplesPerPixel, int resolutionX, int resolutio boxCity.destroy(); boxCityObj.destroy(); octree.destroy(); + kdtree.destroy(); std::cout << "Standard allocator memory leaks: " << StandardAllocator::Global()->getLedger() << ", " << StandardAllocator::Global()->getCurrentUsage() << std::endl; } diff --git a/demos/complex_room_demo.cpp b/demos/complex_room_demo.cpp index 14f6d5c5..fa4f9400 100644 --- a/demos/complex_room_demo.cpp +++ b/demos/complex_room_demo.cpp @@ -117,13 +117,16 @@ void manta_demo::complexRoomDemo(int samplesPerPixel, int resolutionX, int resol sampleRay.setDirection(dir); sampleRay.setSource(cameraPos); - Octree stressSpidersOctree; - stressSpidersOctree.initialize(32, math::constants::Zero); - stressSpidersOctree.analyze(&stressSpiders, 10); //25 - stressSpidersOctree.writeToObjFile("../../workspace/test_results/complex_room_octree.obj", &sampleRay); + //Octree stressSpidersOctree; + //stressSpidersOctree.initialize(32, math::constants::Zero); + //stressSpidersOctree.analyze(&stressSpiders, 10); //25 - std::cout << "Octree faces: " << stressSpidersOctree.countFaces() << std::endl; - std::cout << "Leaf count: " << stressSpidersOctree.countLeaves() << std::endl; + KDTree kdtree; + kdtree.initialize(100.0, math::constants::Zero); + kdtree.analyze(&stressSpiders, 4); + + //std::cout << "Octree faces: " << stressSpidersOctree.countFaces() << std::endl; + //std::cout << "Leaf count: " << stressSpidersOctree.countLeaves() << std::endl; SpherePrimitive outdoorTopLightGeometry; outdoorTopLightGeometry.setRadius((math::real)10.0); @@ -139,7 +142,7 @@ void manta_demo::complexRoomDemo(int samplesPerPixel, int resolutionX, int resol // Create scene objects SceneObject *stressSpidersObject = scene.createSceneObject(); if (useOctree) { - stressSpidersObject->setGeometry(&stressSpidersOctree); + stressSpidersObject->setGeometry(&kdtree); } else { stressSpidersObject->setGeometry(&stressSpiders); @@ -179,6 +182,8 @@ void manta_demo::complexRoomDemo(int samplesPerPixel, int resolutionX, int resol GridSampler sampler; sampler.setGridWidth(3); + RandomSampler randomSampler; + if (regularCamera) { StandardCameraRayEmitterGroup *camera = new StandardCameraRayEmitterGroup; //camera->setSamplingWidth(3); @@ -190,7 +195,7 @@ void manta_demo::complexRoomDemo(int samplesPerPixel, int resolutionX, int resol camera->setResolutionX(resolutionX); camera->setResolutionY(resolutionY); camera->setSampleCount(samplesPerPixel); - camera->setSampler(&sampler); + camera->setSampler(&randomSampler); group = camera; } @@ -257,8 +262,9 @@ void manta_demo::complexRoomDemo(int samplesPerPixel, int resolutionX, int resol sceneBuffer.destroy(); rayTracer.destroy(); - stressSpidersOctree.destroy(); + //stressSpidersOctree.destroy(); stressSpiders.destroy(); + kdtree.destroy(); std::cout << "Standard allocator memory leaks: " << StandardAllocator::Global()->getLedger() << ", " << StandardAllocator::Global()->getCurrentUsage() << std::endl; } diff --git a/demos/demos.h b/demos/demos.h index 66316c77..8bd905ff 100644 --- a/demos/demos.h +++ b/demos/demos.h @@ -34,6 +34,7 @@ #include #include #include +#include namespace manta_demo { diff --git a/demos/main.cpp b/demos/main.cpp index 0dd135fd..6489fdbf 100644 --- a/demos/main.cpp +++ b/demos/main.cpp @@ -3,13 +3,13 @@ using namespace manta_demo; int main() { - //complexRoomDemo(1, 1024 * 2, 768 * 2); + //complexRoomDemo(1000, 1024 * 2, 768 * 2); //penDemo(500, 1024 * 2, 768 * 2); - //stressSpidersDemo(1, 1024 * 2, 768 * 2); // 871 samples + stressSpidersDemo(1, 1024 * 2, 768 * 2); // 871 samples //simpleRoomDemo(1, 1024*2, 768*2); //1024, 768 - //boxCityDemo(1, 1024*2, 768*2); + //boxCityDemo(100, 1024*2, 768*2); //teapotDemo(1, 500, 500); - teapotLampDemo(100, 1024*2, 768*2); // 10 for issue replication 1024, 768 + //teapotLampDemo(2000, 1024*2, 768*2); // 10 for issue replication 1024, 768 //cubeTestDemo(1, 1024, 768); //blocksDemo(40, 1024 * 2, 768 * 2); // 10 for issue replication 1024, 768 diff --git a/demos/pen_demo.cpp b/demos/pen_demo.cpp index e6dd4ce8..ee9e9ae5 100644 --- a/demos/pen_demo.cpp +++ b/demos/pen_demo.cpp @@ -79,6 +79,10 @@ void manta_demo::penDemo(int samplesPerPixel, int resolutionX, int resolutionY) penOctree.analyze(&pen, 25); penOctree.writeToObjFile("../../workspace/test_results/pen_octree.obj", nullptr); + KDTree kdtree; + kdtree.initialize(150, math::constants::Zero); + kdtree.analyze(&pen, 4); + math::real lightRadius = 10.0; SpherePrimitive light1Geometry; @@ -102,7 +106,7 @@ void manta_demo::penDemo(int samplesPerPixel, int resolutionX, int resolutionY) // Create scene objects SceneObject *penObject = scene.createSceneObject(); if (useOctree) { - penObject->setGeometry(&penOctree); + penObject->setGeometry(&kdtree); } else { penObject->setGeometry(&pen); @@ -237,6 +241,7 @@ void manta_demo::penDemo(int samplesPerPixel, int resolutionX, int resolutionY) pen.destroy(); penOctree.destroy(); + kdtree.destroy(); std::cout << "Standard allocator memory leaks: " << StandardAllocator::Global()->getLedger() << ", " << StandardAllocator::Global()->getCurrentUsage() << std::endl; } diff --git a/demos/stress_spiders.cpp b/demos/stress_spiders.cpp index 764d92fc..4cf92095 100644 --- a/demos/stress_spiders.cpp +++ b/demos/stress_spiders.cpp @@ -49,6 +49,10 @@ void manta_demo::stressSpidersDemo(int samplesPerPixel, int resolutionX, int res stressSpidersOctree.analyze(&stressSpiders, 25); //stressSpidersOctree.writeToObjFile("../../workspace/test_results/stress_spiders_octree.obj", nullptr); + KDTree kdtree; + kdtree.initialize(32.0, math::constants::Zero); + kdtree.analyze(&stressSpiders, 4); + SpherePrimitive outdoorTopLightGeometry; outdoorTopLightGeometry.setRadius((math::real)10.0); //outdoorTopLightGeometry.setRadius((math::real)20.0); @@ -63,7 +67,7 @@ void manta_demo::stressSpidersDemo(int samplesPerPixel, int resolutionX, int res // Create scene objects SceneObject *stressSpidersObject = scene.createSceneObject(); if (useOctree) { - stressSpidersObject->setGeometry(&stressSpidersOctree); + stressSpidersObject->setGeometry(&kdtree); } else { stressSpidersObject->setGeometry(&stressSpiders); @@ -90,7 +94,7 @@ void manta_demo::stressSpidersDemo(int samplesPerPixel, int resolutionX, int res up = math::normalize(up); // Create the camera - constexpr bool regularCamera = false; + constexpr bool regularCamera = true; CameraRayEmitterGroup *group; manta::SimpleLens lens; lens.initialize(); @@ -184,6 +188,7 @@ void manta_demo::stressSpidersDemo(int samplesPerPixel, int resolutionX, int res stressSpidersOctree.destroy(); stressSpiders.destroy(); + kdtree.destroy(); std::cout << "Standard allocator memory leaks: " << StandardAllocator::Global()->getLedger() << ", " << StandardAllocator::Global()->getCurrentUsage() << std::endl; } diff --git a/demos/teapot_lamp_demo.cpp b/demos/teapot_lamp_demo.cpp index 37a25261..b0263d02 100644 --- a/demos/teapot_lamp_demo.cpp +++ b/demos/teapot_lamp_demo.cpp @@ -76,6 +76,10 @@ void manta_demo::teapotLampDemo(int samplesPerPixel, int resolutionX, int resolu teapotOctree.analyze(&teapot, 25); //teapotOctree.writeToObjFile("../../workspace/test_results/teapot_lamp_octree.obj", nullptr); + KDTree kdtree; + kdtree.initialize(4.0, math::constants::Zero); + kdtree.analyze(&teapot, 4); + SpherePrimitive bulb; bulb.setRadius(0.25); bulb.setPosition(math::loadVector(0.10669, 3.42135, -2.47464)); @@ -108,7 +112,7 @@ void manta_demo::teapotLampDemo(int samplesPerPixel, int resolutionX, int resolu SceneObject *teapotObject = scene.createSceneObject(); if (useOctree) { - teapotObject->setGeometry(&teapotOctree); + teapotObject->setGeometry(&kdtree); } else { teapotObject->setGeometry(&teapot); @@ -164,7 +168,7 @@ void manta_demo::teapotLampDemo(int samplesPerPixel, int resolutionX, int resolu // Leaks //rayTracer.tracePixel(1281, 900, &scene, &camera); //rayTracer.tracePixel(1456, 1230, &scene, &camera); - //rayTracer.tracePixel(616, 1459, &scene, &camera); + //rayTracer.tracePixel(1160, 1017, &scene, &camera); rayTracer.traceAll(&scene, &camera); @@ -197,6 +201,7 @@ void manta_demo::teapotLampDemo(int samplesPerPixel, int resolutionX, int resolu teapot.destroy(); teapotObj.destroy(); teapotOctree.destroy(); + kdtree.destroy(); std::cout << "Standard allocator memory leaks: " << StandardAllocator::Global()->getLedger() << ", " << StandardAllocator::Global()->getCurrentUsage() << std::endl; } diff --git a/include/kd_tree.h b/include/kd_tree.h new file mode 100644 index 00000000..3052a711 --- /dev/null +++ b/include/kd_tree.h @@ -0,0 +1,173 @@ +#ifndef KD_TREE_H +#define KD_TREE_H + +#include +#include +#include +#include +#include + +#include +#include + +#define OPTIMIZED_KD_TREE_NODE (true) + +namespace manta { + + class Mesh; + class SceneObject; + class StackAllocator; + +#if OPTIMIZED_KD_TREE_NODE + struct KDTreeNode { + union { + math::real split; + int singleObject; + int objectOffset; + }; + union { + int flags; + int primitiveCount; + int aboveChild; + }; + + void initLeaf(int primitiveCount, int object) { + flags = 0x3; + this->primitiveCount |= (primitiveCount << 2); + objectOffset = object; + } + + void initInterior(int axis, int aboveChild, math::real s) { + split = s; + flags = axis; + this->aboveChild |= (aboveChild << 2); + } + + inline math::real getSplit() const { return split; } + inline int getPrimitiveCount() const { return primitiveCount >> 2; } + inline int getObjectOffset() const { return objectOffset; } + inline int getSplitAxis() const { return (flags & 0x3); } + inline bool isLeaf() const { return (flags & 0x3) == 0x3; } + inline int getAboveChild() const { return aboveChild >> 2; } + }; +#else + struct KDTreeNode { + math::real split; + int singleObject; + int objectOffset; + + int flags; + int primitiveCount; + int aboveChild; + + void initLeaf(int primitiveCount, int object) { + flags = 0x3; + this->primitiveCount = primitiveCount; + objectOffset = object; + } + + void initInterior(int axis, int aboveChild, math::real s) { + split = s; + flags = axis; + this->aboveChild = aboveChild; + } + + inline math::real getSplit() const { return split; } + inline int getPrimitiveCount() const { return primitiveCount; } + inline int getObjectOffset() const { return objectOffset; } + inline int getSplitAxis() const { return flags; } + inline bool isLeaf() const { return flags == 0x3; } + inline int getAboveChild() const { return aboveChild; } + }; +#endif + + struct KDBoundingVolume { + AABB bounds; + int faceListOffset; + int padding[3]; + }; + + struct KDBoundEdge { + enum EdgeType { + Start, + End + }; + + KDBoundEdge() { + + } + + KDBoundEdge(math::real t, int primitiveIndex, bool start) : t(t), primitiveIndex(primitiveIndex) { + edgeType = start ? EdgeType::Start : EdgeType::End; + } + + math::real t; + int primitiveIndex; + EdgeType edgeType; + }; + + struct KDTreeWorkspace { + // Accumulators + std::vector faces; + + // Constants + Mesh *mesh; + KDBoundEdge *edges[3]; + AABB *allFaceBounds; + int maxPrimitives; + }; + + struct KDJob { + const KDTreeNode *node; + math::real tmin, tmax; + }; + + class KDTree : public SceneGeometry { + public: + KDTree(); + ~KDTree(); + + void initialize(math::real width, const math::Vector &position); + void destroy(); + + virtual bool findClosestIntersection(const LightRay *ray, CoarseIntersection *intersection, math::real minDepth, math::real maxDepth, StackAllocator *s) const; + virtual math::Vector getClosestPoint(const CoarseIntersection *hint, const math::Vector &p) const; + virtual void getVicinity(const math::Vector &p, math::real radius, IntersectionList *list, SceneObject *object) const; + virtual void fineIntersection(const math::Vector &r, IntersectionPoint *p, const CoarseIntersection *hint) const; + virtual bool fastIntersection(const LightRay *ray) const; + + void analyze(Mesh *mesh, int maxSize); + + int createNode(); + int createNodeVolume(); + KDTreeNode *getNode(int index) { return &m_nodes[index]; } + + void initLeaf(int node, const std::vector &faces, KDTreeWorkspace *workspace); + + void writeToObjFile(const char *fname) const; + + protected: + void analyze(int currentNode, AABB *nodeBounds, const std::vector &faces, int badRefines, int depth, KDTreeWorkspace *workspace); + + Mesh *m_mesh; + + AABB m_bounds; + KDTreeNode *m_nodes; + int m_nodeCount; + int m_nodeCapacity; + + KDBoundingVolume *m_nodeVolumes; + int m_volumeCount; + int m_volumeCapacity; + + int *m_faceLists; + int m_faceCount; + + std::vector m_nodeBounds; + + math::real m_width; + }; + +} /* namespace manta */ + +#endif /* KD_TREE_H */ diff --git a/include/light_ray.h b/include/light_ray.h index 9c76a8f1..a60028f8 100644 --- a/include/light_ray.h +++ b/include/light_ray.h @@ -31,6 +31,7 @@ namespace manta { int getKZ() const { return m_kz; } const math::Vector3 &getShear() const { return m_shear; } math::Vector getPermutedDirection() const { return m_permutedDirection; } + math::Vector getInverseDirection() const { return m_inverseDirection; } protected: math::Vector m_direction; @@ -42,6 +43,7 @@ namespace manta { int m_kx, m_ky, m_kz; math::Vector3 m_shear; math::Vector m_permutedDirection; + math::Vector m_inverseDirection; }; } /* namespace manta */ diff --git a/include/manta_math.h b/include/manta_math.h index a5fe3107..e6575482 100644 --- a/include/manta_math.h +++ b/include/manta_math.h @@ -183,6 +183,12 @@ namespace manta { real getZ(const Vector &v); real getW(const Vector &v); + void setX(Vector &v, math::real value); + void setY(Vector &v, math::real value); + void setZ(Vector &v, math::real value); + void setW(Vector &v, math::real value); + void set(Vector &v, int index, math::real value); + inline real realNAN() { return std::numeric_limits::quiet_NaN(); } real get(const Vector &v, int index); diff --git a/include/primitives.h b/include/primitives.h new file mode 100644 index 00000000..0bfa68e2 --- /dev/null +++ b/include/primitives.h @@ -0,0 +1,90 @@ +#ifndef PRIMITIVES_H +#define PRIMITIVES_H + +#include + +#include + +namespace manta { + + class LightRay; + + struct AABB { + math::Vector minPoint; + math::Vector maxPoint; + + math::real surfaceArea(); + void merge(const AABB &b); + inline bool rayIntersect(const LightRay &ray, math::real *tmin_out, math::real *tmax_out) const { + math::real tmin = (math::real)0.0; + math::real tmax = math::constants::REAL_MAX; + + math::Vector rayDir = ray.getDirection(); + math::Vector rayOrigin = ray.getSource(); + math::Vector ood = ray.getInverseDirection(); + + math::Vector t1_v = math::mul(math::sub(minPoint, rayOrigin), ood); + math::Vector t2_v = math::mul(math::sub(maxPoint, rayOrigin), ood); + + math::Vector t1_v_temp = math::componentMin(t1_v, t2_v); + t2_v = math::componentMax(t1_v, t2_v); + + math::real rayDirX = math::getX(rayDir); + if (rayDirX < 1E-6 && rayDirX > -1E-6) { + if (math::getX(rayOrigin) < math::getX(minPoint) || math::getX(rayOrigin) > math::getX(maxPoint)) return false; + } + else { + math::real t1 = math::getX(t1_v_temp); + math::real t2 = math::getX(t2_v); + + tmin = (tmin > t1) ? tmin : t1; + tmax = t2; + + if (tmin > tmax) return false; + } + + math::real rayDirY = math::getY(rayDir); + if (rayDirY < 1E-6 && rayDirY > -1E-6) { + if (math::getY(rayOrigin) < math::getY(minPoint) || math::getY(rayOrigin) > math::getY(maxPoint)) return false; + } + else { + math::real t1 = math::getY(t1_v_temp); + math::real t2 = math::getY(t2_v); + + tmin = (tmin > t1) ? tmin : t1; + tmax = (tmax > t2) ? t2 : tmax; + + if (tmin > tmax) return false; + } + + math::real rayDirZ = math::getZ(rayDir); + if (rayDirZ < 1E-6 && rayDirZ > -1E-6) { + if (math::getZ(rayOrigin) < math::getZ(minPoint) || math::getZ(rayOrigin) > math::getZ(maxPoint)) return false; + } + else { + math::real t1 = math::getZ(t1_v_temp); + math::real t2 = math::getZ(t2_v); + + tmin = (tmin > t1) ? tmin : t1; + tmax = (tmax > t2) ? t2 : tmax; + + if (tmin > tmax) return false; + } + + *tmin_out = tmin; + *tmax_out = tmax; + return true; + } + }; + + struct Triangle { + math::Vector u; + math::Vector v; + math::Vector w; + }; + + void calculateAABB(const Triangle *triangle, AABB *target); + +} /* namespace manta */ + +#endif /* PRIMITIVES_H */ diff --git a/project/mantaray/mantaray.vcxproj b/project/mantaray/mantaray.vcxproj index b6b218d3..4895c13d 100644 --- a/project/mantaray/mantaray.vcxproj +++ b/project/mantaray/mantaray.vcxproj @@ -30,6 +30,7 @@ + @@ -37,6 +38,7 @@ + @@ -100,6 +102,7 @@ + @@ -108,6 +111,7 @@ + diff --git a/project/mantaray/mantaray.vcxproj.filters b/project/mantaray/mantaray.vcxproj.filters index 5f040de9..94fc6edb 100644 --- a/project/mantaray/mantaray.vcxproj.filters +++ b/project/mantaray/mantaray.vcxproj.filters @@ -253,6 +253,12 @@ {06b4d31e-b294-4cb9-87f4-a7e1c339eb0c} + + {c9dbeaef-19b1-44fb-80ca-c9cee3f0e6d0} + + + {0dae93d1-ec3c-4227-8f92-af96102ef0ae} + @@ -456,6 +462,12 @@ Source Files\emitter\sampler + + Source Files\spatial-partitioning + + + Source Files\primitives + @@ -704,6 +716,12 @@ Header Files\emitter\sampler + + Header Files\space-partitioning + + + Header Files\primitives + diff --git a/project/mantaray_test/mantaray_test.vcxproj b/project/mantaray_test/mantaray_test.vcxproj index 88cbe875..2159ffe3 100644 --- a/project/mantaray_test/mantaray_test.vcxproj +++ b/project/mantaray_test/mantaray_test.vcxproj @@ -51,6 +51,7 @@ + diff --git a/project/mantaray_test/mantaray_test.vcxproj.filters b/project/mantaray_test/mantaray_test.vcxproj.filters index 1bb80145..451d2d74 100644 --- a/project/mantaray_test/mantaray_test.vcxproj.filters +++ b/project/mantaray_test/mantaray_test.vcxproj.filters @@ -29,6 +29,9 @@ Tests + + Tests + diff --git a/src/kd_tree.cpp b/src/kd_tree.cpp new file mode 100644 index 00000000..4fb0ab18 --- /dev/null +++ b/src/kd_tree.cpp @@ -0,0 +1,425 @@ +#include + +#include +#include +#include + +#include + +manta::KDTree::KDTree() { + m_nodes = nullptr; + m_nodeCapacity = 0; + m_nodeCount = 0; + + m_volumeCount = 0; + m_volumeCapacity = 0; + + m_faceLists = nullptr; + m_faceCount = 0; +} + +manta::KDTree::~KDTree() { + assert(m_faceLists == nullptr); + assert(m_nodeVolumes == nullptr); + assert(m_nodes == nullptr); +} + +void manta::KDTree::initialize(math::real width, const math::Vector &position) { + m_width = width; + + m_bounds.maxPoint = math::loadScalar(width); + m_bounds.minPoint = math::loadScalar(-width); +} + +void manta::KDTree::destroy() { + if (m_faceLists != nullptr) { + StandardAllocator::Global()->free(m_faceLists, m_faceCount); + m_faceLists = nullptr; + m_faceCount = 0; + } + + if (m_nodes != nullptr) { + StandardAllocator::Global()->aligned_free(m_nodes, m_nodeCapacity); + m_nodes = nullptr; + m_nodeCapacity = 0; + m_nodeCount = 0; + } + + if (m_nodeVolumes != nullptr) { + StandardAllocator::Global()->aligned_free(m_nodeVolumes, m_volumeCapacity); + m_nodeVolumes = nullptr; + m_volumeCount = 0; + m_volumeCapacity = 0; + } +} + +bool manta::KDTree::findClosestIntersection(const LightRay *ray, CoarseIntersection *intersection, math::real minDepth, math::real maxDepth, StackAllocator *s) const { + math::real tmin, tmax; + if (!m_bounds.rayIntersect(*ray, &tmin, &tmax)) { + return false; + } + + constexpr int MAX_DEPTH = 64; + KDJob jobs[MAX_DEPTH]; + int currentJob = 0; + + bool hit = false; + math::real closestHit = std::min(tmax, maxDepth); + const KDTreeNode *node = &m_nodes[0]; + while (node != nullptr) { + //int nodeIndex = node - m_nodes; + + if (closestHit < tmin) break; + if (!node->isLeaf()) { + int axis = node->getSplitAxis(); + math::real o_axis = math::get(ray->getSource(), axis); + math::real tPlane = (node->getSplit() - o_axis) * math::get(ray->getInverseDirection(), axis); + + const KDTreeNode *firstChild, *secondChild; + bool belowFirst = (o_axis < node->getSplit()) || (o_axis == node->getSplit() && math::get(ray->getDirection(), axis) <= 0); + + if (belowFirst) { + firstChild = node + 1; + secondChild = &m_nodes[node->getAboveChild()]; + } + else { + firstChild = &m_nodes[node->getAboveChild()]; + secondChild = node + 1; + } + + if (tPlane > tmax || tPlane <= 0) { + node = firstChild; + } + else if (tPlane < tmin) { + node = secondChild; + } + else { + // Add second child to queue + jobs[currentJob].node = secondChild; + jobs[currentJob].tmin = tPlane; + jobs[currentJob].tmax = tmax; + ++currentJob; + + node = firstChild; + tmax = tPlane; + } + } + else { + int primitiveCount = node->getPrimitiveCount(); + int objectOffset = node->getObjectOffset(); + int *faceList; + if (primitiveCount > 0) { + const KDBoundingVolume &bv = m_nodeVolumes[objectOffset]; + faceList = &m_faceLists[bv.faceListOffset]; + + math::real t0, t1; + if (bv.bounds.rayIntersect(*ray, &t0, &t1)) { + if (m_mesh->findClosestIntersection(faceList, primitiveCount, ray, intersection, minDepth, closestHit, s)) { + hit = true; + closestHit = intersection->depth; + } + } + } + else { + + } + + if (currentJob > 0) { + --currentJob; + node = jobs[currentJob].node; + tmin = jobs[currentJob].tmin; + tmax = jobs[currentJob].tmax; + } + else break; + } + } + + return hit; +} + +manta::math::Vector manta::KDTree::getClosestPoint(const CoarseIntersection *hint, const math::Vector &p) const { + return math::Vector(); +} + +void manta::KDTree::getVicinity(const math::Vector &p, math::real radius, IntersectionList *list, SceneObject *object) const { + +} + +void manta::KDTree::fineIntersection(const math::Vector &r, IntersectionPoint *p, const CoarseIntersection *hint) const { + hint->sceneGeometry->fineIntersection(r, p, hint); +} + +bool manta::KDTree::fastIntersection(const LightRay *ray) const { + return true; +} + +void manta::KDTree::analyze(Mesh *mesh, int maxSize) { + constexpr math::real maxDepth = 64; + + m_mesh = mesh; + + std::vector faces; + int nFaces = mesh->getFaceCount(); + for (int i = 0; i < nFaces; i++) { + faces.push_back(i); + } + + KDTreeWorkspace workspace; + workspace.mesh = mesh; + workspace.maxPrimitives = maxSize; + + for (int i = 0; i < 3; i++) { + //workspace.edges[i] = new KDBoundEdge[mesh->getFaceCount() * 2]; + workspace.edges[i] = StandardAllocator::Global()->allocate(mesh->getFaceCount() * 2); + } + + // Generate bounds for all faces + //workspace.allFaceBounds = new AABB[nFaces]; + workspace.allFaceBounds = StandardAllocator::Global()->allocate(nFaces, 16); + for (int i = 0; i < nFaces; i++) { + Face *face = &mesh->getFaces()[i]; + + Triangle triangle; + triangle.u = *mesh->getVertex(face->u); + triangle.v = *mesh->getVertex(face->v); + triangle.w = *mesh->getVertex(face->w); + + calculateAABB(&triangle, &workspace.allFaceBounds[i]); + } + + AABB topNodeBounds; + topNodeBounds.minPoint = math::loadScalar(-m_width); + topNodeBounds.maxPoint = math::loadScalar(m_width); + + int badRefines = 0; + analyze(0, &topNodeBounds, faces, badRefines, maxDepth, &workspace); + + // Copy faces into a new array + int totalFaces = workspace.faces.size(); + //m_faceLists = new int[totalFaces]; + m_faceLists = StandardAllocator::Global()->allocate(totalFaces); + m_faceCount = totalFaces; + for (int i = 0; i < totalFaces; i++) { + m_faceLists[i] = workspace.faces[i]; + } + + // Destroy all allocated memory + for (int i = 0; i < 3; i++) { + StandardAllocator::Global()->free(workspace.edges[i], mesh->getFaceCount() * 2); + } + + StandardAllocator::Global()->aligned_free(workspace.allFaceBounds, nFaces); +} + +void manta::KDTree::analyze(int currentNode, AABB *nodeBounds, const std::vector &faces, int badRefines, int depth, KDTreeWorkspace *workspace) { + constexpr math::real intersectionCost = 80; + constexpr math::real traversalCost = 1; + constexpr math::real emptyBonus = 0.5; + + createNode(); + + // Debug only + m_nodeBounds.push_back(*nodeBounds); + + int primitiveCount = (int)faces.size(); + if (primitiveCount <= workspace->maxPrimitives || depth == 0) { + initLeaf(currentNode, faces, workspace); + return; + } + + int bestAxis = -1, bestOffset = -1; + math::real bestCost = math::constants::REAL_MAX; + math::real oldCost = intersectionCost * primitiveCount; + math::real totalSA = nodeBounds->surfaceArea(); + math::real invTotalSA = 1 / totalSA; + math::Vector d = math::sub(nodeBounds->maxPoint, nodeBounds->minPoint); + + int retries = 0; + int axis = math::maxDimension3(d); + + retry_split: + // Initialize edges + for (int i = 0; i < primitiveCount; i++) { + int primitiveIndex = faces[i]; + const AABB &bounds = workspace->allFaceBounds[primitiveIndex]; + workspace->edges[axis][2 * i] = KDBoundEdge(math::get(bounds.minPoint, axis), primitiveIndex, true); // Start + workspace->edges[axis][2 * i + 1] = KDBoundEdge(math::get(bounds.maxPoint, axis), primitiveIndex, false); // End + } + + std::sort(&workspace->edges[axis][0], &workspace->edges[axis][2 * primitiveCount], + [](const KDBoundEdge &e0, const KDBoundEdge &e1) -> bool { + if (e0.t == e1.t) return (int)e0.edgeType < (int)e1.edgeType; + else return e0.t < e1.t; + }); + + int nBelow = 0, nAbove = primitiveCount; + for (int i = 0; i < 2 * primitiveCount; i++) { + if (workspace->edges[axis][i].edgeType == KDBoundEdge::EdgeType::End) --nAbove; + math::real edgeT = workspace->edges[axis][i].t; + if (edgeT > math::get(nodeBounds->minPoint, axis) && edgeT < math::get(nodeBounds->maxPoint, axis)) { + // Compute split cost + int otherAxis0 = (axis + 1) % 3, otherAxis1 = (axis + 2) % 3; + math::real belowSA = 2 * (math::get(d, otherAxis0) * math::get(d, otherAxis1) + + (edgeT - math::get(nodeBounds->minPoint, axis)) * + (math::get(d, otherAxis0) + math::get(d, otherAxis1))); + math::real aboveSA = 2 * (math::get(d, otherAxis0) * math::get(d, otherAxis1) + + (math::get(nodeBounds->maxPoint, axis) - edgeT) * + (math::get(d, otherAxis0) + math::get(d, otherAxis1))); + + math::real pBelow = belowSA * invTotalSA; + math::real pAbove = aboveSA * invTotalSA; + math::real eb = (nAbove == 0 || nBelow == 0) ? emptyBonus : 0; + math::real cost = traversalCost + intersectionCost * (1 - eb) * (pBelow * nBelow + pAbove * nAbove); + + if (cost < bestCost) { + bestCost = cost; + bestAxis = axis; + bestOffset = i; + } + } + if (workspace->edges[axis][i].edgeType == KDBoundEdge::EdgeType::Start) ++nBelow; + } + + if (bestAxis == -1 && retries < 2) { + ++retries; + axis = (axis + 1) % 3; + goto retry_split; + } + + if (bestCost > oldCost) ++badRefines; + if (((bestCost > 4 * oldCost && primitiveCount < workspace->maxPrimitives) || bestAxis == -1 || badRefines == 3)) { + initLeaf(currentNode, faces, workspace); + return; + } + + // Classify primitives + std::vector primitives0; + std::vector primitives1; + for (int i = 0; i < bestOffset; ++i) { + if (workspace->edges[axis][i].edgeType == KDBoundEdge::EdgeType::Start) { + primitives0.push_back(workspace->edges[axis][i].primitiveIndex); + } + } + for (int i = bestOffset + 1; i < 2 * primitiveCount; ++i) { + if (workspace->edges[axis][i].edgeType == KDBoundEdge::EdgeType::End) { + primitives1.push_back(workspace->edges[axis][i].primitiveIndex); + } + } + + // Split + math::real split = workspace->edges[axis][bestOffset].t; + AABB bounds0 = *nodeBounds, bounds1 = *nodeBounds; + math::set(bounds0.maxPoint, bestAxis, split); + math::set(bounds1.minPoint, bestAxis, split); + + analyze(currentNode + 1, &bounds0, primitives0, badRefines, depth - 1, workspace); + int aboveChild = m_nodeCount; + m_nodes[currentNode].initInterior(bestAxis, aboveChild, split); + analyze(aboveChild, &bounds1, primitives1, badRefines, depth - 1, workspace); +} + +int manta::KDTree::createNode() { + if (m_nodeCount == m_nodeCapacity) { + int newSize = 1; + if (m_nodeCapacity > 0) newSize = m_nodeCapacity * 2; + + KDTreeNode *newNodes = StandardAllocator::Global()->allocate(newSize, 16); + if (m_nodeCount > 0) { + // Copy old nodes + memcpy((void *)newNodes, (void *)m_nodes, sizeof(KDTreeNode) * m_nodeCount); + } + + if (m_nodeCapacity > 0) { + StandardAllocator::Global()->aligned_free(m_nodes, m_nodeCapacity); + } + + m_nodes = newNodes; + m_nodeCapacity = newSize; + } + + int newNode = ++m_nodeCount; + return newNode; +} + +int manta::KDTree::createNodeVolume() { + if (m_volumeCount == m_volumeCapacity) { + int newSize = 1; + if (m_volumeCapacity > 0) newSize = m_volumeCapacity * 2; + + KDBoundingVolume *newVolumes = StandardAllocator::Global()->allocate(newSize, 16); + if (m_nodeCount > 0) { + // Copy old nodes + memcpy((void *)newVolumes, (void *)m_nodeVolumes, sizeof(KDBoundingVolume) * m_volumeCount); + } + + if (m_volumeCapacity > 0) { + StandardAllocator::Global()->aligned_free(m_nodeVolumes, m_volumeCapacity); + } + + m_nodeVolumes = newVolumes; + m_volumeCapacity = newSize; + } + + int newVolume = m_volumeCount++; + return newVolume; +} + +void manta::KDTree::initLeaf(int node, const std::vector &faces, KDTreeWorkspace *workspace) { + int primitiveCount = faces.size(); + int newNodeVolume = createNodeVolume(); + m_nodes[node].initLeaf(primitiveCount, newNodeVolume); + + if (primitiveCount > 0) { + // Initialize the new volume + KDBoundingVolume &volume = m_nodeVolumes[newNodeVolume]; + volume.faceListOffset = workspace->faces.size(); + volume.bounds = workspace->allFaceBounds[faces[0]]; + + // Add all primitives to the list + for (int i = 0; i < primitiveCount; i++) { + workspace->faces.push_back(faces[i]); + } + + // Merge all bounds + for (int i = 1; i < primitiveCount; i++) { + volume.bounds.merge(workspace->allFaceBounds[faces[i]]); + } + } +} + +void manta::KDTree::writeToObjFile(const char *fname) const { + std::ofstream f(fname); + + math::real depth; + int nodeCount = m_nodeCount; + for (int i = 0; i < nodeCount; i++) { + const AABB &bounds = m_nodeBounds[i]; + KDTreeNode &node = m_nodes[i]; + + int faceCount = 0; + if (node.getSplitAxis() == 0x3) faceCount = node.getPrimitiveCount(); + + if (faceCount != 0) f << "o " << "LEAF_" << i << "_N" << faceCount << std::endl; + else f << "o " << "LEAF_" << i << "_N" << 0 << std::endl; + + math::Vector minPoint = bounds.minPoint; + math::Vector maxPoint = bounds.maxPoint; + + int vertexOffset = i * 8 + 1; + f << "v " << math::getX(minPoint) << " " << math::getY(minPoint) << " " << math::getZ(minPoint) << std::endl; + f << "v " << math::getX(maxPoint) << " " << math::getY(minPoint) << " " << math::getZ(minPoint) << std::endl; + f << "v " << math::getX(minPoint) << " " << math::getY(minPoint) << " " << math::getZ(maxPoint) << std::endl; + f << "v " << math::getX(maxPoint) << " " << math::getY(minPoint) << " " << math::getZ(maxPoint) << std::endl; + f << "v " << math::getX(minPoint) << " " << math::getY(maxPoint) << " " << math::getZ(minPoint) << std::endl; + f << "v " << math::getX(maxPoint) << " " << math::getY(maxPoint) << " " << math::getZ(minPoint) << std::endl; + f << "v " << math::getX(minPoint) << " " << math::getY(maxPoint) << " " << math::getZ(maxPoint) << std::endl; + f << "v " << math::getX(maxPoint) << " " << math::getY(maxPoint) << " " << math::getZ(maxPoint) << std::endl; + + f << "f " << vertexOffset << " " << vertexOffset + 1 << " " << vertexOffset + 3 << " " << vertexOffset + 2 << std::endl; + f << "f " << vertexOffset << " " << vertexOffset + 4 << " " << vertexOffset + 6 << " " << vertexOffset + 2 << std::endl; + f << "f " << vertexOffset << " " << vertexOffset + 4 << " " << vertexOffset + 5 << " " << vertexOffset + 1 << std::endl; + f << "f " << vertexOffset + 7 << " " << vertexOffset + 5 << " " << vertexOffset + 1 << " " << vertexOffset + 3 << std::endl; + f << "f " << vertexOffset + 7 << " " << vertexOffset + 6 << " " << vertexOffset + 2 << " " << vertexOffset + 3 << std::endl; + f << "f " << vertexOffset + 7 << " " << vertexOffset + 6 << " " << vertexOffset + 4 << " " << vertexOffset + 5 << std::endl; + } +} diff --git a/src/light_ray.cpp b/src/light_ray.cpp index 9554e70a..544e2f09 100644 --- a/src/light_ray.cpp +++ b/src/light_ray.cpp @@ -19,4 +19,6 @@ void manta::LightRay::calculateTransformations() { m_shear.x = -math::getX(m_permutedDirection) / dirZ; m_shear.y = -math::getY(m_permutedDirection) / dirZ; m_shear.z = (math::real)1.0 / dirZ; + + m_inverseDirection = math::div(math::constants::One, m_direction); } diff --git a/src/manta_math_float_simd.cpp b/src/manta_math_float_simd.cpp index 536161f8..33ea138b 100644 --- a/src/manta_math_float_simd.cpp +++ b/src/manta_math_float_simd.cpp @@ -115,6 +115,26 @@ float manta::math::getW(const Vector &v) { return v.m128_f32[3]; } +void math::setX(Vector &v, math::real value) { + v.m128_f32[0] = value; +} + +void math::setY(Vector &v, math::real value) { + v.m128_f32[1] = value; +} + +void math::setZ(Vector &v, math::real value) { + v.m128_f32[2] = value; +} + +void math::setW(Vector &v, math::real value) { + v.m128_f32[3] = value; +} + +void math::set(Vector &v, int index, math::real value) { + v.m128_f32[index] = value; +} + float manta::math::getQuatX(const Quaternion &v) { return v.m128_f32[1]; } diff --git a/src/primitives.cpp b/src/primitives.cpp new file mode 100644 index 00000000..4bcb20ff --- /dev/null +++ b/src/primitives.cpp @@ -0,0 +1,26 @@ +#include + +manta::math::real manta::AABB::surfaceArea() { + math::Vector d = math::sub(maxPoint, minPoint); + + return 2 * ( + math::getX(d) * math::getY(d) + + math::getX(d) * math::getZ(d) + + math::getY(d) * math::getZ(d)); +} + +void manta::calculateAABB(const Triangle *triangle, AABB *target) { + target->minPoint = triangle->u; + target->maxPoint = triangle->u; + + target->minPoint = math::componentMin(target->minPoint, triangle->v); + target->minPoint = math::componentMin(target->minPoint, triangle->w); + + target->maxPoint = math::componentMax(target->maxPoint, triangle->v); + target->maxPoint = math::componentMax(target->maxPoint, triangle->w); +} + +void manta::AABB::merge(const AABB &b) { + minPoint = math::componentMin(minPoint, b.minPoint); + maxPoint = math::componentMax(maxPoint, b.maxPoint); +} diff --git a/test/geometry/cube.obj b/test/geometry/cube.obj new file mode 100644 index 00000000..f4b6a6ef --- /dev/null +++ b/test/geometry/cube.obj @@ -0,0 +1,30 @@ +# Blender v2.79 (sub 0) OBJ File: '' +# www.blender.org +o Cube +v 1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -0.999999 +v 0.999999 1.000000 1.000001 +v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 1.0000 -0.0000 0.0000 +vn 0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn 0.0000 0.0000 -1.0000 +s off +f 2//1 4//1 1//1 +f 8//2 6//2 5//2 +f 5//3 2//3 1//3 +f 6//4 3//4 2//4 +f 3//5 8//5 4//5 +f 1//6 8//6 5//6 +f 2//1 3//1 4//1 +f 8//2 7//2 6//2 +f 5//3 6//3 2//3 +f 6//4 7//4 3//4 +f 3//5 7//5 8//5 +f 1//6 4//6 8//6 diff --git a/test/geometry/two_cubes.obj b/test/geometry/two_cubes.obj new file mode 100644 index 00000000..e5ba2a2e --- /dev/null +++ b/test/geometry/two_cubes.obj @@ -0,0 +1,58 @@ +# Blender v2.79 (sub 0) OBJ File: '' +# www.blender.org +o Cube.001 +v 1.000000 -1.000000 -3.000000 +v 1.000000 -1.000000 -1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 -1.000000 -3.000000 +v 1.000000 1.000000 -3.000000 +v 0.999999 1.000000 -0.999999 +v -1.000000 1.000000 -1.000000 +v -1.000000 1.000000 -3.000000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 1.0000 -0.0000 0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn 0.0000 0.0000 -1.0000 +s off +f 2//1 4//1 1//1 +f 8//2 6//2 5//2 +f 5//3 2//3 1//3 +f 6//4 3//4 2//4 +f 3//5 8//5 4//5 +f 1//6 8//6 5//6 +f 2//1 3//1 4//1 +f 8//2 7//2 6//2 +f 5//3 6//3 2//3 +f 6//4 7//4 3//4 +f 3//5 7//5 8//5 +f 1//6 4//6 8//6 +o Cube +v 1.000000 -1.000000 1.000000 +v 1.000000 -1.000000 3.000000 +v -1.000000 -1.000000 3.000000 +v -1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 0.999999 1.000000 3.000000 +v -1.000000 1.000000 3.000000 +v -1.000000 1.000000 1.000000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 1.0000 -0.0000 0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn 0.0000 0.0000 -1.0000 +s off +f 10//7 12//7 9//7 +f 16//8 14//8 13//8 +f 13//9 10//9 9//9 +f 14//10 11//10 10//10 +f 11//11 16//11 12//11 +f 9//12 16//12 13//12 +f 10//7 11//7 12//7 +f 16//8 15//8 14//8 +f 13//9 14//9 10//9 +f 14//10 15//10 11//10 +f 11//11 15//11 16//11 +f 9//12 12//12 16//12 diff --git a/test/kdtree_tests.cpp b/test/kdtree_tests.cpp new file mode 100644 index 00000000..d1f8391d --- /dev/null +++ b/test/kdtree_tests.cpp @@ -0,0 +1,207 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace manta; + +TEST(KDTreeTests, KDTreeSanityTest) { + ObjFileLoader singleTriangleObj; + bool result = singleTriangleObj.readObjFile("../../../test/geometry/single_triangle.obj"); + + Mesh mesh; + mesh.loadObjFileData(&singleTriangleObj); + mesh.setFastIntersectEnabled(false); + + KDTree kdTree; + kdTree.initialize(10, math::constants::Zero); + + LightRay ray; + ray.setDirection(math::loadVector(0.0, 0.0, -1.0)); + ray.setSource(math::loadVector(1.0, 1.0, 1.0)); + ray.calculateTransformations(); + + kdTree.analyze(&mesh, 16); + + singleTriangleObj.destroy(); + mesh.destroy(); + + EXPECT_EQ(kdTree.getNode(0)->getSplitAxis(), 0x3); + EXPECT_EQ(kdTree.getNode(0)->getObjectOffset(), 0x0); + EXPECT_EQ(kdTree.getNode(0)->getPrimitiveCount(), 0x1); + + kdTree.destroy(); +} + +TEST(KDTreeTests, KDTreeIntersectionTest) { + ObjFileLoader singleTriangleObj; + bool result = singleTriangleObj.readObjFile("../../../test/geometry/single_triangle.obj"); + + Mesh mesh; + mesh.loadObjFileData(&singleTriangleObj); + mesh.setFastIntersectEnabled(false); + + KDTree kdTree; + kdTree.initialize(10, math::constants::Zero); + + LightRay ray; + ray.setDirection(math::loadVector(0.0, 0.0, -1.0)); + ray.setSource(math::loadVector(0.5, 0.0, 1.0)); + ray.calculateTransformations(); + + kdTree.analyze(&mesh, 16); + kdTree.writeToObjFile("../../../workspace/test_results/kdtree_debug.obj"); + + CoarseIntersection c; + bool intersect = kdTree.findClosestIntersection(&ray, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + + singleTriangleObj.destroy(); + mesh.destroy(); + + EXPECT_TRUE(intersect); + EXPECT_FLOAT_EQ(c.depth, 1.0); + + kdTree.destroy(); +} + +TEST(KDTreeTests, KDTreeCubeTest) { + ObjFileLoader singleTriangleObj; + bool result = singleTriangleObj.readObjFile("../../../test/geometry/cube.obj"); + + Mesh mesh; + mesh.loadObjFileData(&singleTriangleObj); + mesh.setFastIntersectEnabled(false); + + KDTree kdTree; + kdTree.initialize(10, math::constants::Zero); + + LightRay ray; + ray.setDirection(math::loadVector(0.0, 0.0, -1.0)); + ray.setSource(math::loadVector(0.5, 0.0, 2.0)); + ray.calculateTransformations(); + + kdTree.analyze(&mesh, 16); + kdTree.writeToObjFile("../../../workspace/test_results/kdtree_debug.obj"); + + CoarseIntersection c; + bool intersect = kdTree.findClosestIntersection(&ray, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + + singleTriangleObj.destroy(); + mesh.destroy(); + + EXPECT_TRUE(intersect); + EXPECT_NEAR(c.depth, 1.0, 1E-5); + + kdTree.destroy(); +} + +TEST(KDTreeTests, KDTreeDoubleCubeTest) { + ObjFileLoader singleTriangleObj; + bool result = singleTriangleObj.readObjFile("../../../test/geometry/two_cubes.obj"); + + Mesh mesh; + mesh.loadObjFileData(&singleTriangleObj); + mesh.setFastIntersectEnabled(false); + + KDTree kdTree; + kdTree.initialize(10, math::constants::Zero); + + LightRay ray1; + ray1.setDirection(math::loadVector(0.0, -1.0, 0.0)); + ray1.setSource(math::loadVector(0.0, 2.0, 2.0)); + ray1.calculateTransformations(); + + LightRay ray2; + ray2.setDirection(math::loadVector(0.0, -1.0, 0.0)); + ray2.setSource(math::loadVector(0.0, 2.0, 0.0)); + ray2.calculateTransformations(); + + LightRay ray3; + ray3.setDirection(math::loadVector(0.0, -1.0, 0.0)); + ray3.setSource(math::loadVector(0.0, 2.0, -2.0)); + ray3.calculateTransformations(); + + kdTree.analyze(&mesh, 4); + kdTree.writeToObjFile("../../../workspace/test_results/kdtree_debug.obj"); + + CoarseIntersection c; + bool intersect = kdTree.findClosestIntersection(&ray1, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_TRUE(intersect); + EXPECT_NEAR(c.depth, 1.0, 1E-5); + + intersect = kdTree.findClosestIntersection(&ray2, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_FALSE(intersect); + + intersect = kdTree.findClosestIntersection(&ray3, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_TRUE(intersect); + EXPECT_NEAR(c.depth, 1.0, 1E-5); + + singleTriangleObj.destroy(); + mesh.destroy(); + + kdTree.destroy(); +} + +TEST(KDTreeTests, KDTreeDoubleCubeSideTest) { + ObjFileLoader singleTriangleObj; + bool result = singleTriangleObj.readObjFile("../../../test/geometry/two_cubes.obj"); + + Mesh mesh; + mesh.loadObjFileData(&singleTriangleObj); + mesh.setFastIntersectEnabled(false); + + KDTree kdTree; + kdTree.initialize(10, math::constants::Zero); + + LightRay ray1; + ray1.setDirection(math::loadVector(1.0, 0.0, 0.0)); + ray1.setSource(math::loadVector(-2.0, 0.0, 2.0)); + ray1.calculateTransformations(); + + LightRay ray2; + ray2.setDirection(math::loadVector(1.0, 0.0, 0.0)); + ray2.setSource(math::loadVector(-2.0, 0.0, 0.0)); + ray2.calculateTransformations(); + + LightRay ray3; + ray3.setDirection(math::loadVector(1.0, 0.0, 0.0)); + ray3.setSource(math::loadVector(-2.0, 0.0, -2.0)); + ray3.calculateTransformations(); + + LightRay ray4; + ray4.setDirection(math::loadVector(0.0, 0.0, -1.0)); + ray4.setSource(math::loadVector(0.0, 0.0, 4.0)); + ray4.calculateTransformations(); + + kdTree.analyze(&mesh, 4); + kdTree.writeToObjFile("../../../workspace/test_results/kdtree_debug.obj"); + + CoarseIntersection c; + bool intersect = kdTree.findClosestIntersection(&ray1, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_TRUE(intersect); + EXPECT_NEAR(c.depth, 1.0, 1E-5); + + intersect = kdTree.findClosestIntersection(&ray2, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_FALSE(intersect); + + intersect = kdTree.findClosestIntersection(&ray3, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_TRUE(intersect); + EXPECT_NEAR(c.depth, 1.0, 1E-5); + + bool correctIntersect = mesh.findClosestIntersection(&ray4, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + intersect = kdTree.findClosestIntersection(&ray4, &c, (math::real)0.0, math::constants::REAL_MAX, nullptr); + EXPECT_TRUE(intersect); + EXPECT_NEAR(c.depth, 1.0, 1E-5); + + singleTriangleObj.destroy(); + mesh.destroy(); + + kdTree.destroy(); +} diff --git a/test/primitives.cpp b/test/primitives.cpp index 20383bda..fec871d4 100644 --- a/test/primitives.cpp +++ b/test/primitives.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -72,4 +73,74 @@ TEST(PrimitiveTests, SphereIntersectionInside) { //EXPECT_TRUE(list.getIntersectionCount() == 1); list.destroy(); -} \ No newline at end of file +} + +TEST(PrimitiveTests, AABBSanityCheck) { + LightRay ray; + ray.setDirection(math::loadVector(-1.0, 0.0, 0.0)); + ray.setSource(math::loadVector(0.0, 0.0, 0.0)); + ray.calculateTransformations(); + + AABB aabb; + aabb.maxPoint = math::loadVector(1.0, 1.0, 1.0); + aabb.minPoint = math::loadVector(-1.0, -1.0, -1.0); + + math::real tmin, tmax; + bool detected = aabb.rayIntersect(ray, &tmin, &tmax); + + EXPECT_EQ(tmin, 0.0); + EXPECT_EQ(tmax, 1.0); + EXPECT_TRUE(detected); +} + +TEST(PrimitiveTests, AABBCheck1) { + LightRay ray; + ray.setDirection(math::loadVector(-1.0, 0.0, 0.0)); + ray.setSource(math::loadVector(2.0, 0.0, 0.0)); + ray.calculateTransformations(); + + AABB aabb; + aabb.maxPoint = math::loadVector(1.0, 1.0, 1.0); + aabb.minPoint = math::loadVector(-1.0, -1.0, -1.0); + + math::real tmin, tmax; + bool detected = aabb.rayIntersect(ray, &tmin, &tmax); + + EXPECT_EQ(tmin, 1.0); + EXPECT_EQ(tmax, 3.0); + EXPECT_TRUE(detected); +} + +TEST(PrimitiveTests, AABBEdgeTest) { + LightRay ray; + ray.setDirection(math::loadVector(-1.0, 0.0, 0.0)); + ray.setSource(math::loadVector(2.0, 1.0, 1.0)); + ray.calculateTransformations(); + + AABB aabb; + aabb.maxPoint = math::loadVector(1.0, 1.0, 1.0); + aabb.minPoint = math::loadVector(-1.0, -1.0, -1.0); + + math::real tmin, tmax; + bool detected = aabb.rayIntersect(ray, &tmin, &tmax); + + EXPECT_EQ(tmin, 1.0); + EXPECT_EQ(tmax, 3.0); + EXPECT_TRUE(detected); +} + +TEST(PrimitiveTests, AABBNoHitTest) { + LightRay ray; + ray.setDirection(math::loadVector(-1.0, 0.0, 0.0)); + ray.setSource(math::loadVector(2.0, 1.1, 1.0)); + ray.calculateTransformations(); + + AABB aabb; + aabb.maxPoint = math::loadVector(1.0, 1.0, 1.0); + aabb.minPoint = math::loadVector(-1.0, -1.0, -1.0); + + math::real tmin, tmax; + bool detected = aabb.rayIntersect(ray, &tmin, &tmax); + + EXPECT_FALSE(detected); +} diff --git a/tracking/BuildVersion.txt b/tracking/BuildVersion.txt index eb119261..b3da4c84 100644 --- a/tracking/BuildVersion.txt +++ b/tracking/BuildVersion.txt @@ -1,7 +1,50 @@ MantaRay 2018 Build Information Ange Yaghi | 2018 | Every now and then, some rain must fall -BUILD VERSION: 2343 +BUILD VERSION: 2386 +Build 2019-01-27 19:52 14329 2386 +Build 2019-01-27 19:50 14322 2385 +Build 2019-01-27 19:50 14322 2384 +Build 2019-01-27 17:30 14265 2383 +Build 2019-01-27 17:29 14265 2382 +Build 2019-01-27 17:23 14265 2381 +Build 2019-01-27 17:16 14265 2380 +Build 2019-01-27 17:04 14261 2379 +Build 2019-01-27 16:58 14258 2378 +Build 2019-01-27 16:52 14258 2377 +Build 2019-01-27 16:52 14258 2376 +Build 2019-01-27 16:37 14258 2375 +Build 2019-01-27 15:53 14201 2374 +Build 2019-01-27 15:46 14196 2373 +Build 2019-01-27 15:39 14196 2372 +Build 2019-01-27 15:39 14196 2371 +Build 2019-01-27 15:23 14194 2370 +Build 2019-01-27 15:20 14163 2369 +Build 2019-01-27 15:09 14162 2368 +Build 2019-01-27 14:59 14117 2367 +Build 2019-01-27 14:50 14088 2366 +Build 2019-01-27 13:54 13932 2365 +Build 2019-01-27 13:52 13929 2364 +Build 2019-01-27 05:40 13846 2363 +Build 2019-01-27 05:40 13846 2362 +Build 2019-01-27 05:35 13846 2361 +Build 2019-01-27 05:23 13846 2360 +Build 2019-01-27 05:19 13846 2359 +Build 2019-01-27 05:17 13846 2358 +Build 2019-01-27 05:13 13846 2357 +Build 2019-01-27 04:15 13801 2356 +Build 2019-01-27 03:59 13799 2355 +Build 2019-01-27 03:51 13794 2354 +Build 2019-01-27 03:50 13794 2353 +Build 2019-01-27 03:40 13768 2352 +Build 2019-01-27 03:37 13768 2351 +Build 2019-01-27 03:33 13764 2350 +Build 2019-01-27 03:31 13764 2349 +Build 2019-01-27 03:30 13764 2348 +Build 2019-01-27 03:29 13764 2347 +Build 2019-01-27 03:28 13760 2346 +Build 2019-01-26 23:08 13370 2345 +Build 2019-01-26 22:06 13280 2344 Build 2019-01-26 21:40 13280 2343 Build 2019-01-26 21:38 13279 2342 Build 2019-01-26 21:34 13278 2341