diff --git a/hart/inferno-hart-cpu/src/kdtree.cpp b/hart/inferno-hart-cpu/src/kdtree.cpp new file mode 100644 index 0000000..b82aad5 --- /dev/null +++ b/hart/inferno-hart-cpu/src/kdtree.cpp @@ -0,0 +1,2 @@ +#include + diff --git a/hart/inferno-hart-cpu/src/kdtree.hpp b/hart/inferno-hart-cpu/src/kdtree.hpp new file mode 100644 index 0000000..8ea5d25 --- /dev/null +++ b/hart/inferno-hart-cpu/src/kdtree.hpp @@ -0,0 +1,148 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace inferno; + +bool AABBIntersection(glm::vec3 min, glm::vec3 max, const Ray* r) +{ + float tmin = 0.0, tmax = INFINITY; + glm::vec3 invDir = 1.0f / r->Direction; + + for (int i = 0; i < 3; ++i) { + float t1 = (min[i] - r->Origin[i]) * invDir[i]; + float t2 = (max[i] - r->Origin[i]) * invDir[i]; + + tmin = std::max(tmin, std::min(t1, t2)); + tmax = std::min(tmax, std::max(t1, t2)); + } + + return tmin <= tmax; +} + +struct KDNode { + uint32_t TriIdx; + glm::vec3 MinBounds; + glm::vec3 MaxBounds; + KDNode* LeftChild; + KDNode* RightChild; + + KDNode(uint32_t triIdx, glm::vec3 minBounds, glm::vec3 maxBounds) + : TriIdx(triIdx), MinBounds(minBounds), MaxBounds(maxBounds), LeftChild(nullptr), RightChild(nullptr) {} + + ~KDNode() { + delete LeftChild; + delete RightChild; + } + + void setLeftChild(KDNode* child) { + LeftChild = child; + updateBounds(); + } + + void setRightChild(KDNode* child) { + RightChild = child; + updateBounds(); + } + + void updateBounds() { + if (LeftChild && RightChild) { + MinBounds = glm::min(LeftChild->MinBounds, RightChild->MinBounds); + MaxBounds = glm::max(LeftChild->MaxBounds, RightChild->MaxBounds); + } + } +}; + +class KDTree { + public: + KDTree(float* vertices, uint32_t* indices, std::vector& indicesToProcess, uint32_t startIdx, uint32_t endIdx, uint32_t depthLimit) + : mVertices(vertices), mIndices(indices), mDepthLimit(depthLimit), mRoot(nullptr) + { + if (indicesToProcess.size() == 0) { + return; + } + + mRoot = buildNode(indicesToProcess, startIdx, endIdx, 0); + } + + ~KDTree() { + delete mRoot; + } + + void intersect(const Ray* ray, std::vector& outIndices) { + intersect(mRoot, ray, outIndices); + } + + private: + KDNode* buildNode(std::vector& indicesToProcess, uint32_t startIdx, uint32_t endIdx, uint32_t depth) { + if (startIdx >= endIdx || depth >= mDepthLimit) { + return nullptr; + } + + if (endIdx - startIdx == 1) { + return new KDNode(indicesToProcess[startIdx], getVertexBounds(mIndices[indicesToProcess[startIdx] * 3]), getVertexBounds(mIndices[indicesToProcess[startIdx] * 3])); + } + + glm::vec3 minBounds(INFINITY), maxBounds(-INFINITY); + for (uint32_t i = startIdx; i < endIdx; ++i) { + const glm::vec3& v0 = getVertexBounds(mIndices[indicesToProcess[i] * 3]); + const glm::vec3& v1 = getVertexBounds(mIndices[indicesToProcess[i] * 3 + 1]); + const glm::vec3& v2 = getVertexBounds(mIndices[indicesToProcess[i] * 3 + 2]); + minBounds = glm::min(minBounds, glm::min(v0, glm::min(v1, v2))); + maxBounds = glm::max(maxBounds, glm::max(v0, glm::max(v1, v2))); + } + + uint32_t axis = depth % 3; + uint32_t median = partition(indicesToProcess, startIdx, endIdx, axis); + + KDNode* node = new KDNode(0, minBounds, maxBounds); + + std::vector leftIndices(indicesToProcess.begin() + startIdx, indicesToProcess.begin() + median); + std::vector rightIndices(indicesToProcess.begin() + median, indicesToProcess.begin() + endIdx); + + node->setLeftChild(buildNode(leftIndices, startIdx, median, depth + 1)); + node->setRightChild(buildNode(rightIndices, 0, endIdx - median, depth + 1)); + + return node; + } + + void intersect(const KDNode* node, const Ray* ray, std::vector& outIndices) { + if (!node) { + return; + } + + if (AABBIntersection(node->MinBounds, node->MaxBounds, ray)) { + if (node->LeftChild || node->RightChild) { + intersect(node->LeftChild, ray, outIndices); + intersect(node->RightChild, ray, outIndices); + } + else { + outIndices.push_back(node->TriIdx); + } + } + } + + glm::vec3 getVertexBounds(uint32_t index) { + return { mVertices[index * 3], mVertices[index * 3 + 1], mVertices[index * 3 + 2] }; + } + + uint32_t partition(std::vector& indicesToProcess, uint32_t startIdx, uint32_t endIdx, uint32_t axis) { + uint32_t medianIdx = (startIdx + endIdx) / 2; + glm::vec3 pivot = getVertexBounds(mIndices[indicesToProcess[medianIdx] * 3]); + std::nth_element(indicesToProcess.begin() + startIdx, indicesToProcess.begin() + medianIdx, indicesToProcess.begin() + endIdx, + [this, &pivot, axis](uint32_t a, uint32_t b) { return getVertexBounds(mIndices[a * 3])[axis] < getVertexBounds(mIndices[b * 3])[axis]; }); + return medianIdx; + } + private: + float* mVertices; + uint32_t* mIndices; + uint32_t mDepthLimit; + KDNode* mRoot; +}; diff --git a/hart/inferno-hart-cpu/src/main.cpp b/hart/inferno-hart-cpu/src/main.cpp index d00fff6..1bca9f6 100644 --- a/hart/inferno-hart-cpu/src/main.cpp +++ b/hart/inferno-hart-cpu/src/main.cpp @@ -4,9 +4,12 @@ #include #include +#include "kdtree.hpp" + #include #include #include +#include using namespace inferno; @@ -27,14 +30,25 @@ public: void submitTris(void* vert, void* norm, int vc, - void* indicies, + void* indices, int ic) override { std::lock_guard lock(_mData); mState = EModuleState::Build; - mVert = (float*)vert; mNorm = (float*)norm; mVc = vc; mIndicies = (uint32_t*)indicies; mIc = ic; - spdlog::info("[hartcpu] Recieved {} verticies ({}) and {} indicies ({})", vc / 3, vert, ic / 3, indicies); + mVert = (float*)vert; mNorm = (float*)norm; mVc = vc; mIndices = (uint32_t*)indices; mIc = ic; + spdlog::info("[hartcpu] Recieved {} verticies ({}) and {} indicies ({})", vc / 3, vert, ic / 3, indices); + + std::vector indicesToProcess(ic / 3); + for (uint32_t i = 0; i < ic / 3; ++i) + { + indicesToProcess[i] = i; + } + + mKdTree = new KDTree(mVert, mIndices, indicesToProcess, 0, indicesToProcess.size() - 1, 10); + + spdlog::info("[hartcpu] Accelerator ready.."); + mState = EModuleState::Idle; } @@ -91,21 +105,25 @@ public: float bestDist = INFINITY; float dist; - for (int i = 0; i < mIc; i += 3) + // Traverse the K-D tree to identify the set of triangles that may intersect the ray. + std::vector candidateIndices; + mKdTree->intersect(ray, candidateIndices); + + for (uint32_t idx : candidateIndices) { - uint32_t ind1 = mIndicies[i]; - uint32_t ind2 = mIndicies[i + 1]; - uint32_t ind3 = mIndicies[i + 2]; - - const glm::vec3 a = { mVert[ind1 * 3], mVert[ind1 * 3 + 1], mVert[ind1 * 3 + 2] }; - const glm::vec3 b = { mVert[ind2 * 3], mVert[ind2 * 3 + 1], mVert[ind2 * 3 + 2] }; - const glm::vec3 c = { mVert[ind3 * 3], mVert[ind3 * 3 + 1], mVert[ind3 * 3 + 2] }; + uint32_t ind1 = mIndices[idx * 3]; + uint32_t ind2 = mIndices[idx * 3 + 1]; + uint32_t ind3 = mIndices[idx * 3 + 2]; + const glm::vec3 a = { mVert[ind1], mVert[ind1 + 1], mVert[ind1 + 2] }; + const glm::vec3 b = { mVert[ind2], mVert[ind2 + 1], mVert[ind2 + 2] }; + const glm::vec3 c = { mVert[ind3], mVert[ind3 + 1], mVert[ind3 + 2] }; + // Perform intersection test... if (!glm::intersectRayTriangle(ray->Origin, ray->Direction, a, b, c, coords, dist)) { continue; } if (dist > bestDist || dist < 0.0f) { continue; } - bestIdx = i; + bestIdx = idx; bestDist = dist; bestTexcoord = coords; } @@ -139,10 +157,12 @@ private: private: // Scene Data + KDTree* mKdTree; + float* mVert; float* mNorm; int mVc; - uint32_t* mIndicies; + uint32_t* mIndices; int mIc; }; diff --git a/libhart/hart_graphics.hpp b/libhart/hart_graphics.hpp index a14c79d..07a4ddc 100644 --- a/libhart/hart_graphics.hpp +++ b/libhart/hart_graphics.hpp @@ -14,3 +14,5 @@ extern "C" // glm #include #include +#include +