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;