Scene::CalcBounds(), including Box::Extend(Box&), Box::Clear()
[MicroTrace.git] / Box.cxx
1 #include "Box.hxx"
2 #include "InfinitePlane.hxx"
3
4 Box::Box() : m_min(Infinity, Infinity, Infinity),
5 m_max(-Infinity, -Infinity, -Infinity)
6 {
7 }
8
9 Box::Box(Vec3f min, Vec3f max) : m_min(min), m_max(max)
10 {
11 }
12
13 Box::~Box()
14 {
15 }
16
17 Box::Box(const Box& b)
18 {
19 m_min = b.m_min;
20 m_max = b.m_max;
21 }
22
23 Box&
24 Box::operator=(const Box& b)
25 {
26 m_min = b.m_min;
27 m_max = b.m_max;
28 return *this;
29 }
30
31 void
32 Box::Clear()
33 {
34 m_min = Vec3f(Infinity, Infinity, Infinity);
35 m_max = Vec3f(-Infinity, -Infinity, -Infinity);
36 }
37
38 void
39 Box::Extend(const Vec3f& a)
40 {
41 // for all three coordinates, move m_max or m_min to the point
42 for(size_t i = 0; i < 3; i++) {
43 if(a[i] > m_max[i]) {
44 m_max[i] = a[i];
45 } else if(a[i] < m_min[i]) {
46 m_min[i] = a[i];
47 } // else: do nothing, coordinate is inside the box
48 }
49 }
50
51 void
52 Box::Extend(const Box& box)
53 {
54 Extend(box.min());
55 Extend(box.max());
56 }
57
58 bool Box::OverlapsHelper(Box b) const {
59 bool overlaps = true;
60 for(size_t i = 0; i < 3; i++) {
61 overlaps &=
62 (m_min[i] < b.min()[i] && b.min()[i] < m_max[i]) ||
63 (m_min[i] < b.max()[i] && b.min()[i] < m_max[i]) ||
64 (m_min[i] > b.min()[i] && b.min()[i] > m_max[i]) ||
65 (m_min[i] > b.max()[i] && b.min()[i] > m_max[i]);
66 }
67 return overlaps;
68 }
69
70 bool
71 Box::Overlaps(const Box& b) const
72 {
73 // Boxes overlap if b.m_min or b.m_max are between our m_max and m_min
74 // for all dimensions
75 bool overlaps = OverlapsHelper(b);
76
77 // if that is not enough, swap y coordinates, we could have the following
78 // (sample for 2-dimensional case):
79 // o----------
80 // | | + denotes crossing,
81 // -----+----o | o denotes min/max point
82 // | | | |
83 // | -----+----o
84 // | |
85 // o----------
86 if(!overlaps) {
87 Vec3f min = b.min();
88 Vec3f max = b.max();
89 Vec3f new_min(min[0], max[1], min[2]);
90 Vec3f new_max(max[0], min[1], max[2]);
91 Box new_y_box(new_min, new_max);
92 overlaps = OverlapsHelper(new_y_box);
93
94 // and now for y and z coordinates also
95 if(!overlaps) {
96 min = new_y_box.min();
97 max = new_y_box.max();
98 new_min = Vec3f(min[0], min[1], max[2]);
99 new_max = Vec3f(max[0], max[1], min[2]);
100 Box new_yz_box(new_min, new_max);
101 overlaps = OverlapsHelper(new_yz_box);
102
103 // and now for only z coordinates
104 if(!overlaps) {
105 min = new_y_box.min();
106 max = new_y_box.max();
107 new_min = Vec3f(min[0], max[1], min[2]);
108 new_max = Vec3f(max[0], min[1], max[2]);
109 Box new_z_box(new_min, new_max);
110 overlaps = OverlapsHelper(new_z_box);
111 // at this point, we do not need to swap further, as either m_max or
112 // m_min has been tested inside b.
113 }
114 }
115 }
116
117 return overlaps;
118 }
119
120 void
121 Box::Clip(const Ray& ray, float& tnear, float& tfar) const
122 {
123 // Note: equations here are the same as in InfinitePlane::Intersect()
124 // t = ((o-a)*n) / (d*n)
125 // o = ray.origin(), d = ray.direction(),
126 // n = surface normal of plane, a = anchor point of plane
127 Vec3f diff_min = m_min - ray.origin(); // o-a
128 Vec3f diff_max = m_max - ray.origin();
129
130 float cos_theta, temp;
131 float tx_near = -Infinity, tx_far = Infinity,
132 ty_near = -Infinity, ty_far = Infinity,
133 tz_near = -Infinity, tz_far = Infinity;
134
135 // clip along x axis
136 if((cos_theta = ray.direction().dot(Vec3f(1,0,0))) != 0) // if not parallel...
137 tx_near = diff_min.dot(Vec3f(1,0,0)) / cos_theta;
138 if((cos_theta = ray.direction().dot(Vec3f(1,0,0))) != 0)
139 tx_far = diff_max.dot(Vec3f(1,0,0)) / cos_theta;
140
141 // we don't know which is nearer, m_min or m_max, so swap them if neccessary
142 if(tx_near > tx_far) {
143 temp = tx_near;
144 tx_near = tx_far;
145 tx_far = temp;
146 }
147
148 // do the same for y axis
149 if((cos_theta = ray.direction().dot(Vec3f(0,1,0))) != 0)
150 ty_near = diff_min.dot(Vec3f(0,1,0)) / cos_theta;
151 if((cos_theta = ray.direction().dot(Vec3f(0,1,0))) != 0)
152 ty_far = diff_max.dot(Vec3f(0,1,0)) / cos_theta;
153 if(ty_near > ty_far) {
154 temp = ty_near;
155 ty_near = ty_far;
156 ty_far = temp;
157 }
158
159 // ...and for z axis
160 if((cos_theta = ray.direction().dot(Vec3f(0,0,1))) != 0)
161 tz_near = diff_min.dot(Vec3f(0,0,1)) / cos_theta;
162 if((cos_theta = ray.direction().dot(Vec3f(0,0,1))) != 0)
163 tz_far = diff_max.dot(Vec3f(0,0,1)) / cos_theta;
164 if(tz_near > tz_far) {
165 temp = tz_near;
166 tz_near = tz_far;
167 tz_far = temp;
168 }
169
170 // now the maximum of "near"s is the entry point of the ray, the minimum
171 // of "far"s is the ray's exit point. Visually speaking: the ray must cross
172 // all three "near" planes before it can be inside the box.
173 // Note: If t_near_max > t_far_min, the ray does not intersect the box
174 tnear = fmax(fmax(tx_near, ty_near), tz_near);
175 tfar = fmin(fmin(tx_far, ty_far), tz_far);
176 }
177
178 const Vec3f&
179 Box::min() const
180 {
181 return m_min;
182 }
183
184 const Vec3f&
185 Box::max() const
186 {
187 return m_max;
188 }
This page took 0.058683 seconds and 5 git commands to generate.