55cfed7d2fb41aa161d0e8d5d6d267bda4105fc5
[laserbrain_demo] / src / geom.cc
1 #include <algorithm>
2 #include <float.h>
3 #include "geom.h"
4
5 GeomObject::~GeomObject()
6 {
7 }
8
9
10 Sphere::Sphere()
11 {
12         radius = 1.0;
13 }
14
15 Sphere::Sphere(const Vec3 &cent, float radius)
16         : center(cent)
17 {
18         this->radius = radius;
19 }
20
21 GeomObjectType Sphere::get_type() const
22 {
23         return GOBJ_SPHERE;
24 }
25
26 void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2)
27 {
28         if(obj1->get_type() != GOBJ_SPHERE || obj2->get_type() != GOBJ_SPHERE) {
29                 fprintf(stderr, "Sphere::set_union: arguments must be spheres");
30                 return;
31         }
32         const Sphere *sph1 = (const Sphere*)obj1;
33         const Sphere *sph2 = (const Sphere*)obj2;
34
35         float dist = length(sph1->center - sph2->center);
36         float surf_dist = dist - (sph1->radius + sph2->radius);
37         float d1 = sph1->radius + surf_dist / 2.0;
38         float d2 = sph2->radius + surf_dist / 2.0;
39         float t = d1 / (d1 + d2);
40
41         if(t < 0.0) t = 0.0;
42         if(t > 1.0) t = 1.0;
43
44         center = sph1->center * t + sph2->center * (1.0 - t);
45         radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius);
46 }
47
48 void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
49 {
50         fprintf(stderr, "Sphere::intersection undefined\n");
51 }
52
53 bool Sphere::intersect(const Ray &ray, HitPoint *hit) const
54 {
55         float a = dot(ray.dir, ray.dir);
56         float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) +
57                 2.0 * ray.dir.y * (ray.origin.y - center.y) +
58                 2.0 * ray.dir.z * (ray.origin.z - center.z);
59         float c = dot(ray.origin, ray.origin) + dot(center, center) -
60                 2.0 * dot(ray.origin, center) - radius * radius;
61
62         float discr = b * b - 4.0 * a * c;
63         if(discr < 1e-4) {
64                 return false;
65         }
66
67         float sqrt_discr = sqrt(discr);
68         float t0 = (-b + sqrt_discr) / (2.0 * a);
69         float t1 = (-b - sqrt_discr) / (2.0 * a);
70
71         if(t0 < 1e-4)
72                 t0 = t1;
73         if(t1 < 1e-4)
74                 t1 = t0;
75
76         float t = t0 < t1 ? t0 : t1;
77         if(t < 1e-4) {
78                 return false;
79         }
80
81         // fill the HitPoint structure
82         if(hit) {
83                 hit->obj = this;
84                 hit->dist = t;
85                 hit->pos = ray.origin + ray.dir * t;
86                 hit->normal = (hit->pos - center) / radius;
87         }
88         return true;
89 }
90
91 bool Sphere::contains(const Vec3 &pt) const
92 {
93         return length_sq(pt - center) <= radius * radius;
94 }
95
96 float Sphere::distance(const Vec3 &v) const
97 {
98         return length(v - center) - radius;
99 }
100
101 AABox::AABox()
102 {
103 }
104
105 AABox::AABox(const Vec3 &vmin, const Vec3 &vmax)
106         : min(vmin), max(vmax)
107 {
108 }
109
110 GeomObjectType AABox::get_type() const
111 {
112         return GOBJ_AABOX;
113 }
114
115 void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2)
116 {
117         if(obj1->get_type() != GOBJ_AABOX || obj2->get_type() != GOBJ_AABOX) {
118                 fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n");
119                 return;
120         }
121         const AABox *box1 = (const AABox*)obj1;
122         const AABox *box2 = (const AABox*)obj2;
123
124         min.x = std::min(box1->min.x, box2->min.x);
125         min.y = std::min(box1->min.y, box2->min.y);
126         min.z = std::min(box1->min.z, box2->min.z);
127
128         max.x = std::max(box1->max.x, box2->max.x);
129         max.y = std::max(box1->max.y, box2->max.y);
130         max.z = std::max(box1->max.z, box2->max.z);
131 }
132
133 void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
134 {
135         if(obj1->get_type() != GOBJ_AABOX || obj2->get_type() != GOBJ_AABOX) {
136                 fprintf(stderr, "AABox::set_intersection: arguments must be AABoxes too\n");
137                 return;
138         }
139         const AABox *box1 = (const AABox*)obj1;
140         const AABox *box2 = (const AABox*)obj2;
141
142         for(int i=0; i<3; i++) {
143                 min[i] = std::max(box1->min[i], box2->min[i]);
144                 max[i] = std::min(box1->max[i], box2->max[i]);
145
146                 if(max[i] < min[i]) {
147                         max[i] = min[i];
148                 }
149         }
150 }
151
152 bool AABox::intersect(const Ray &ray, HitPoint *hit) const
153 {
154         Vec3 param[2] = {min, max};
155         Vec3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z);
156         int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0};
157
158         float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x;
159         float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x;
160         float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y;
161         float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y;
162
163         if(tmin > tymax || tymin > tmax) {
164                 return false;
165         }
166         if(tymin > tmin) {
167                 tmin = tymin;
168         }
169         if(tymax < tmax) {
170                 tmax = tymax;
171         }
172
173         float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z;
174         float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z;
175
176         if(tmin > tzmax || tzmin > tmax) {
177                 return false;
178         }
179         if(tzmin > tmin) {
180                 tmin = tzmin;
181         }
182         if(tzmax < tmax) {
183                 tmax = tzmax;
184         }
185
186         float t = tmin < 1e-4 ? tmax : tmin;
187         if(t >= 1e-4) {
188
189                 if(hit) {
190                         hit->obj = this;
191                         hit->dist = t;
192                         hit->pos = ray.origin + ray.dir * t;
193
194                         float min_dist = FLT_MAX;
195                         Vec3 offs = min + (max - min) / 2.0;
196                         Vec3 local_hit = hit->pos - offs;
197
198                         static const Vec3 axis[] = {
199                                 Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)
200                         };
201                         //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}};
202
203                         for(int i=0; i<3; i++) {
204                                 float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i]));
205                                 if(dist < min_dist) {
206                                         min_dist = dist;
207                                         hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0);
208                                         //hit->texcoord = Vec2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]);
209                                 }
210                         }
211                 }
212                 return true;
213         }
214         return false;
215 }
216
217 bool AABox::contains(const Vec3 &v) const
218 {
219         return v.x >= min.x && v.y >= min.y && v.z >= min.z &&
220                 v.x <= max.x && v.y <= max.y && v.z <= max.z;
221 }
222
223 float AABox::distance(const Vec3 &v) const
224 {
225         return 0.0;     // TODO
226 }
227
228
229 Plane::Plane()
230         : normal(0.0, 1.0, 0.0)
231 {
232 }
233
234 Plane::Plane(const Vec3 &p, const Vec3 &norm)
235         : pt(p)
236 {
237         normal = normalize(norm);
238 }
239
240 Plane::Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3)
241         : pt(p1)
242 {
243         normal = normalize(cross(p2 - p1, p3 - p1));
244 }
245
246 Plane::Plane(const Vec3 &normal, float dist)
247 {
248         this->normal = normalize(normal);
249         pt = this->normal * dist;
250 }
251
252 GeomObjectType Plane::get_type() const
253 {
254         return GOBJ_PLANE;
255 }
256
257 void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2)
258 {
259         fprintf(stderr, "Plane::set_union undefined\n");
260 }
261
262 void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
263 {
264         fprintf(stderr, "Plane::set_intersection undefined\n");
265 }
266
267 bool Plane::intersect(const Ray &ray, HitPoint *hit) const
268 {
269         float ndotdir = dot(normal, ray.dir);
270         if(fabs(ndotdir) < 1e-4) {
271                 return false;
272         }
273
274         if(hit) {
275                 Vec3 ptdir = pt - ray.origin;
276                 float t = dot(normal, ptdir) / ndotdir;
277
278                 hit->dist = t;
279                 hit->pos = ray.origin + ray.dir * t;
280                 hit->normal = normal;
281                 hit->obj = this;
282         }
283         return true;
284 }
285
286 bool Plane::contains(const Vec3 &v) const
287 {
288         return dot(v, normal) <= 0.0;
289 }
290
291 float Plane::distance(const Vec3 &v) const
292 {
293         return dot(v - pt, normal);
294 }