rev |
line source |
nuclear@7
|
1 #include <float.h>
|
nuclear@7
|
2 #include <algorithm>
|
nuclear@7
|
3 #include "geom.h"
|
nuclear@7
|
4
|
nuclear@7
|
5 static bool col_sphere_sphere(const Sphere *a, const Sphere *b, HitPoint *hit);
|
nuclear@7
|
6 static bool col_plane_plane(const Plane *a, const Plane *b, HitPoint *hit);
|
nuclear@7
|
7 static bool col_sphere_plane(const Sphere *sph, const Plane *plane, HitPoint *hit);
|
nuclear@7
|
8
|
nuclear@7
|
9 GeomShape::GeomShape()
|
nuclear@7
|
10 {
|
nuclear@7
|
11 type = GEOM_UNKNOWN;
|
nuclear@7
|
12 }
|
nuclear@7
|
13
|
nuclear@7
|
14 GeomShape::~GeomShape()
|
nuclear@7
|
15 {
|
nuclear@7
|
16 }
|
nuclear@7
|
17
|
nuclear@7
|
18 GeomShape::Type GeomShape::get_type() const
|
nuclear@7
|
19 {
|
nuclear@7
|
20 return type;
|
nuclear@7
|
21 }
|
nuclear@7
|
22
|
nuclear@7
|
23 // ---- Sphere class ----
|
nuclear@7
|
24
|
nuclear@7
|
25 Sphere::Sphere()
|
nuclear@7
|
26 {
|
nuclear@7
|
27 type = GEOM_SPHERE;
|
nuclear@7
|
28 radius = 1.0f;
|
nuclear@7
|
29 }
|
nuclear@7
|
30
|
nuclear@7
|
31 Sphere::Sphere(const Vector3 &c, float rad)
|
nuclear@7
|
32 : center(c)
|
nuclear@7
|
33 {
|
nuclear@7
|
34 type = GEOM_SPHERE;
|
nuclear@7
|
35 radius = rad;
|
nuclear@7
|
36 }
|
nuclear@7
|
37
|
nuclear@7
|
38 bool Sphere::intersect(const Ray &ray, HitPoint *hit) const
|
nuclear@7
|
39 {
|
nuclear@7
|
40 float a = dot_product(ray.dir, ray.dir);
|
nuclear@7
|
41 float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) +
|
nuclear@7
|
42 2.0 * ray.dir.y * (ray.origin.y - center.y) +
|
nuclear@7
|
43 2.0 * ray.dir.z * (ray.origin.z - center.z);
|
nuclear@7
|
44 float c = dot_product(ray.origin, ray.origin) + dot_product(center, center) -
|
nuclear@7
|
45 2.0 * dot_product(ray.origin, center) - radius * radius;
|
nuclear@7
|
46
|
nuclear@7
|
47 float discr = b * b - 4.0 * a * c;
|
nuclear@7
|
48 if(discr < 1e-4) {
|
nuclear@7
|
49 return false;
|
nuclear@7
|
50 }
|
nuclear@7
|
51
|
nuclear@7
|
52 float sqrt_discr = sqrt(discr);
|
nuclear@7
|
53 float t0 = (-b + sqrt_discr) / (2.0 * a);
|
nuclear@7
|
54 float t1 = (-b - sqrt_discr) / (2.0 * a);
|
nuclear@7
|
55
|
nuclear@7
|
56 if(t0 < 1e-4)
|
nuclear@7
|
57 t0 = t1;
|
nuclear@7
|
58 if(t1 < 1e-4)
|
nuclear@7
|
59 t1 = t0;
|
nuclear@7
|
60
|
nuclear@7
|
61 float t = t0 < t1 ? t0 : t1;
|
nuclear@7
|
62 if(t < 1e-4) {
|
nuclear@7
|
63 return false;
|
nuclear@7
|
64 }
|
nuclear@7
|
65
|
nuclear@7
|
66 // fill the HitPoint structure
|
nuclear@7
|
67 if(hit) {
|
nuclear@7
|
68 hit->shape = this;
|
nuclear@7
|
69 hit->data = 0;
|
nuclear@7
|
70 hit->dist = t;
|
nuclear@7
|
71 hit->pos = ray.origin + ray.dir * t;
|
nuclear@7
|
72 hit->normal = (hit->pos - center) / radius;
|
nuclear@7
|
73 }
|
nuclear@7
|
74 return true;
|
nuclear@7
|
75 }
|
nuclear@7
|
76
|
nuclear@7
|
77 bool Sphere::collide(const GeomShape *geom, HitPoint *hit) const
|
nuclear@7
|
78 {
|
nuclear@7
|
79 switch(geom->get_type()) {
|
nuclear@7
|
80 case GEOM_SPHERE:
|
nuclear@7
|
81 return col_sphere_sphere(this, (const Sphere*)geom, hit);
|
nuclear@7
|
82 case GEOM_PLANE:
|
nuclear@7
|
83 return col_sphere_plane(this, (const Plane*)geom, hit);
|
nuclear@7
|
84 default:
|
nuclear@7
|
85 break;
|
nuclear@7
|
86 }
|
nuclear@7
|
87 return false;
|
nuclear@7
|
88 }
|
nuclear@7
|
89
|
nuclear@7
|
90 float Sphere::distance(const Vector3 &pt) const
|
nuclear@7
|
91 {
|
nuclear@7
|
92 return sqrt(distance_sq(pt));
|
nuclear@7
|
93 }
|
nuclear@7
|
94
|
nuclear@7
|
95 float Sphere::distance_sq(const Vector3 &pt) const
|
nuclear@7
|
96 {
|
nuclear@7
|
97 return (pt - center).length_sq();
|
nuclear@7
|
98 }
|
nuclear@7
|
99
|
nuclear@7
|
100 // ---- Plane class ----
|
nuclear@7
|
101
|
nuclear@7
|
102 Plane::Plane()
|
nuclear@7
|
103 : normal(0, 1, 0)
|
nuclear@7
|
104 {
|
nuclear@7
|
105 type = GEOM_PLANE;
|
nuclear@7
|
106 dist = 0.0f;
|
nuclear@7
|
107 }
|
nuclear@7
|
108
|
nuclear@7
|
109 Plane::Plane(const Vector3 &n, float d)
|
nuclear@7
|
110 : normal(n)
|
nuclear@7
|
111 {
|
nuclear@7
|
112 type = GEOM_PLANE;
|
nuclear@7
|
113 dist = d;
|
nuclear@7
|
114
|
nuclear@7
|
115 normal.normalize();
|
nuclear@7
|
116 }
|
nuclear@7
|
117
|
nuclear@7
|
118 Plane::Plane(float a, float b, float c, float d)
|
nuclear@7
|
119 : normal(a, b, c)
|
nuclear@7
|
120 {
|
nuclear@7
|
121 type = GEOM_PLANE;
|
nuclear@7
|
122 dist = d;
|
nuclear@7
|
123
|
nuclear@7
|
124 normal.normalize();
|
nuclear@7
|
125 }
|
nuclear@7
|
126
|
nuclear@7
|
127 Plane::Plane(const Vector3 &pos, const Vector3 &norm)
|
nuclear@7
|
128 : normal(norm)
|
nuclear@7
|
129 {
|
nuclear@7
|
130 type = GEOM_PLANE;
|
nuclear@7
|
131
|
nuclear@7
|
132 dist = 0.0f;
|
nuclear@7
|
133 dist = distance(pos);
|
nuclear@7
|
134 }
|
nuclear@7
|
135
|
nuclear@7
|
136 Plane::Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3)
|
nuclear@7
|
137 {
|
nuclear@7
|
138 type = GEOM_PLANE;
|
nuclear@7
|
139
|
nuclear@7
|
140 normal = cross_product(p2 - p1, p3 - p1).normalized();
|
nuclear@7
|
141 dist = 0.0f;
|
nuclear@7
|
142 dist = distance(p1);
|
nuclear@7
|
143 }
|
nuclear@7
|
144
|
nuclear@7
|
145
|
nuclear@7
|
146 bool Plane::intersect(const Ray &ray, HitPoint *hit) const
|
nuclear@7
|
147 {
|
nuclear@7
|
148 Vector3 pt = Vector3(0, 0, 0) + normal * dist;
|
nuclear@7
|
149
|
nuclear@7
|
150 float ndotdir = dot_product(normal, ray.dir);
|
nuclear@7
|
151 if(fabs(ndotdir) < 1e-4) {
|
nuclear@7
|
152 return false;
|
nuclear@7
|
153 }
|
nuclear@7
|
154
|
nuclear@7
|
155 if(hit) {
|
nuclear@7
|
156 Vector3 ptdir = pt - ray.origin;
|
nuclear@7
|
157 float t = dot_product(normal, ptdir) / ndotdir;
|
nuclear@7
|
158
|
nuclear@7
|
159 hit->pos = ray.origin + ray.dir * t;
|
nuclear@7
|
160 hit->normal = normal;
|
nuclear@7
|
161 hit->shape = this;
|
nuclear@7
|
162 }
|
nuclear@7
|
163 return true;
|
nuclear@7
|
164 }
|
nuclear@7
|
165
|
nuclear@7
|
166 bool Plane::collide(const GeomShape *geom, HitPoint *hit) const
|
nuclear@7
|
167 {
|
nuclear@7
|
168 switch(geom->get_type()) {
|
nuclear@7
|
169 case GEOM_SPHERE:
|
nuclear@7
|
170 {
|
nuclear@7
|
171 bool res = col_sphere_plane((const Sphere*)geom, this, hit);
|
nuclear@7
|
172 if(hit) {
|
nuclear@7
|
173 hit->normal = -hit->normal;
|
nuclear@7
|
174 }
|
nuclear@7
|
175 return res;
|
nuclear@7
|
176 }
|
nuclear@7
|
177
|
nuclear@7
|
178 case GEOM_PLANE:
|
nuclear@7
|
179 return col_plane_plane(this, (const Plane*)geom, hit);
|
nuclear@7
|
180
|
nuclear@7
|
181 default:
|
nuclear@7
|
182 break;
|
nuclear@7
|
183 }
|
nuclear@7
|
184 return false;
|
nuclear@7
|
185 }
|
nuclear@7
|
186
|
nuclear@7
|
187 float Plane::distance(const Vector3 &v) const
|
nuclear@7
|
188 {
|
nuclear@7
|
189 Vector3 pt = normal * dist;
|
nuclear@7
|
190 return dot_product(v - pt, normal);
|
nuclear@7
|
191 }
|
nuclear@7
|
192
|
nuclear@7
|
193 float Plane::distance_sq(const Vector3 &v) const
|
nuclear@7
|
194 {
|
nuclear@7
|
195 float d = distance(v);
|
nuclear@7
|
196 return d * d;
|
nuclear@7
|
197 }
|
nuclear@7
|
198
|
nuclear@7
|
199
|
nuclear@7
|
200 // collision detection functions
|
nuclear@7
|
201
|
nuclear@7
|
202 static bool col_sphere_sphere(const Sphere *a, const Sphere *b, HitPoint *hit)
|
nuclear@7
|
203 {
|
nuclear@7
|
204 float sum_rad = a->radius + b->radius;
|
nuclear@7
|
205 Vector3 dir = b->center - a->center;
|
nuclear@7
|
206 float dist_sq = dir.length_sq();
|
nuclear@7
|
207 bool res = dist_sq <= sum_rad * sum_rad;
|
nuclear@7
|
208
|
nuclear@7
|
209 if(res && hit) {
|
nuclear@7
|
210 hit->pos = a->center + dir * 0.5;
|
nuclear@7
|
211 hit->shape = b;
|
nuclear@7
|
212 hit->normal = -dir;
|
nuclear@7
|
213 }
|
nuclear@7
|
214 return res;
|
nuclear@7
|
215 }
|
nuclear@7
|
216
|
nuclear@7
|
217 static bool col_plane_plane(const Plane *a, const Plane *b, HitPoint *hit)
|
nuclear@7
|
218 {
|
nuclear@7
|
219 fprintf(stderr, "%s: not implemented\n", __FUNCTION__);
|
nuclear@7
|
220 return false;
|
nuclear@7
|
221 }
|
nuclear@7
|
222
|
nuclear@7
|
223 static bool col_sphere_plane(const Sphere *sph, const Plane *plane, HitPoint *hit)
|
nuclear@7
|
224 {
|
nuclear@7
|
225 float dist = plane->distance(sph->center);
|
nuclear@7
|
226 bool res = fabs(dist) <= sph->radius;
|
nuclear@7
|
227
|
nuclear@7
|
228 if(res && hit) {
|
nuclear@7
|
229 Vector3 planept = plane->normal * plane->dist;
|
nuclear@7
|
230 Vector3 sphdir = sph->center - planept;
|
nuclear@7
|
231 if(dot_product(sphdir, plane->normal) >= 0.0f) {
|
nuclear@7
|
232 hit->normal = plane->normal;
|
nuclear@7
|
233 } else {
|
nuclear@7
|
234 hit->normal = -plane->normal;
|
nuclear@7
|
235 }
|
nuclear@7
|
236 hit->pos = sph->center - plane->normal * fabs(dist);
|
nuclear@7
|
237 hit->shape = plane;
|
nuclear@7
|
238 }
|
nuclear@7
|
239 return res;
|
nuclear@7
|
240 }
|