summaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/bvh.cc
blob: 350f09af4bada34a37517cde496195041abb4a74 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "bvh.h"

#include "mesh.h"
#include "ray.h"

#include <float.h>
#include <algorithm>

namespace mingfx {

    
BVH::BVH() : root_(NULL) {
}
    
BVH::~BVH() {
    FreeNodeRecursive(root_);
}
    
void BVH::CreateFromMesh(const Mesh &mesh) {
    FreeNodeRecursive(root_);
    
    std::vector<AABB> tri_boxes;
    for (int i=0; i<mesh.num_triangles(); i++) {
        AABB box = AABB(mesh, i);
        box.set_user_data(i);
        tri_boxes.push_back(box);
    }
    
    root_ = new Node();
    BuildHierarchyRecursive(root_, tri_boxes);
}

void BVH::CreateFromListOfBoxes(const std::vector<AABB> &boxes) {
    FreeNodeRecursive(root_);

    root_ = new Node();
    BuildHierarchyRecursive(root_, boxes);
}
    
    
void BVH::FreeNodeRecursive(Node* node) {
    if (node == NULL) return;
    FreeNodeRecursive(node->child1);
    FreeNodeRecursive(node->child2);
    delete node;
}
    
bool sort_by_x(const AABB &lhs, const AABB &rhs) {
    return (lhs.min()[0] + lhs.max()[0]) < (rhs.min()[0] + rhs.max()[0]);
}

bool sort_by_y(const AABB &lhs, const AABB &rhs) {
    return (lhs.min()[1] + lhs.max()[1]) < (rhs.min()[1] + rhs.max()[1]);
}

bool sort_by_z(const AABB &lhs, const AABB &rhs) {
    return (lhs.min()[2] + lhs.max()[2]) < (rhs.min()[2] + rhs.max()[2]);
}
    
void BVH::BuildHierarchyRecursive(Node *node, std::vector<AABB> tri_boxes) {
    // got down to a leaf, a single box
    if (tri_boxes.size() == 1) {
        node->box = tri_boxes[0];
        return;
    }
    
    // calc the full bounding box for this node
    for (int i=0; i<tri_boxes.size(); i++) {
        node->box = node->box + tri_boxes[i];
    }
    
    // sort boxes along the longest axis
    Vector3 dims = node->box.Dimensions();
    dims[0] = fabsf(dims[0]);
    dims[1] = fabsf(dims[1]);
    dims[2] = fabsf(dims[2]);

    if ((dims[0] > dims[1]) && (dims[0] > dims[2])) {
        std::sort(tri_boxes.begin(), tri_boxes.end(), sort_by_x);
    }
    else if ((dims[1] > dims[0]) && (dims[1] > dims[2])) {
        std::sort(tri_boxes.begin(), tri_boxes.end(), sort_by_y);
    }
    else {
        std::sort(tri_boxes.begin(), tri_boxes.end(), sort_by_z);
    }
    
    // assign half to child1 and half to child2
    std::size_t const half_size = tri_boxes.size() / 2;
    std::vector<AABB> left_boxes(tri_boxes.begin(), tri_boxes.begin() + half_size);
    std::vector<AABB> right_boxes(tri_boxes.begin() + half_size, tri_boxes.end());
    
    node->child1 = new Node();
    BuildHierarchyRecursive(node->child1, left_boxes);
    node->child2 = new Node();
    BuildHierarchyRecursive(node->child2, right_boxes);
}
    
std::vector<int> BVH::IntersectAndReturnUserData(const Ray &r) const {
    std::vector<int> data_list;
    IntersectRecursive(r, root_, &data_list);
    return data_list;
}

void BVH::IntersectRecursive(const Ray &r, Node *node, std::vector<int> *data_list) const {
    float t;
    if (r.IntersectAABB(node->box, &t)) {
        if ((node->child1 == NULL) && (node->child2 == NULL)) {
            // reached a leaf node, add the object's user data to the list
            data_list->push_back(node->box.user_data());
        }
        else {
            // go deeper and check children
            IntersectRecursive(r, node->child1, data_list);
            IntersectRecursive(r, node->child2, data_list);
        }
	}
}

} // end namespace