BVH now has a source file.

main
Brett 2022-11-16 16:15:08 -05:00
parent 2ea273a4a3
commit e72fc15007
15 changed files with 250 additions and 229 deletions

View File

@ -50,3 +50,15 @@
1293 1371 1668628542127579215 Step_3 9d5d6ad8db7a0552
1 1142 1668628564072244382 CMakeFiles/Step_3.dir/src/engine/math/bvh.cpp.o 7d05e3e63b85d471
1142 1242 1668628564172247411 Step_3 9d5d6ad8db7a0552
1 1337 1668632607895588360 CMakeFiles/Step_3.dir/src/engine/math/bvh.cpp.o 7d05e3e63b85d471
1337 1457 1668632608015591790 Step_3 9d5d6ad8db7a0552
2 1291 1668633243861949710 CMakeFiles/Step_3.dir/src/engine/globals.cpp.o 4ef77d2224f86511
3 1424 1668633243993953547 CMakeFiles/Step_3.dir/src/engine/math/colliders.cpp.o 39e9f435096d066b
3 1577 1668633244145957965 CMakeFiles/Step_3.dir/src/graphics/gl/gl.cpp.o 330ad35a6abf06c3
2 1588 1668633244157958315 CMakeFiles/Step_3.dir/src/engine/math/bvh.cpp.o 7d05e3e63b85d471
3 1697 1668633244265961453 CMakeFiles/Step_3.dir/src/engine/util/models.cpp.o 164394d360c43072
2 1895 1668633269338690359 CMakeFiles/Step_3.dir/src/engine/world.cpp.o 6470df278966c4
1 2359 1668633269802703851 CMakeFiles/Step_3.dir/src/engine/main.cpp.o 641dce3f86933e2e
1 2386 1668633269830704665 CMakeFiles/Step_3.dir/src/engine/raytracing.cpp.o 11f0e227e9fda9ca
1 1382 1668633279558987547 CMakeFiles/Step_3.dir/src/graphics/graphics.cpp.o ce988de97a5cb51d
1382 1513 1668633279690991384 Step_3 9d5d6ad8db7a0552

Binary file not shown.

View File

@ -1,3 +1,3 @@
Start testing: Nov 16 14:56 EST
Start testing: Nov 16 16:14 EST
----------------------------------------------------------
End testing: Nov 16 14:56 EST
End testing: Nov 16 16:14 EST

View File

@ -19,9 +19,6 @@
#include <utility>
// A currently pure header implementation of a BVH. TODO: make source file.
// this is also for testing and might not make it into the step 2.
namespace Raytracing {
#ifdef COMPILE_GUI
@ -41,17 +38,6 @@ namespace Raytracing {
};
struct BVHNode {
private:
static Raytracing::Mat4x4 getTransform(const AABB& _aabb) {
Raytracing::Mat4x4 transform{};
auto center = _aabb.getCenter();
transform.translate(center);
auto xRadius = _aabb.getXRadius(center) * 2;
auto yRadius = _aabb.getYRadius(center) * 2;
auto zRadius = _aabb.getZRadius(center) * 2;
transform.scale(float(xRadius), float(yRadius), float(zRadius));
return transform;
}
public:
struct BVHHitData {
BVHNode* ptr{};
@ -69,70 +55,6 @@ namespace Raytracing {
index = count++;
}
BVHHitData firstHitRayIntersectTraversal(const Ray& r, PRECISION_TYPE min, PRECISION_TYPE max);
#ifdef COMPILE_GUI
void draw(Shader& worldShader) {
worldShader.setVec3("color", {1.0, 1.0, 1.0});
aabbVAO->bind();
if (selected == index) {
if (selected == index && ImGui::BeginListBox("", ImVec2(250, 350))) {
std::stringstream strs;
strs << aabb;
ImGui::Text("%s", strs.str().c_str());
for (const auto& item: objs) {
auto pos = item.ptr->getPosition();
std::stringstream stm;
stm << item.aabb;
ImGui::Text("%s,\n\t%s", (std::to_string(pos.x()) + " " + std::to_string(pos.y()) + " " + std::to_string(pos.z())).c_str(),
stm.str().c_str());
}
ImGui::EndListBox();
}
for (const auto& obj: objs) {
auto transform = getTransform(obj.aabb);
worldShader.setMatrix("transform", transform);
aabbVAO->draw(worldShader);
}
auto transform = getTransform(aabb);
worldShader.setMatrix("transform", transform);
aabbVAO->draw(worldShader);
/*auto splitAABBs = aabb.splitByLongestAxis();
transform = getTransform(splitAABBs.second);
worldShader.setMatrix("transform", transform);
aabbVAO->draw(worldShader);
transform = getTransform(splitAABBs.first);
worldShader.setMatrix("transform", transform);
aabbVAO->draw(worldShader);*/
}
if (hit){
if (hit == 1)
worldShader.setVec3("color", {0.0, 0.0, 1.0});
else if (hit == 2)
worldShader.setVec3("color", {0.0, 1.0, 0.0});
else
worldShader.setVec3("color", {1.0, 0.5, 0.5});
auto transform = getTransform(aabb);
worldShader.setMatrix("transform", transform);
aabbVAO->draw(worldShader);
}
}
void gui() const {
int c1 = -1;
int c2 = -1;
if (left != nullptr)
c1 = left->index;
if (right != nullptr)
c2 = right->index;
std::string t;
if (c1 == -1 && c2 == -1)
t = " LEAF";
else
t = " L: " + std::to_string(c1) + " R: " + std::to_string(c2);
if (ImGui::Selectable(("S: " + std::to_string(objs.size()) + " I: " + std::to_string(index) + t).c_str(), selected == index))
selected = index;
}
#endif
~BVHNode() {
delete (left);
delete (right);
@ -145,77 +67,8 @@ namespace Raytracing {
// splits the objs in the vector based on the provided AABBs
static BVHPartitionedSpace partition(const std::pair<AABB, AABB>& aabbs, const std::vector<BVHObject>& objs);
static bool vectorEquals(const BVHPartitionedSpace& oldSpace, const BVHPartitionedSpace& newSpace){
if (oldSpace.left.size() != newSpace.left.size() || oldSpace.right.size() != newSpace.right.size())
return false;
for (int i = 0; i < oldSpace.left.size(); i++){
if (oldSpace.left[i].aabb != newSpace.left[i].aabb)
return false;
}
for (int i = 0; i < oldSpace.right.size(); i++){
if (oldSpace.right[i].aabb != newSpace.right[i].aabb)
return false;
}
return true;
}
BVHNode* addObjectsRecur(const std::vector<BVHObject>& objects, const BVHPartitionedSpace& prevSpace) {
// create a volume for the entire world.
// yes, we could use a recursion provided AABB, but that wouldn't be minimum, only half. this ensures that we have a minimum AABB.
AABB world;
for (const auto& obj: objects)
world = world.expand(obj.aabb);
// then split and partition the world
auto splitAABBs = world.splitByLongestAxis();
auto partitionedObjs = partition(splitAABBs, objects);
if (vectorEquals(prevSpace, partitionedObjs)){
splitAABBs = world.splitAlongAxis();
partitionedObjs = partition(splitAABBs, objects);
}
if ((objects.size() <= 1 && !objects.empty())) {
return new BVHNode(objects, world, nullptr, nullptr);
} else if (objects.empty()) // should never reach here!!
return nullptr;
BVHNode* left = nullptr;
BVHNode* right = nullptr;
// don't try to explore nodes which don't have anything in them.
if (!partitionedObjs.left.empty())
left = addObjectsRecur(partitionedObjs.left, partitionedObjs);
if (!partitionedObjs.right.empty())
right = addObjectsRecur(partitionedObjs.right, partitionedObjs);
if (left == nullptr && right == nullptr)
return new BVHNode(objects, world, left, right);
else
return new BVHNode({}, world, left, right);
}
#ifdef COMPILE_GUI
void drawNodesRecur(Shader& worldShader, BVHNode* node) {
node->draw(worldShader);
if (node->left != nullptr)
drawNodesRecur(worldShader, node->left);
if (node->right != nullptr)
drawNodesRecur(worldShader, node->right);
}
void guiNodesRecur(BVHNode* node) {
node->gui();
if (node->left != nullptr)
guiNodesRecur(node->left);
if (node->right != nullptr)
guiNodesRecur(node->right);
}
#endif
void reset(BVHNode* node){
if (node == nullptr)
return;
node->hit = false;
reset(node->left);
reset(node->right);
}
static bool vectorEquals(const BVHPartitionedSpace& oldSpace, const BVHPartitionedSpace& newSpace);
BVHNode* addObjectsRecur(const std::vector<BVHObject>& objects, const BVHPartitionedSpace& prevSpace);
public:
std::vector<Object*> noAABBObjects;
explicit BVHTree(const std::vector<Object*>& objectsInWorld) {
@ -227,62 +80,10 @@ namespace Raytracing {
#endif
}
void addObjects(const std::vector<Object*>& objects) {
if (root != nullptr)
throw std::runtime_error("BVHTree already exists. What are you trying to do?");
// move all the object's aabb's into world position
std::vector<BVHObject> objs;
for (auto* obj: objects) {
// we don't want to store all the AABBs which don't exist: ie spheres
if (obj->getAABB().isEmpty()) {
noAABBObjects.push_back(obj);
continue;
}
BVHObject bvhObject;
// returns a copy of the AABB object and assigns it in to the tree storage object
bvhObject.aabb = obj->getAABB().translate(obj->getPosition());
// which means we don't have to do memory management, since we are using the pointer without ownership or coping now.
bvhObject.ptr = obj;
objs.push_back(bvhObject);
}
root = addObjectsRecur(objs, {});
}
void addObjects(const std::vector<Object*>& objects);
std::vector<BVHObject> rayFirstHitIntersect(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max);
std::vector<BVHObject> rayAnyHitIntersect(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max);
void resetNodes(){
reset(root);
}
#ifdef COMPILE_GUI
// renders all the debug VAOs on screen.
void render(Shader& worldShader) {
ImGui::Begin(("BVH Data "), nullptr, ImGuiWindowFlags_NoCollapse);
worldShader.use();
worldShader.setInt("useWhite", 1);
worldShader.setVec3("color", {1.0, 1.0, 1.0});
{
ImGui::BeginChild("left pane", ImVec2(180, 0), true);
guiNodesRecur(root);
ImGui::EndChild();
}
ImGui::SameLine();
{
ImGui::BeginGroup();
ImGui::BeginChild("item view",
ImVec2(0, -ImGui::GetFrameHeightWithSpacing()),
true,
ImGuiWindowFlags_AlwaysAutoResize); // Leave room for 1 line below us
drawNodesRecur(worldShader, root);
ImGui::EndChild();
ImGui::EndGroup();
}
worldShader.setInt("useWhite", 0);
ImGui::End();
}
#endif
~BVHTree() {
delete (root);
}

View File

@ -76,6 +76,7 @@ namespace Raytracing {
AABBHitData intersects(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax);
AABBHitData simpleSlabRayAABBMethod(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax);
[[nodiscard]] Mat4x4 getTransform() const;
[[nodiscard]] inline bool isInside(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z) const {
return x >= min.x() && x <= max.x() && y >= min.y() && y <= max.y() && z >= min.z() && z <= max.z();

View File

@ -127,7 +127,8 @@ namespace Raytracing {
// this allows us to generate a statically unchanging BVH for easy rendering
void generateBVH();
#ifdef COMPILE_GUI
void drawBVH(Shader& worldShader) {bvhObjects->render(worldShader);}
// currently disabled. TODO: BVH renderer class
void drawBVH(Shader& worldShader) {}
#endif
inline void add(Object* object) { objects.push_back(object); }

View File

@ -61,6 +61,32 @@ namespace Raytracing {
* -------------------------------------------------------------------------
*/
/**
* Creates a BVH using the supplied vector of objects
* @param objects objects used to generate the BVH
*/
void BVHTree::addObjects(const std::vector<Object*>& objects) {
if (root != nullptr)
throw std::runtime_error("BVHTree already exists. What are you trying to do?");
// move all the object's aabb's into world position
std::vector<BVHObject> objs;
for (auto* obj: objects) {
// we don't want to store all the AABBs which don't exist: ie spheres
if (obj->getAABB().isEmpty()) {
noAABBObjects.push_back(obj);
continue;
}
BVHObject bvhObject;
// returns a copy of the AABB object and assigns it in to the tree storage object
bvhObject.aabb = obj->getAABB().translate(obj->getPosition());
// which means we don't have to do memory management, since we are using the pointer without ownership or coping now.
bvhObject.ptr = obj;
objs.push_back(bvhObject);
}
root = addObjectsRecur(objs, {});
}
/**
* Splits the objects into the two distinct spaces provided in the aabbs param based on their intersection
* This is left side based and produces two vectors which are unique.
@ -80,6 +106,7 @@ namespace Raytracing {
}
return space;
}
/**
* Returns the object array from the first bounding tree with objects in it hit by the provided ray.
* @firstHitRayIntersectTraversal for more information.
@ -94,16 +121,15 @@ namespace Raytracing {
return {};
}
/**
*
* @param ray
* @param min
* @param max
* @return
* Returns all the objects intersected by the provided ray. The returned objects are in no particular order.
* @param ray to use in AABB intersection
* @param min min t allowed for intersection search
* @param max max t allowed
* @return a unordered array of objects intersected by ray in this BVH.
*/
std::vector<BVHObject> BVHTree::rayAnyHitIntersect(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) {
std::queue<BVHNode*> nodes{};
std::vector<BVHObject> objects;
nodes.push(root);
while (!nodes.empty()) {
@ -124,7 +150,177 @@ namespace Raytracing {
}
nodes.pop();
}
return objects;
}
}
BVHNode* BVHTree::addObjectsRecur(const std::vector<BVHObject>& objects, const BVHPartitionedSpace& prevSpace) {
// create a volume for the entire world.
// yes, we could use a recursion provided AABB, but that wouldn't be minimum, only half. this ensures that we have a minimum AABB.
AABB world;
for (const auto& obj: objects)
world = world.expand(obj.aabb);
// then split and partition the world
auto splitAABBs = world.splitByLongestAxis();
auto partitionedObjs = partition(splitAABBs, objects);
if (vectorEquals(prevSpace, partitionedObjs)){
splitAABBs = world.splitAlongAxis();
partitionedObjs = partition(splitAABBs, objects);
}
if ((objects.size() <= 1 && !objects.empty())) {
return new BVHNode(objects, world, nullptr, nullptr);
} else if (objects.empty()) // should never reach here!!
return nullptr;
BVHNode* left = nullptr;
BVHNode* right = nullptr;
// don't try to explore nodes which don't have anything in them.
if (!partitionedObjs.left.empty())
left = addObjectsRecur(partitionedObjs.left, partitionedObjs);
if (!partitionedObjs.right.empty())
right = addObjectsRecur(partitionedObjs.right, partitionedObjs);
if (left == nullptr && right == nullptr)
return new BVHNode(objects, world, left, right);
else
return new BVHNode({}, world, left, right);
}
bool BVHTree::vectorEquals(const BVHPartitionedSpace& oldSpace, const BVHPartitionedSpace& newSpace) {
if (oldSpace.left.size() != newSpace.left.size() || oldSpace.right.size() != newSpace.right.size())
return false;
for (int i = 0; i < oldSpace.left.size(); i++){
if (oldSpace.left[i].aabb != newSpace.left[i].aabb)
return false;
}
for (int i = 0; i < oldSpace.right.size(); i++){
if (oldSpace.right[i].aabb != newSpace.right[i].aabb)
return false;
}
return true;
}
}
/**
* UNUSED CODE:
*/
// #ifdef COMPILE_GUI
// // renders all the debug VAOs on screen.
// void render(Shader& worldShader) {
// ImGui::Begin(("BVH Data "), nullptr, ImGuiWindowFlags_NoCollapse);
// worldShader.use();
// worldShader.setInt("useWhite", 1);
// worldShader.setVec3("color", {1.0, 1.0, 1.0});
// {
// ImGui::BeginChild("left pane", ImVec2(180, 0), true);
// guiNodesRecur(root);
// ImGui::EndChild();
// }
// ImGui::SameLine();
// {
// ImGui::BeginGroup();
// ImGui::BeginChild("item view",
// ImVec2(0, -ImGui::GetFrameHeightWithSpacing()),
// true,
// ImGuiWindowFlags_AlwaysAutoResize); // Leave room for 1 line below us
// drawNodesRecur(worldShader, root);
// ImGui::EndChild();
// ImGui::EndGroup();
// }
// worldShader.setInt("useWhite", 0);
// ImGui::End();
// }
// #endif
//#ifdef COMPILE_GUI
// void drawNodesRecur(Shader& worldShader, BVHNode* node) {
// node->draw(worldShader);
// if (node->left != nullptr)
// drawNodesRecur(worldShader, node->left);
// if (node->right != nullptr)
// drawNodesRecur(worldShader, node->right);
// }
// void guiNodesRecur(BVHNode* node) {
// node->gui();
// if (node->left != nullptr)
// guiNodesRecur(node->left);
// if (node->right != nullptr)
// guiNodesRecur(node->right);
// }
//#endif
// BVH NODE
// static Raytracing::Mat4x4 getTransform(const AABB& _aabb) {
// Raytracing::Mat4x4 transform{};
// auto center = _aabb.getCenter();
// transform.translate(center);
// auto xRadius = _aabb.getXRadius(center) * 2;
// auto yRadius = _aabb.getYRadius(center) * 2;
// auto zRadius = _aabb.getZRadius(center) * 2;
// transform.scale(float(xRadius), float(yRadius), float(zRadius));
// return transform;
// }
// #ifdef COMPILE_GUI
// void draw(Shader& worldShader) {
// worldShader.setVec3("color", {1.0, 1.0, 1.0});
// aabbVAO->bind();
// if (selected == index) {
// if (selected == index && ImGui::BeginListBox("", ImVec2(250, 350))) {
// std::stringstream strs;
// strs << aabb;
// ImGui::Text("%s", strs.str().c_str());
// for (const auto& item: objs) {
// auto pos = item.ptr->getPosition();
// std::stringstream stm;
// stm << item.aabb;
// ImGui::Text("%s,\n\t%s", (std::to_string(pos.x()) + " " + std::to_string(pos.y()) + " " + std::to_string(pos.z())).c_str(),
// stm.str().c_str());
// }
// ImGui::EndListBox();
// }
//
// for (const auto& obj: objs) {
// auto transform = getTransform(obj.aabb);
// worldShader.setMatrix("transform", transform);
// aabbVAO->draw(worldShader);
// }
// auto transform = getTransform(aabb);
// worldShader.setMatrix("transform", transform);
// aabbVAO->draw(worldShader);
//
// /*auto splitAABBs = aabb.splitByLongestAxis();
// transform = getTransform(splitAABBs.second);
// worldShader.setMatrix("transform", transform);
// aabbVAO->draw(worldShader);
// transform = getTransform(splitAABBs.first);
// worldShader.setMatrix("transform", transform);
// aabbVAO->draw(worldShader);*/
// }
// if (hit){
// if (hit == 1)
// worldShader.setVec3("color", {0.0, 0.0, 1.0});
// else if (hit == 2)
// worldShader.setVec3("color", {0.0, 1.0, 0.0});
// else
// worldShader.setVec3("color", {1.0, 0.5, 0.5});
// auto transform = getTransform(aabb);
// worldShader.setMatrix("transform", transform);
// aabbVAO->draw(worldShader);
// }
// }
// void gui() const {
// int c1 = -1;
// int c2 = -1;
// if (left != nullptr)
// c1 = left->index;
// if (right != nullptr)
// c2 = right->index;
// std::string t;
// if (c1 == -1 && c2 == -1)
// t = " LEAF";
// else
// t = " L: " + std::to_string(c1) + " R: " + std::to_string(c2);
// if (ImGui::Selectable(("S: " + std::to_string(objs.size()) + " I: " + std::to_string(index) + t).c_str(), selected == index))
// selected = index;
// }
// #endif

View File

@ -144,4 +144,14 @@ namespace Raytracing {
return {{min.x(), min.y(), min.z(), max.x(), max.y(), max.z()-Z2}, {min.x(), min.y(), min.z()+Z2, max.x(), max.y(), max.z()}};
}
}
Mat4x4 AABB::getTransform() const {
Raytracing::Mat4x4 transform{};
auto center = getCenter();
transform.translate(center);
auto xRadius = getXRadius(center) * 2;
auto yRadius = getYRadius(center) * 2;
auto zRadius = getZRadius(center) * 2;
transform.scale(float(xRadius), float(yRadius), float(zRadius));
return transform;
}
}

View File

@ -558,22 +558,22 @@ namespace Raytracing {
m_camera.setPosition(m_camera.getPosition() + Vec4{deltaX, deltaY, deltaZ});
}
if (Input::isKeyDown(GLFW_KEY_E) && Input::isState(GLFW_KEY_E)) {
auto ray = m_camera.projectRay((PRECISION_TYPE) m_window.displayWidth() / 2, (PRECISION_TYPE) m_window.displayHeight() / 2);
//auto results = m_world.checkIfHit(ray, 0, 1000).first;
auto bvh = m_world.getBVH()->rayAnyHitIntersect(ray, 0, 1000);
//if (results.hit)
// ilog << "World Results: " << results.hitPoint << " " << results.length << "\n";
//else
// ilog << "World not hit.\n";
if (!bvh.empty())
ilog << "BVH Results: " << bvh.size() << " " << bvh[0].ptr->getPosition() << "\n";
else
ilog << "BVH not hit.\n";
}
if (Input::isKeyDown(GLFW_KEY_R) && Input::isState(GLFW_KEY_R))
m_world.getBVH()->resetNodes();
// if (Input::isKeyDown(GLFW_KEY_E) && Input::isState(GLFW_KEY_E)) {
// auto ray = m_camera.projectRay((PRECISION_TYPE) m_window.displayWidth() / 2, (PRECISION_TYPE) m_window.displayHeight() / 2);
//
// //auto results = m_world.checkIfHit(ray, 0, 1000).first;
// auto bvh = m_world.getBVH()->rayAnyHitIntersect(ray, 0, 1000);
// //if (results.hit)
// // ilog << "World Results: " << results.hitPoint << " " << results.length << "\n";
// //else
// // ilog << "World not hit.\n";
// if (!bvh.empty())
// ilog << "BVH Results: " << bvh.size() << " " << bvh[0].ptr->getPosition() << "\n";
// else
// ilog << "BVH not hit.\n";
// }
// if (Input::isKeyDown(GLFW_KEY_R) && Input::isState(GLFW_KEY_R))
// m_world.getBVH()->resetNodes();
auto view = m_camera.view(yaw, pitch);
m_worldShader.setMatrix("projectMatrix", projection);
m_worldShader.setMatrix("viewMatrix", view);