bvh.cpp 5.07 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2

#include "../rays/bvh.h"
TheNumbat's avatar
TheNumbat committed
3
#include "debug.h"
TheNumbat's avatar
TheNumbat committed
4
5
6
7
#include <stack>

namespace PT {

TheNumbat's avatar
TheNumbat committed
8
9
template <typename Primitive>
void BVH<Primitive>::build(std::vector<Primitive> &&prims, size_t max_leaf_size) {
TheNumbat's avatar
TheNumbat committed
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

    // NOTE (PathTracer):
    // This BVH is parameterized on the type of the primitive it contains. This allows
    // us to build a BVH over any type that defines a certain interface. Specifically,
    // we use this to both build a BVH over triangles within each Tri_Mesh, and over
    // a variety of Objects (which might be Tri_Meshes, Spheres, etc.) in Pathtracer.
    //
    // The Primitive interface must implement these two functions:
    //      BBox bbox() const;
    //      Trace hit(const Ray& ray) const;
    // Hence, you may call bbox() and hit() on any value of type Primitive.
    //
    // Finally, also note that while a BVH is a tree structure, our BVH nodes don't
    // contain pointers to children, but rather indicies. This is because instead
    // of allocating each node individually, the BVH class contains a vector that
    // holds all of the nodes. Hence, to get the child of a node, you have to
    // look up the child index in this vector (e.g. nodes[node.l]). Similarly,
    // to create a new node, don't allocate one yourself - use BVH::new_node, which
    // returns the index of a newly added node.

    nodes.clear();
    primitives = std::move(prims);

    // TODO (PathTracer): Task 3
    // Construct a BVH from the given vector of primitives and maximum leaf
    // size configuration. The starter code builds a BVH with a
    // single leaf node (which is also the root) that encloses all the
    // primitives.

    BBox box;
TheNumbat's avatar
TheNumbat committed
40
41
    for (const Primitive &prim : primitives)
        box.enclose(prim.bbox());
TheNumbat's avatar
TheNumbat committed
42
43
44
    new_node(box, 0, primitives.size(), 0, 0);
}

TheNumbat's avatar
TheNumbat committed
45
template <typename Primitive> Trace BVH<Primitive>::hit(const Ray &ray) const {
TheNumbat's avatar
TheNumbat committed
46
47
48
49
50
51
52
53
54
55

    // TODO (PathTracer): Task 3
    // Implement ray - BVH intersection test. A ray intersects
    // with a BVH aggregate if and only if it intersects a primitive in
    // the BVH that is not an aggregate.

    // The starter code simply iterates through all the primitives.
    // Again, remember you can use hit() on any Primitive value.

    Trace ret;
TheNumbat's avatar
TheNumbat committed
56
    for (const Primitive &prim : primitives) {
TheNumbat's avatar
TheNumbat committed
57
58
59
60
61
62
        Trace hit = prim.hit(ray);
        ret = Trace::min(ret, hit);
    }
    return ret;
}

TheNumbat's avatar
TheNumbat committed
63
64
template <typename Primitive>
BVH<Primitive>::BVH(std::vector<Primitive> &&prims, size_t max_leaf_size) {
TheNumbat's avatar
TheNumbat committed
65
66
67
    build(std::move(prims), max_leaf_size);
}

TheNumbat's avatar
TheNumbat committed
68
template <typename Primitive> bool BVH<Primitive>::Node::is_leaf() const {
TheNumbat's avatar
TheNumbat committed
69
70
71
    return l == 0 && r == 0;
}

TheNumbat's avatar
TheNumbat committed
72
template <typename Primitive>
TheNumbat's avatar
TheNumbat committed
73
74
75
76
77
78
79
80
81
82
83
size_t BVH<Primitive>::new_node(BBox box, size_t start, size_t size, size_t l, size_t r) {
    Node n;
    n.bbox = box;
    n.start = start;
    n.size = size;
    n.l = l;
    n.r = r;
    nodes.push_back(n);
    return nodes.size() - 1;
}

TheNumbat's avatar
TheNumbat committed
84
template <typename Primitive> BBox BVH<Primitive>::bbox() const { return nodes[0].bbox; }
TheNumbat's avatar
TheNumbat committed
85

TheNumbat's avatar
TheNumbat committed
86
template <typename Primitive> std::vector<Primitive> BVH<Primitive>::destructure() {
TheNumbat's avatar
TheNumbat committed
87
88
89
90
    nodes.clear();
    return std::move(primitives);
}

TheNumbat's avatar
TheNumbat committed
91
template <typename Primitive> void BVH<Primitive>::clear() {
TheNumbat's avatar
TheNumbat committed
92
93
94
95
    nodes.clear();
    primitives.clear();
}

TheNumbat's avatar
TheNumbat committed
96
97
98
template <typename Primitive>
size_t BVH<Primitive>::visualize(GL::Lines &lines, GL::Lines &active, size_t level,
                                 const Mat4 &trans) const {
TheNumbat's avatar
TheNumbat committed
99

TheNumbat's avatar
TheNumbat committed
100
101
    std::stack<std::pair<size_t, size_t>> tstack;
    tstack.push({0, 0});
TheNumbat's avatar
TheNumbat committed
102
103
    size_t max_level = 0;

TheNumbat's avatar
TheNumbat committed
104
105
    if (nodes.empty())
        return max_level;
TheNumbat's avatar
TheNumbat committed
106
107
108
109
110

    while (!tstack.empty()) {

        auto [idx, lvl] = tstack.top();
        max_level = std::max(max_level, lvl);
TheNumbat's avatar
TheNumbat committed
111
        const Node &node = nodes[idx];
TheNumbat's avatar
TheNumbat committed
112
113
114
        tstack.pop();

        Vec3 color = lvl == level ? Vec3(1.0f, 0.0f, 0.0f) : Vec3(1.0f);
TheNumbat's avatar
TheNumbat committed
115
        GL::Lines &add = lvl == level ? active : lines;
TheNumbat's avatar
TheNumbat committed
116
117
118
119
120

        BBox box = node.bbox;
        box.transform(trans);
        Vec3 min = box.min, max = box.max;

TheNumbat's avatar
TheNumbat committed
121
        auto edge = [&](Vec3 a, Vec3 b) { add.add(a, b, color); };
TheNumbat's avatar
TheNumbat committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135

        edge(min, Vec3{max.x, min.y, min.z});
        edge(min, Vec3{min.x, max.y, min.z});
        edge(min, Vec3{min.x, min.y, max.z});
        edge(max, Vec3{min.x, max.y, max.z});
        edge(max, Vec3{max.x, min.y, max.z});
        edge(max, Vec3{max.x, max.y, min.z});
        edge(Vec3{min.x, max.y, min.z}, Vec3{max.x, max.y, min.z});
        edge(Vec3{min.x, max.y, min.z}, Vec3{min.x, max.y, max.z});
        edge(Vec3{min.x, min.y, max.z}, Vec3{max.x, min.y, max.z});
        edge(Vec3{min.x, min.y, max.z}, Vec3{min.x, max.y, max.z});
        edge(Vec3{max.x, min.y, min.z}, Vec3{max.x, max.y, min.z});
        edge(Vec3{max.x, min.y, min.z}, Vec3{max.x, min.y, max.z});

TheNumbat's avatar
TheNumbat committed
136
137
138
139
        if (node.l)
            tstack.push({node.l, lvl + 1});
        if (node.r)
            tstack.push({node.r, lvl + 1});
TheNumbat's avatar
TheNumbat committed
140

TheNumbat's avatar
TheNumbat committed
141
142
        if (!node.l && !node.r) {
            for (size_t i = node.start; i < node.start + node.size; i++) {
TheNumbat's avatar
TheNumbat committed
143
144
145
146
147
148
149
150
                size_t c = primitives[i].visualize(lines, active, level - lvl, trans);
                max_level = std::max(c, max_level);
            }
        }
    }
    return max_level;
}

TheNumbat's avatar
TheNumbat committed
151
} // namespace PT