coeng
diff src/geom.cc @ 8:8cce82794f90
seems to work nicely
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 15 Feb 2015 05:14:20 +0200 |
parents | af24cfbdf9b6 |
children |
line diff
1.1 --- a/src/geom.cc Sat Feb 14 10:08:00 2015 +0200 1.2 +++ b/src/geom.cc Sun Feb 15 05:14:20 2015 +0200 1.3 @@ -1,3 +1,4 @@ 1.4 +#include "opengl.h" 1.5 #include <float.h> 1.6 #include <algorithm> 1.7 #include "geom.h" 1.8 @@ -20,6 +21,22 @@ 1.9 return type; 1.10 } 1.11 1.12 +void GeomShape::set_transform(const Matrix4x4 &m) 1.13 +{ 1.14 + xform = m; 1.15 + inv_xform = xform.inverse(); 1.16 +} 1.17 + 1.18 +const Matrix4x4 &GeomShape::get_transform() const 1.19 +{ 1.20 + return xform; 1.21 +} 1.22 + 1.23 +const Matrix4x4 &GeomShape::get_inv_transform() const 1.24 +{ 1.25 + return inv_xform; 1.26 +} 1.27 + 1.28 // ---- Sphere class ---- 1.29 1.30 Sphere::Sphere() 1.31 @@ -35,8 +52,15 @@ 1.32 radius = rad; 1.33 } 1.34 1.35 -bool Sphere::intersect(const Ray &ray, HitPoint *hit) const 1.36 +bool Sphere::contains(const Vector3 &pt) const 1.37 { 1.38 + return distance_sq(pt) <= 0.0; 1.39 +} 1.40 + 1.41 +bool Sphere::intersect(const Ray &inray, HitPoint *hit) const 1.42 +{ 1.43 + Ray ray = inray.transformed(inv_xform); 1.44 + 1.45 float a = dot_product(ray.dir, ray.dir); 1.46 float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) + 1.47 2.0 * ray.dir.y * (ray.origin.y - center.y) + 1.48 @@ -68,8 +92,10 @@ 1.49 hit->shape = this; 1.50 hit->data = 0; 1.51 hit->dist = t; 1.52 - hit->pos = ray.origin + ray.dir * t; 1.53 - hit->normal = (hit->pos - center) / radius; 1.54 + 1.55 + Vector3 local_pos = ray.origin + ray.dir * t; 1.56 + hit->pos = inray.origin + inray.dir * t; 1.57 + hit->normal = (local_pos - center) / radius; 1.58 } 1.59 return true; 1.60 } 1.61 @@ -89,12 +115,19 @@ 1.62 1.63 float Sphere::distance(const Vector3 &pt) const 1.64 { 1.65 - return sqrt(distance_sq(pt)); 1.66 + Vector3 local_pt = pt.transformed(inv_xform); 1.67 + return (local_pt - center).length() - radius; 1.68 } 1.69 1.70 float Sphere::distance_sq(const Vector3 &pt) const 1.71 { 1.72 - return (pt - center).length_sq(); 1.73 + Vector3 local_pt = pt.transformed(inv_xform); 1.74 + return (local_pt - center).length_sq() - radius * radius; 1.75 +} 1.76 + 1.77 +void Sphere::draw() const 1.78 +{ 1.79 + // TODO 1.80 } 1.81 1.82 // ---- Plane class ---- 1.83 @@ -142,10 +175,15 @@ 1.84 dist = distance(p1); 1.85 } 1.86 1.87 +bool Plane::contains(const Vector3 &pt) const 1.88 +{ 1.89 + return distance(pt) <= 0.0; 1.90 +} 1.91 1.92 -bool Plane::intersect(const Ray &ray, HitPoint *hit) const 1.93 +bool Plane::intersect(const Ray &inray, HitPoint *hit) const 1.94 { 1.95 - Vector3 pt = Vector3(0, 0, 0) + normal * dist; 1.96 + Ray ray = inray.transformed(inv_xform); 1.97 + Vector3 pt = normal * dist; 1.98 1.99 float ndotdir = dot_product(normal, ray.dir); 1.100 if(fabs(ndotdir) < 1e-4) { 1.101 @@ -156,7 +194,7 @@ 1.102 Vector3 ptdir = pt - ray.origin; 1.103 float t = dot_product(normal, ptdir) / ndotdir; 1.104 1.105 - hit->pos = ray.origin + ray.dir * t; 1.106 + hit->pos = inray.origin + inray.dir * t; 1.107 hit->normal = normal; 1.108 hit->shape = this; 1.109 } 1.110 @@ -187,7 +225,7 @@ 1.111 float Plane::distance(const Vector3 &v) const 1.112 { 1.113 Vector3 pt = normal * dist; 1.114 - return dot_product(v - pt, normal); 1.115 + return dot_product(v.transformed(inv_xform) - pt, normal); 1.116 } 1.117 1.118 float Plane::distance_sq(const Vector3 &v) const 1.119 @@ -196,20 +234,187 @@ 1.120 return d * d; 1.121 } 1.122 1.123 +void Plane::draw() const 1.124 +{ 1.125 + // TODO 1.126 +} 1.127 + 1.128 +// ---- AABox ---- 1.129 + 1.130 +AABox::AABox() 1.131 +{ 1.132 + type = GEOM_AABOX; 1.133 +} 1.134 + 1.135 +AABox::AABox(const Vector3 &vmin, const Vector3 &vmax) 1.136 + : min(vmin), max(vmax) 1.137 +{ 1.138 + type = GEOM_AABOX; 1.139 +} 1.140 + 1.141 +void AABox::set_union(const GeomShape *obj1, const GeomShape *obj2) 1.142 +{ 1.143 + const AABox *box1 = (const AABox*)obj1; 1.144 + const AABox *box2 = (const AABox*)obj2; 1.145 + 1.146 + if(!box1 || !box2 || obj1->get_type() != GEOM_AABOX || obj2->get_type() != GEOM_AABOX) { 1.147 + fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); 1.148 + return; 1.149 + } 1.150 + 1.151 + min.x = std::min(box1->min.x, box2->min.x); 1.152 + min.y = std::min(box1->min.y, box2->min.y); 1.153 + min.z = std::min(box1->min.z, box2->min.z); 1.154 + 1.155 + max.x = std::max(box1->max.x, box2->max.x); 1.156 + max.y = std::max(box1->max.y, box2->max.y); 1.157 + max.z = std::max(box1->max.z, box2->max.z); 1.158 +} 1.159 + 1.160 +void AABox::set_intersection(const GeomShape *obj1, const GeomShape *obj2) 1.161 +{ 1.162 + const AABox *box1 = (const AABox*)obj1; 1.163 + const AABox *box2 = (const AABox*)obj2; 1.164 + 1.165 + if(!box1 || !box2 || obj1->get_type() != GEOM_AABOX || obj2->get_type() != GEOM_AABOX) { 1.166 + fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); 1.167 + return; 1.168 + } 1.169 + 1.170 + for(int i=0; i<3; i++) { 1.171 + min[i] = std::max(box1->min[i], box2->min[i]); 1.172 + max[i] = std::min(box1->max[i], box2->max[i]); 1.173 + 1.174 + if(max[i] < min[i]) { 1.175 + max[i] = min[i]; 1.176 + } 1.177 + } 1.178 +} 1.179 + 1.180 +bool AABox::contains(const Vector3 &pt) const 1.181 +{ 1.182 + return pt.x >= min.x && pt.x <= max.x && 1.183 + pt.y >= min.y && pt.y <= max.y && 1.184 + pt.z >= min.z && pt.z <= max.z; 1.185 +} 1.186 + 1.187 +bool AABox::intersect(const Ray &inray, HitPoint *hit) const 1.188 +{ 1.189 + Ray ray = inray.transformed(inv_xform); 1.190 + 1.191 + Vector3 param[2] = {min, max}; 1.192 + Vector3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); 1.193 + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; 1.194 + 1.195 + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; 1.196 + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; 1.197 + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; 1.198 + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; 1.199 + 1.200 + if(tmin > tymax || tymin > tmax) { 1.201 + return false; 1.202 + } 1.203 + if(tymin > tmin) { 1.204 + tmin = tymin; 1.205 + } 1.206 + if(tymax < tmax) { 1.207 + tmax = tymax; 1.208 + } 1.209 + 1.210 + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; 1.211 + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; 1.212 + 1.213 + if(tmin > tzmax || tzmin > tmax) { 1.214 + return false; 1.215 + } 1.216 + if(tzmin > tmin) { 1.217 + tmin = tzmin; 1.218 + } 1.219 + if(tzmax < tmax) { 1.220 + tmax = tzmax; 1.221 + } 1.222 + 1.223 + float t = tmin < 1e-4 ? tmax : tmin; 1.224 + if(t >= 1e-4) { 1.225 + 1.226 + if(hit) { 1.227 + hit->shape = this; 1.228 + hit->dist = t; 1.229 + hit->pos = inray.origin + inray.dir * t; 1.230 + 1.231 + float min_dist = FLT_MAX; 1.232 + Vector3 offs = min + (max - min) / 2.0; 1.233 + Vector3 local_hit = hit->pos - offs; 1.234 + 1.235 + static const Vector3 axis[] = { 1.236 + Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1) 1.237 + }; 1.238 + //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}}; 1.239 + 1.240 + for(int i=0; i<3; i++) { 1.241 + float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i])); 1.242 + if(dist < min_dist) { 1.243 + min_dist = dist; 1.244 + hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0); 1.245 + //hit->texcoord = Vector2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]); 1.246 + } 1.247 + } 1.248 + hit->normal.transform(Matrix3x3(xform)); 1.249 + } 1.250 + return true; 1.251 + } 1.252 + return false; 1.253 +} 1.254 + 1.255 +bool AABox::collide(const GeomShape *geom, HitPoint *hit) const 1.256 +{ 1.257 + fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__); 1.258 + return false; 1.259 +} 1.260 + 1.261 +float AABox::distance(const Vector3 &pt) const 1.262 +{ 1.263 + float d = distance_sq(pt); 1.264 + return d * d; 1.265 +} 1.266 + 1.267 +float AABox::distance_sq(const Vector3 &world_pt) const 1.268 +{ 1.269 + Vector3 pt = pt.transformed(inv_xform); 1.270 + 1.271 + float dx = std::max(pt.x - max.x, min.x - pt.x); 1.272 + float dy = std::max(pt.y - max.y, min.y - pt.y); 1.273 + float dz = std::max(pt.z - max.z, min.z - pt.z); 1.274 + 1.275 + return Vector3(dx, dy, dz).length_sq(); 1.276 +} 1.277 + 1.278 +void AABox::draw() const 1.279 +{ 1.280 +} 1.281 + 1.282 1.283 // collision detection functions 1.284 1.285 static bool col_sphere_sphere(const Sphere *a, const Sphere *b, HitPoint *hit) 1.286 { 1.287 - float sum_rad = a->radius + b->radius; 1.288 - Vector3 dir = b->center - a->center; 1.289 + const Matrix4x4 &xforma = a->get_transform(); 1.290 + const Matrix4x4 &xformb = b->get_transform(); 1.291 + 1.292 + Vector3 ca = a->center.transformed(xforma); 1.293 + Vector3 cb = b->center.transformed(xformb); 1.294 + float rada = a->radius * xforma.get_scaling().x; 1.295 + float radb = b->radius * xformb.get_scaling().x; 1.296 + 1.297 + float sum_rad = rada + radb; 1.298 + Vector3 dir = cb - ca; 1.299 float dist_sq = dir.length_sq(); 1.300 bool res = dist_sq <= sum_rad * sum_rad; 1.301 1.302 if(res && hit) { 1.303 - hit->pos = a->center + dir * 0.5; 1.304 + hit->pos = ca + dir * 0.5; 1.305 hit->shape = b; 1.306 - hit->normal = -dir; 1.307 + hit->normal = -dir.normalized(); 1.308 } 1.309 return res; 1.310 } 1.311 @@ -222,18 +427,25 @@ 1.312 1.313 static bool col_sphere_plane(const Sphere *sph, const Plane *plane, HitPoint *hit) 1.314 { 1.315 - float dist = plane->distance(sph->center); 1.316 - bool res = fabs(dist) <= sph->radius; 1.317 + const Matrix4x4 &xform = sph->get_transform(); 1.318 + 1.319 + Vector3 sph_center = sph->center.transformed(xform); 1.320 + float sph_rad = sph->radius * xform.get_scaling().x; 1.321 + 1.322 + float dist = plane->distance(sph_center); 1.323 + bool res = fabs(dist) <= sph_rad; 1.324 1.325 if(res && hit) { 1.326 - Vector3 planept = plane->normal * plane->dist; 1.327 - Vector3 sphdir = sph->center - planept; 1.328 - if(dot_product(sphdir, plane->normal) >= 0.0f) { 1.329 - hit->normal = plane->normal; 1.330 + Vector3 planept = (plane->normal * plane->dist).transformed(plane->get_transform()); 1.331 + Vector3 planenorm = plane->normal.transformed(Matrix3x3(plane->get_transform())); 1.332 + 1.333 + Vector3 sphdir = sph_center - planept; 1.334 + if(dot_product(sphdir, planenorm) >= 0.0f) { 1.335 + hit->normal = planenorm; 1.336 } else { 1.337 - hit->normal = -plane->normal; 1.338 + hit->normal = -planenorm; 1.339 } 1.340 - hit->pos = sph->center - plane->normal * fabs(dist); 1.341 + hit->pos = sph_center - planenorm * fabs(dist); 1.342 hit->shape = plane; 1.343 } 1.344 return res;