a rewrite (maybe rust) is coming SOON
This commit is contained in:
5
hart/inferno-hart-cpu/src/kdtree.cpp
Normal file
5
hart/inferno-hart-cpu/src/kdtree.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "kdtree.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
@@ -1,203 +1,135 @@
|
||||
#include <hart_graphics.hpp>
|
||||
|
||||
#include <tracing/ray.hpp>
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
#include <hart_graphics.hpp>
|
||||
|
||||
#include <tracing/ray.hpp>
|
||||
#include <yolo/yolo.hpp>
|
||||
|
||||
using namespace inferno;
|
||||
|
||||
inline bool AABBIntersection(glm::vec3 min, glm::vec3 max, const Ray* r)
|
||||
struct Triangle
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
bool hit = (tmin <= tmax);
|
||||
//if (hit) {
|
||||
//std::cout << "Ray hits AABB: " << tmin << ", " << tmax << std::endl;
|
||||
//} else {
|
||||
//std::cout << "Ray misses AABB" << std::endl;
|
||||
//}
|
||||
return hit;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
unsigned int indices[3];
|
||||
};
|
||||
|
||||
class KDTree {
|
||||
public:
|
||||
KDTree(float* vertices, uint32_t* indices, std::vector<uint32_t>& indicesToProcess, uint32_t startIdx, uint32_t endIdx, uint32_t depthLimit)
|
||||
: mVertices(vertices), mIndices(indices), mDepthLimit(depthLimit), mRoot(nullptr)
|
||||
struct AABB
|
||||
{
|
||||
glm::vec3 min;
|
||||
glm::vec3 max;
|
||||
};
|
||||
|
||||
struct KDNode
|
||||
{
|
||||
AABB aabb;
|
||||
unsigned int splitAxis;
|
||||
std::vector<Triangle> triangles;
|
||||
KDNode *left;
|
||||
KDNode *right;
|
||||
};
|
||||
|
||||
inline AABB createAABB(const std::vector<glm::vec3>& vertices, const std::vector<Triangle>& triangles)
|
||||
{
|
||||
glm::vec3 min(std::numeric_limits<float>::max());
|
||||
glm::vec3 max(std::numeric_limits<float>::min());
|
||||
|
||||
for (const auto& triangle : triangles)
|
||||
{
|
||||
if (indicesToProcess.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRoot = buildNode(indicesToProcess, startIdx, endIdx, 0);
|
||||
}
|
||||
|
||||
~KDTree() {
|
||||
delete mRoot;
|
||||
}
|
||||
|
||||
void intersect(const Ray* ray, std::vector<uint32_t>& outIndices) {
|
||||
intersect(mRoot, ray, outIndices);
|
||||
}
|
||||
|
||||
KDNode* getRoot() const {
|
||||
return mRoot;
|
||||
}
|
||||
|
||||
void printTree(KDNode* node, int depth) const {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < depth; i++) {
|
||||
std::cout << "-";
|
||||
}
|
||||
std::cout << " " << glm::to_string(node->MinBounds) << " " << glm::to_string(node->MaxBounds) << ": " << node->TriIdx << "\n";
|
||||
|
||||
printTree(node->LeftChild, depth + 1);
|
||||
printTree(node->RightChild, depth + 1);
|
||||
}
|
||||
|
||||
private:
|
||||
KDNode* buildNode(std::vector<uint32_t>& 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);
|
||||
bool isPartitionValid = checkPartition(indicesToProcess, startIdx, endIdx, axis, median);
|
||||
if (!isPartitionValid) {
|
||||
std::cout << "Partition failed!" << std::endl;
|
||||
}
|
||||
|
||||
KDNode* node = new KDNode(0, minBounds, maxBounds);
|
||||
|
||||
std::vector<uint32_t> leftIndices(indicesToProcess.begin() + startIdx, indicesToProcess.begin() + median);
|
||||
std::vector<uint32_t> 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<uint32_t>& outIndices) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
//std::cout << "Checking node bounds: " << glm::to_string(node->MinBounds) << " " << glm::to_string(node->MaxBounds) << std::endl;
|
||||
|
||||
if (AABBIntersection(node->MinBounds, node->MaxBounds, ray)) {
|
||||
//std::cout << "Ray intersects node, num tris: " << (node->LeftChild || node->RightChild ? -1 : 1) << std::endl;
|
||||
if (node->LeftChild || node->RightChild) {
|
||||
intersect(node->LeftChild, ray, outIndices);
|
||||
intersect(node->RightChild, ray, outIndices);
|
||||
}
|
||||
else {
|
||||
//std::cout << "Ray hit leaf node with triangle index: " << node->TriIdx << std::endl;
|
||||
outIndices.push_back(node->TriIdx);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//std::cout << "Ray does not intersect node" << std::endl;
|
||||
}
|
||||
//std::cout << std::endl;
|
||||
//exit(0);
|
||||
}
|
||||
|
||||
glm::vec3 getVertexBounds(uint32_t index) const {
|
||||
return { mVertices[index * 3], mVertices[index * 3 + 1], mVertices[index * 3 + 2] };
|
||||
}
|
||||
|
||||
// TODO: this could definately be more advanced, at the moment is is a split down the middle
|
||||
uint32_t partition(std::vector<uint32_t>& 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;
|
||||
}
|
||||
|
||||
bool checkPartition(std::vector<uint32_t>& indicesToProcess, uint32_t startIdx, uint32_t endIdx, uint32_t axis, uint32_t median) {
|
||||
for (uint32_t i = startIdx; i < median; ++i) {
|
||||
if (getVertexBounds(mIndices[indicesToProcess[i] * 3])[axis] > getVertexBounds(mIndices[indicesToProcess[median] * 3])[axis]) {
|
||||
return false;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
min = glm::min(min, vertices[triangle.indices[i]]);
|
||||
max = glm::max(max, vertices[triangle.indices[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = median + 1; i < endIdx; ++i) {
|
||||
if (getVertexBounds(mIndices[indicesToProcess[i] * 3])[axis] < getVertexBounds(mIndices[indicesToProcess[median] * 3])[axis]) {
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
inline bool intersectAABB(const AABB& aabb, const glm::vec3& origin, const glm::vec3& direction, float& tNear, float& tFar)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
float invDirection = 1.0f / direction[i];
|
||||
float t0 = (aabb.min[i] - origin[i]) * invDirection;
|
||||
float t1 = (aabb.max[i] - origin[i]) * invDirection;
|
||||
|
||||
if (invDirection < 0.0f)
|
||||
{
|
||||
std::swap(t0, t1);
|
||||
}
|
||||
|
||||
tNear = t0 > tNear ? t0 : tNear;
|
||||
tFar = t1 < tFar ? t1 : tFar;
|
||||
|
||||
if (tNear > tFar)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline KDNode* buildKDTree(const std::vector<glm::vec3>& vertices, std::vector<Triangle>& triangles, unsigned int depth = 0)
|
||||
{
|
||||
if (triangles.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
float* mVertices;
|
||||
uint32_t* mIndices;
|
||||
uint32_t mDepthLimit;
|
||||
KDNode* mRoot;
|
||||
};
|
||||
unsigned int splitAxis = depth % 3;
|
||||
std::sort(triangles.begin(), triangles.end(), [&](const Triangle& a, const Triangle& b) {
|
||||
return vertices[a.indices[0]][splitAxis] < vertices[b.indices[0]][splitAxis];
|
||||
});
|
||||
|
||||
size_t midIdx = triangles.size() / 2;
|
||||
KDNode* node = new KDNode;
|
||||
node->splitAxis = splitAxis;
|
||||
node->aabb = createAABB(vertices, triangles);
|
||||
node->triangles.push_back(triangles[midIdx]);
|
||||
|
||||
std::vector<Triangle> leftTriangles(triangles.begin(), triangles.begin() + midIdx);
|
||||
std::vector<Triangle> rightTriangles(triangles.begin() + midIdx + 1, triangles.end());
|
||||
|
||||
node->left = buildKDTree(vertices, leftTriangles, depth + 1);
|
||||
node->right = buildKDTree(vertices, rightTriangles, depth + 1);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
inline void intersectKDTree(KDNode* node, const glm::vec3& origin, const glm::vec3& direction, std::vector<Triangle>& hitCandidates) {
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float tNear = -std::numeric_limits<float>::max();
|
||||
float tFar = std::numeric_limits<float>::max();
|
||||
if (!intersectAABB(node->aabb, origin, direction, tNear, tFar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node->left && !node->right)
|
||||
{
|
||||
hitCandidates.insert(hitCandidates.end(), node->triangles.begin(), node->triangles.end());
|
||||
return;
|
||||
}
|
||||
|
||||
intersectKDTree(node->left, origin, direction, hitCandidates);
|
||||
intersectKDTree(node->right, origin, direction, hitCandidates);
|
||||
}
|
||||
|
||||
inline void deleteKDTree(KDNode* node)
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
deleteKDTree(node->left);
|
||||
deleteKDTree(node->right);
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,17 +16,21 @@ using namespace inferno;
|
||||
|
||||
class HARTCPU : public HARTModule
|
||||
{
|
||||
public:
|
||||
public:
|
||||
HARTCPU()
|
||||
{
|
||||
mMasterWorker = std::thread(&HARTCPU::intersectMasterWorker, this);
|
||||
mLogModule = yolo::registerModule("hartcpu", "\u001b[35;1m");
|
||||
rootNode = nullptr;
|
||||
}
|
||||
|
||||
~HARTCPU()
|
||||
{
|
||||
this->stop(true);
|
||||
mMasterWorker.detach();
|
||||
if (rootNode) {
|
||||
deleteKDTree(rootNode);
|
||||
}
|
||||
}
|
||||
|
||||
void submitTris(void* vert,
|
||||
@@ -41,14 +45,19 @@ public:
|
||||
mVert = (float*)vert; mNorm = (float*)norm; mVc = vc; mIndices = (uint32_t*)indices; mIc = ic;
|
||||
yolo::info(mLogModule, "Recieved {} verticies ({}) and {} indicies ({})", vc / 3, vert, ic / 3, indices);
|
||||
|
||||
std::vector<uint32_t> indicesToProcess(ic / 3);
|
||||
for (uint32_t i = 0; i < ic / 3; ++i)
|
||||
{
|
||||
indicesToProcess[i] = i;
|
||||
std::vector<glm::vec3> vertices(mVc / 3);
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
vertices[i] = glm::vec3(mVert[i * 3], mVert[i * 3 + 1], mVert[i * 3 + 2]);
|
||||
}
|
||||
|
||||
mKdTree = new KDTree(mVert, mIndices, indicesToProcess, 0, indicesToProcess.size() - 1, 10);
|
||||
mKdTree->printTree(mKdTree->getRoot(), 1);
|
||||
std::vector<Triangle> triangles(mIc / 3);
|
||||
for (size_t i = 0; i < triangles.size(); ++i) {
|
||||
triangles[i].indices[0] = mIndices[i * 3];
|
||||
triangles[i].indices[1] = mIndices[i * 3 + 1];
|
||||
triangles[i].indices[2] = mIndices[i * 3 + 2];
|
||||
}
|
||||
|
||||
rootNode = buildKDTree(vertices, triangles);
|
||||
yolo::info(mLogModule, "Accelerator ready..");
|
||||
|
||||
mState = EModuleState::Idle;
|
||||
@@ -106,26 +115,27 @@ public:
|
||||
glm::vec2 bestTexcoord;
|
||||
float bestDist = INFINITY;
|
||||
float dist;
|
||||
// ...
|
||||
// (Keep the existing implementation of intersectMasterWorker, but replace the KDTree intersection part)
|
||||
|
||||
// Traverse the K-D tree to identify the set of triangles that may intersect the ray.
|
||||
std::vector<uint32_t> candidateIndices;
|
||||
mKdTree->intersect(ray, candidateIndices);
|
||||
std::vector<Triangle> candidateTriangles;
|
||||
intersectKDTree(rootNode, ray->Origin, ray->Direction, candidateTriangles);
|
||||
|
||||
for (uint32_t idx : candidateIndices)
|
||||
for (const Triangle& triangle : candidateTriangles)
|
||||
{
|
||||
uint32_t ind1 = mIndices[idx * 3];
|
||||
uint32_t ind2 = mIndices[idx * 3 + 1];
|
||||
uint32_t ind3 = mIndices[idx * 3 + 2];
|
||||
uint32_t ind1 = triangle.indices[0];
|
||||
uint32_t ind2 = triangle.indices[1];
|
||||
uint32_t ind3 = triangle.indices[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] };
|
||||
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] };
|
||||
|
||||
// Perform intersection test...
|
||||
if (!glm::intersectRayTriangle(ray->Origin, ray->Direction, a, b, c, coords, dist)) { continue; }
|
||||
if (dist > bestDist || dist < 0.0f) { continue; }
|
||||
|
||||
bestIdx = idx;
|
||||
//bestIdx = ;
|
||||
bestDist = dist;
|
||||
bestTexcoord = coords;
|
||||
}
|
||||
@@ -147,8 +157,7 @@ public:
|
||||
mToTrace.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Signaling Stuffs
|
||||
std::atomic<bool> mIsRunning;
|
||||
std::thread mMasterWorker;
|
||||
@@ -157,9 +166,8 @@ private:
|
||||
std::condition_variable _mSignalCv;
|
||||
std::condition_variable _mDoneCv;
|
||||
|
||||
private:
|
||||
// Scene Data
|
||||
KDTree* mKdTree;
|
||||
private:
|
||||
KDNode* rootNode;
|
||||
|
||||
float* mVert;
|
||||
float* mNorm;
|
||||
@@ -192,3 +200,4 @@ HART_INTERFACE void* _CREDIT()
|
||||
.VersionBuild = 1,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user