cubemapper
diff src/geom.cc @ 0:8fc9e1d3aad2
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 27 Jul 2017 20:36:12 +0300 |
parents | |
children | 2bfafdced01a |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/geom.cc Thu Jul 27 20:36:12 2017 +0300 1.3 @@ -0,0 +1,334 @@ 1.4 +#include <assert.h> 1.5 +#include <float.h> 1.6 +#include <algorithm> 1.7 +#include "geom.h" 1.8 + 1.9 +GeomObject::~GeomObject() 1.10 +{ 1.11 +} 1.12 + 1.13 + 1.14 +Sphere::Sphere() 1.15 +{ 1.16 + radius = 1.0; 1.17 +} 1.18 + 1.19 +Sphere::Sphere(const Vec3 ¢, float radius) 1.20 + : center(cent) 1.21 +{ 1.22 + this->radius = radius; 1.23 +} 1.24 + 1.25 +void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2) 1.26 +{ 1.27 + const Sphere *sph1 = dynamic_cast<const Sphere*>(obj1); 1.28 + const Sphere *sph2 = dynamic_cast<const Sphere*>(obj2); 1.29 + 1.30 + if(!sph1 || !sph2) { 1.31 + fprintf(stderr, "Sphere::set_union: arguments must be spheres"); 1.32 + return; 1.33 + } 1.34 + 1.35 + float dist = length(sph1->center - sph2->center); 1.36 + float surf_dist = dist - (sph1->radius + sph2->radius); 1.37 + float d1 = sph1->radius + surf_dist / 2.0; 1.38 + float d2 = sph2->radius + surf_dist / 2.0; 1.39 + float t = d1 / (d1 + d2); 1.40 + 1.41 + if(t < 0.0) t = 0.0; 1.42 + if(t > 1.0) t = 1.0; 1.43 + 1.44 + center = sph1->center * t + sph2->center * (1.0 - t); 1.45 + radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius); 1.46 +} 1.47 + 1.48 +void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2) 1.49 +{ 1.50 + fprintf(stderr, "Sphere::intersection undefined\n"); 1.51 +} 1.52 + 1.53 +bool Sphere::intersect(const Ray &ray, HitPoint *hit) const 1.54 +{ 1.55 + float a = dot(ray.dir, ray.dir); 1.56 + float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) + 1.57 + 2.0 * ray.dir.y * (ray.origin.y - center.y) + 1.58 + 2.0 * ray.dir.z * (ray.origin.z - center.z); 1.59 + float c = dot(ray.origin, ray.origin) + dot(center, center) - 1.60 + 2.0 * dot(ray.origin, center) - radius * radius; 1.61 + 1.62 + float discr = b * b - 4.0 * a * c; 1.63 + if(discr < 1e-4) { 1.64 + return false; 1.65 + } 1.66 + 1.67 + float sqrt_discr = sqrt(discr); 1.68 + float t0 = (-b + sqrt_discr) / (2.0 * a); 1.69 + float t1 = (-b - sqrt_discr) / (2.0 * a); 1.70 + 1.71 + if(t0 < 1e-4) 1.72 + t0 = t1; 1.73 + if(t1 < 1e-4) 1.74 + t1 = t0; 1.75 + 1.76 + float t = t0 < t1 ? t0 : t1; 1.77 + if(t < 1e-4) { 1.78 + return false; 1.79 + } 1.80 + 1.81 + // fill the HitPoint structure 1.82 + if(hit) { 1.83 + hit->obj = this; 1.84 + hit->dist = t; 1.85 + hit->pos = ray.origin + ray.dir * t; 1.86 + hit->normal = (hit->pos - center) / radius; 1.87 + } 1.88 + return true; 1.89 +} 1.90 + 1.91 + 1.92 +AABox::AABox() 1.93 +{ 1.94 +} 1.95 + 1.96 +AABox::AABox(const Vec3 &vmin, const Vec3 &vmax) 1.97 + : min(vmin), max(vmax) 1.98 +{ 1.99 +} 1.100 + 1.101 +void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2) 1.102 +{ 1.103 + const AABox *box1 = dynamic_cast<const AABox*>(obj1); 1.104 + const AABox *box2 = dynamic_cast<const AABox*>(obj2); 1.105 + 1.106 + if(!box1 || !box2) { 1.107 + fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); 1.108 + return; 1.109 + } 1.110 + 1.111 + min.x = std::min(box1->min.x, box2->min.x); 1.112 + min.y = std::min(box1->min.y, box2->min.y); 1.113 + min.z = std::min(box1->min.z, box2->min.z); 1.114 + 1.115 + max.x = std::max(box1->max.x, box2->max.x); 1.116 + max.y = std::max(box1->max.y, box2->max.y); 1.117 + max.z = std::max(box1->max.z, box2->max.z); 1.118 +} 1.119 + 1.120 +void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2) 1.121 +{ 1.122 + const AABox *box1 = dynamic_cast<const AABox*>(obj1); 1.123 + const AABox *box2 = dynamic_cast<const AABox*>(obj2); 1.124 + 1.125 + if(!box1 || !box2) { 1.126 + fprintf(stderr, "AABox::set_intersection: arguments must be AABoxes too\n"); 1.127 + return; 1.128 + } 1.129 + 1.130 + for(int i=0; i<3; i++) { 1.131 + min[i] = std::max(box1->min[i], box2->min[i]); 1.132 + max[i] = std::min(box1->max[i], box2->max[i]); 1.133 + 1.134 + if(max[i] < min[i]) { 1.135 + max[i] = min[i]; 1.136 + } 1.137 + } 1.138 +} 1.139 + 1.140 +bool AABox::intersect(const Ray &ray, HitPoint *hit) const 1.141 +{ 1.142 + Vec3 param[2] = {min, max}; 1.143 + Vec3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); 1.144 + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; 1.145 + 1.146 + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; 1.147 + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; 1.148 + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; 1.149 + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; 1.150 + 1.151 + if(tmin > tymax || tymin > tmax) { 1.152 + return false; 1.153 + } 1.154 + if(tymin > tmin) { 1.155 + tmin = tymin; 1.156 + } 1.157 + if(tymax < tmax) { 1.158 + tmax = tymax; 1.159 + } 1.160 + 1.161 + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; 1.162 + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; 1.163 + 1.164 + if(tmin > tzmax || tzmin > tmax) { 1.165 + return false; 1.166 + } 1.167 + if(tzmin > tmin) { 1.168 + tmin = tzmin; 1.169 + } 1.170 + if(tzmax < tmax) { 1.171 + tmax = tzmax; 1.172 + } 1.173 + 1.174 + float t = tmin < 1e-4 ? tmax : tmin; 1.175 + if(t >= 1e-4) { 1.176 + 1.177 + if(hit) { 1.178 + hit->obj = this; 1.179 + hit->dist = t; 1.180 + hit->pos = ray.origin + ray.dir * t; 1.181 + 1.182 + float min_dist = FLT_MAX; 1.183 + Vec3 offs = min + (max - min) / 2.0; 1.184 + Vec3 local_hit = hit->pos - offs; 1.185 + 1.186 + static const Vec3 axis[] = { 1.187 + Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1) 1.188 + }; 1.189 + //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}}; 1.190 + 1.191 + for(int i=0; i<3; i++) { 1.192 + float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i])); 1.193 + if(dist < min_dist) { 1.194 + min_dist = dist; 1.195 + hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0); 1.196 + //hit->texcoord = Vec2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]); 1.197 + } 1.198 + } 1.199 + } 1.200 + return true; 1.201 + } 1.202 + return false; 1.203 + 1.204 +} 1.205 + 1.206 +Plane::Plane() 1.207 + : normal(0.0, 1.0, 0.0) 1.208 +{ 1.209 +} 1.210 + 1.211 +Plane::Plane(const Vec3 &p, const Vec3 &norm) 1.212 + : pt(p) 1.213 +{ 1.214 + normal = normalize(norm); 1.215 +} 1.216 + 1.217 +Plane::Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) 1.218 + : pt(p1) 1.219 +{ 1.220 + normal = normalize(cross(p2 - p1, p3 - p1)); 1.221 +} 1.222 + 1.223 +Plane::Plane(const Vec3 &normal, float dist) 1.224 +{ 1.225 + this->normal = normalize(normal); 1.226 + pt = this->normal * dist; 1.227 +} 1.228 + 1.229 +void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2) 1.230 +{ 1.231 + fprintf(stderr, "Plane::set_union undefined\n"); 1.232 +} 1.233 + 1.234 +void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2) 1.235 +{ 1.236 + fprintf(stderr, "Plane::set_intersection undefined\n"); 1.237 +} 1.238 + 1.239 +bool Plane::intersect(const Ray &ray, HitPoint *hit) const 1.240 +{ 1.241 + float ndotdir = dot(normal, ray.dir); 1.242 + if(fabs(ndotdir) < 1e-4) { 1.243 + return false; 1.244 + } 1.245 + 1.246 + if(hit) { 1.247 + Vec3 ptdir = pt - ray.origin; 1.248 + float t = dot(normal, ptdir) / ndotdir; 1.249 + 1.250 + hit->pos = ray.origin + ray.dir * t; 1.251 + hit->normal = normal; 1.252 + hit->obj = this; 1.253 + } 1.254 + return true; 1.255 +} 1.256 + 1.257 +float sphere_distance(const Vec3 ¢, float rad, const Vec3 &pt) 1.258 +{ 1.259 + return length(pt - cent) - rad; 1.260 +} 1.261 + 1.262 +// TODO version which takes both radii into account 1.263 +float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt) 1.264 +{ 1.265 + Vec3 ab_dir = b - a; 1.266 + float ab_len_sq = length_sq(ab_dir); 1.267 + 1.268 + if(fabs(ab_len_sq) < 1e-5) { 1.269 + // if a == b, the capsule is a sphere with radius the maximum of the capsule radii 1.270 + return sphere_distance(a, std::max(ra, rb), pt); 1.271 + } 1.272 + float ab_len = sqrt(ab_len_sq); 1.273 + 1.274 + Vec3 ap_dir = pt - a; 1.275 + 1.276 + float t = dot(ap_dir, ab_dir / ab_len) / ab_len; 1.277 + if(t < 0.0) { 1.278 + return sphere_distance(a, ra, pt); 1.279 + } 1.280 + if(t >= 1.0) { 1.281 + return sphere_distance(b, rb, pt); 1.282 + } 1.283 + 1.284 + Vec3 pproj = a + ab_dir * t; 1.285 + return length(pproj - pt) - ra; 1.286 +} 1.287 + 1.288 +#if 0 1.289 +float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt) 1.290 +{ 1.291 + Vec3 ab_dir = b - a; 1.292 + 1.293 + if(fabs(length_sq(ab_dir)) < 1e-5) { 1.294 + // if a == b, the capsule is a sphere with radius the maximum of the capsule radii 1.295 + return sphere_distance(a, std::max(ra, rb), pt); 1.296 + } 1.297 + float ab_len = length(ab_dir); 1.298 + 1.299 + Vec3 ap_dir = pt - a; 1.300 + Vec3 rotaxis = normalize(cross(ab_dir, ap_dir)); 1.301 + 1.302 + Mat4 rmat; 1.303 + rmat.set_rotation(rotaxis, M_PI / 2.0); 1.304 + Vec3 right = rmat * ab_dir / ab_len; 1.305 + 1.306 + // XXX I think this check is redundant, always false, due to the cross product order 1.307 + //assert(dot(right, ab_dir) >= 0.0); 1.308 + if(dot(right, ab_dir) < 0.0) { 1.309 + right = -right; 1.310 + } 1.311 + Vec3 aa = a + right * ra; 1.312 + Vec3 bb = b + right * rb; 1.313 + 1.314 + // project pt to the line segment bb-aa, see if the projection lies within the interval [0, 1) 1.315 + Vec3 aabb_dir = bb - aa; 1.316 + float aabb_len = length(aabb_dir); 1.317 + Vec3 aap_dir = pt - aa; 1.318 + 1.319 + float t = dot(aap_dir, aabb_dir / aabb_len) / aabb_len; 1.320 + if(t < 0.0) { 1.321 + return sphere_distance(a, ra, pt); 1.322 + } 1.323 + if(t >= 1.0) { 1.324 + return sphere_distance(b, rb, pt); 1.325 + } 1.326 + 1.327 + Vec3 ppt = aa + aabb_dir * t; 1.328 + Vec3 norm = ppt - pt; 1.329 + float dist = length(norm); 1.330 + 1.331 + if(dot(norm, right) < 0.0) { 1.332 + // inside the cone 1.333 + dist = -dist; 1.334 + } 1.335 + return dist; 1.336 +} 1.337 +#endif