bbox.h 3.58 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2
3
4
5

#pragma once

#include <algorithm>
#include <cfloat>
TheNumbat's avatar
TheNumbat committed
6
7
8
#include <cmath>
#include <ostream>
#include <vector>
TheNumbat's avatar
TheNumbat committed
9
10
11

#include "mat4.h"
#include "ray.h"
TheNumbat's avatar
TheNumbat committed
12
13
#include "vec2.h"
#include "vec3.h"
TheNumbat's avatar
TheNumbat committed
14
15
16
17

struct BBox {

    /// Default min is max float value, default max is negative max float value
TheNumbat's avatar
TheNumbat committed
18
    BBox() : min(FLT_MAX), max(-FLT_MAX) {}
TheNumbat's avatar
TheNumbat committed
19
    /// Set minimum and maximum extent
TheNumbat's avatar
TheNumbat committed
20
    explicit BBox(Vec3 min, Vec3 max) : min(min), max(max) {}
TheNumbat's avatar
TheNumbat committed
21

TheNumbat's avatar
TheNumbat committed
22
23
24
    BBox(const BBox &) = default;
    BBox &operator=(const BBox &) = default;
    ~BBox() = default;
TheNumbat's avatar
TheNumbat committed
25
26
27
28
29
30
31
32
33
34

    /// Rest min to max float, max to negative max float
    void reset() {
        min = Vec3(FLT_MAX);
        max = Vec3(-FLT_MAX);
    }

    /// Expand bounding box to include point
    void enclose(Vec3 point) {
        min = hmin(min, point);
TheNumbat's avatar
TheNumbat committed
35
        max = hmax(max, point);
TheNumbat's avatar
TheNumbat committed
36
37
38
39
40
41
42
    }
    void enclose(BBox box) {
        min = hmin(min, box.min);
        max = hmax(max, box.max);
    }

    /// Get center point of box
TheNumbat's avatar
TheNumbat committed
43
    Vec3 center() const { return (min + max) * 0.5f; }
TheNumbat's avatar
TheNumbat committed
44
45

    // Check whether box has no volume
TheNumbat's avatar
TheNumbat committed
46
    bool empty() const { return min.x > max.x || min.y > max.y || min.z > max.z; }
TheNumbat's avatar
TheNumbat committed
47
48
49

    /// Get surface area of the box
    float surface_area() const {
TheNumbat's avatar
TheNumbat committed
50
51
        if (empty())
            return 0.0f;
TheNumbat's avatar
TheNumbat committed
52
53
54
55
56
        Vec3 extent = max - min;
        return 2.0f * (extent.x * extent.z + extent.x * extent.y + extent.y * extent.z);
    }

    /// Transform box by a matrix
TheNumbat's avatar
TheNumbat committed
57
    void transform(const Mat4 &trans) {
TheNumbat's avatar
TheNumbat committed
58
59
        Vec3 amin = min, amax = max;
        min = max = trans[3].xyz();
TheNumbat's avatar
TheNumbat committed
60
61
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
TheNumbat's avatar
TheNumbat committed
62
63
                float a = trans[j][i] * amin[j];
                float b = trans[j][i] * amax[j];
TheNumbat's avatar
TheNumbat committed
64
65
                if (a < b) {
                    min[i] += a;
TheNumbat's avatar
TheNumbat committed
66
67
                    max[i] += b;
                } else {
TheNumbat's avatar
TheNumbat committed
68
                    min[i] += b;
TheNumbat's avatar
TheNumbat committed
69
70
71
72
73
74
75
                    max[i] += a;
                }
            }
        }
    }

    // TODO (PathTracer): see student/bbox.cpp
TheNumbat's avatar
TheNumbat committed
76
    bool hit(const Ray &ray, Vec2 &times) const;
TheNumbat's avatar
TheNumbat committed
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

    /// Get the eight corner points of the bounding box
    std::vector<Vec3> corners() const {
        std::vector<Vec3> ret(8);
        ret[0] = Vec3(min.x, min.y, min.z);
        ret[1] = Vec3(max.x, min.y, min.z);
        ret[2] = Vec3(min.x, max.y, min.z);
        ret[3] = Vec3(min.x, min.y, max.z);
        ret[4] = Vec3(max.x, max.y, min.z);
        ret[5] = Vec3(min.x, max.y, max.z);
        ret[6] = Vec3(max.x, min.y, max.z);
        ret[7] = Vec3(max.x, max.y, max.z);
        return ret;
    }

TheNumbat's avatar
TheNumbat committed
92
    /// Given a screen transformation (projection), calculate screen-space ([-1,1]x[-1,1])
TheNumbat's avatar
TheNumbat committed
93
    /// bounds that will always contain the bounding box on screen
TheNumbat's avatar
TheNumbat committed
94
    void screen_rect(const Mat4 &transform, Vec2 &min_out, Vec2 &max_out) const {
TheNumbat's avatar
TheNumbat committed
95
96
97
98
99

        min_out = Vec2(FLT_MAX);
        max_out = Vec2(-FLT_MAX);
        auto c = corners();
        bool partially_behind = false, all_behind = true;
TheNumbat's avatar
TheNumbat committed
100
        for (auto &v : c) {
TheNumbat's avatar
TheNumbat committed
101
            Vec3 p = transform * v;
TheNumbat's avatar
TheNumbat committed
102
            if (p.z < 0) {
TheNumbat's avatar
TheNumbat committed
103
104
105
106
107
108
109
110
                partially_behind = true;
            } else {
                all_behind = false;
            }
            min_out = hmin(min_out, Vec2(p.x, p.y));
            max_out = hmax(max_out, Vec2(p.x, p.y));
        }

TheNumbat's avatar
TheNumbat committed
111
        if (partially_behind && !all_behind) {
TheNumbat's avatar
TheNumbat committed
112
113
            min_out = Vec2(-1.0f, -1.0f);
            max_out = Vec2(1.0f, 1.0f);
TheNumbat's avatar
TheNumbat committed
114
        } else if (all_behind) {
TheNumbat's avatar
TheNumbat committed
115
116
117
118
119
120
121
122
            min_out = Vec2(0.0f, 0.0f);
            max_out = Vec2(0.0f, 0.0f);
        }
    }

    Vec3 min, max;
};

TheNumbat's avatar
TheNumbat committed
123
124
125
inline std::ostream &operator<<(std::ostream &out, BBox b) {
    out << "BBox{" << b.min << "," << b.max << "}";
    return out;
TheNumbat's avatar
TheNumbat committed
126
}