/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "mathlib.h" const aabb_t g_aabb_null = { { 0, 0, 0, }, { -FLT_MAX, -FLT_MAX, -FLT_MAX, }, }; void aabb_construct_for_vec3(aabb_t *aabb, const vec3_t min, const vec3_t max) { VectorMid(min, max, aabb->origin); VectorSubtract(max, aabb->origin, aabb->extents); } void aabb_clear(aabb_t *aabb) { VectorCopy(g_aabb_null.origin, aabb->origin); VectorCopy(g_aabb_null.extents, aabb->extents); } void aabb_extend_by_point(aabb_t *aabb, const vec3_t point) { #if 1 int i; vec_t min, max, displacement; for(i=0; i<3; i++) { displacement = point[i] - aabb->origin[i]; if(fabs(displacement) > aabb->extents[i]) { if(aabb->extents[i] < 0) // degenerate { min = max = point[i]; } else if(displacement > 0) { min = aabb->origin[i] - aabb->extents[i]; max = aabb->origin[i] + displacement; } else { max = aabb->origin[i] + aabb->extents[i]; min = aabb->origin[i] + displacement; } aabb->origin[i] = (min + max) * 0.5f; aabb->extents[i] = max - aabb->origin[i]; } } #else unsigned int i; for(i=0; i<3; ++i) { if(aabb->extents[i] < 0) // degenerate { aabb->origin[i] = point[i]; aabb->extents[i] = 0; } else { vec_t displacement = point[i] - aabb->origin[i]; vec_t increment = (vec_t)fabs(displacement) - aabb->extents[i]; if(increment > 0) { increment *= (vec_t)((displacement > 0) ? 0.5 : -0.5); aabb->extents[i] += increment; aabb->origin[i] += increment; } } } #endif } void aabb_extend_by_aabb(aabb_t *aabb, const aabb_t *aabb_src) { int i; vec_t min, max, displacement, difference; for(i=0; i<3; i++) { displacement = aabb_src->origin[i] - aabb->origin[i]; difference = aabb_src->extents[i] - aabb->extents[i]; if(aabb->extents[i] < 0 || difference >= fabs(displacement)) { // 2nd contains 1st aabb->extents[i] = aabb_src->extents[i]; aabb->origin[i] = aabb_src->origin[i]; } else if(aabb_src->extents[i] < 0 || -difference >= fabs(displacement)) { // 1st contains 2nd continue; } else { // not contained if(displacement > 0) { min = aabb->origin[i] - aabb->extents[i]; max = aabb_src->origin[i] + aabb_src->extents[i]; } else { min = aabb_src->origin[i] - aabb_src->extents[i]; max = aabb->origin[i] + aabb->extents[i]; } aabb->origin[i] = (min + max) * 0.5f; aabb->extents[i] = max - aabb->origin[i]; } } } void aabb_extend_by_vec3(aabb_t *aabb, vec3_t extension) { VectorAdd(aabb->extents, extension, aabb->extents); } int aabb_test_point(const aabb_t *aabb, const vec3_t point) { int i; for(i=0; i<3; i++) if(fabs(point[i] - aabb->origin[i]) >= aabb->extents[i]) return 0; return 1; } int aabb_test_aabb(const aabb_t *aabb, const aabb_t *aabb_src) { int i; for (i=0; i<3; i++) if ( fabs(aabb_src->origin[i] - aabb->origin[i]) > (fabs(aabb->extents[i]) + fabs(aabb_src->extents[i])) ) return 0; return 1; } int aabb_test_plane(const aabb_t *aabb, const float *plane) { float fDist, fIntersect; // calc distance of origin from plane fDist = DotProduct(plane, aabb->origin) + plane[3]; // calc extents distance relative to plane normal fIntersect = (vec_t)(fabs(plane[0] * aabb->extents[0]) + fabs(plane[1] * aabb->extents[1]) + fabs(plane[2] * aabb->extents[2])); // accept if origin is less than or equal to this distance if (fabs(fDist) < fIntersect) return 1; // partially inside else if (fDist < 0) return 2; // totally inside return 0; // totally outside } /* Fast Ray-Box Intersection by Andrew Woo from "Graphics Gems", Academic Press, 1990 */ #define NUMDIM 3 #define RIGHT 0 #define LEFT 1 #define MIDDLE 2 int aabb_intersect_ray(const aabb_t *aabb, const ray_t *ray, vec3_t intersection) { int inside = 1; char quadrant[NUMDIM]; register int i; int whichPlane; double maxT[NUMDIM]; double candidatePlane[NUMDIM]; const float *origin = ray->origin; const float *direction = ray->direction; /* Find candidate planes; this loop can be avoided if rays cast all from the eye(assume perpsective view) */ for (i=0; iorigin[i] - aabb->extents[i])) { quadrant[i] = LEFT; candidatePlane[i] = (aabb->origin[i] - aabb->extents[i]); inside = 0; } else if (origin[i] > (aabb->origin[i] + aabb->extents[i])) { quadrant[i] = RIGHT; candidatePlane[i] = (aabb->origin[i] + aabb->extents[i]); inside = 0; } else { quadrant[i] = MIDDLE; } } /* Ray origin inside bounding box */ if(inside == 1) { VectorCopy(ray->origin, intersection); return 1; } /* Calculate T distances to candidate planes */ for (i = 0; i < NUMDIM; i++) { if (quadrant[i] != MIDDLE && direction[i] !=0.) maxT[i] = (candidatePlane[i] - origin[i]) / direction[i]; else maxT[i] = -1.; } /* Get largest of the maxT's for final choice of intersection */ whichPlane = 0; for (i = 1; i < NUMDIM; i++) if (maxT[whichPlane] < maxT[i]) whichPlane = i; /* Check final candidate actually inside box */ if (maxT[whichPlane] < 0.) return 0; for (i = 0; i < NUMDIM; i++) { if (whichPlane != i) { intersection[i] = (vec_t)(origin[i] + maxT[whichPlane] * direction[i]); if (fabs(intersection[i] - aabb->origin[i]) > aabb->extents[i]) return 0; } else { intersection[i] = (vec_t)candidatePlane[i]; } } return 1; /* ray hits box */ } int aabb_test_ray(const aabb_t* aabb, const ray_t* ray) { vec3_t displacement, ray_absolute; vec_t f; displacement[0] = ray->origin[0] - aabb->origin[0]; if(fabs(displacement[0]) > aabb->extents[0] && displacement[0] * ray->direction[0] >= 0.0f) return 0; displacement[1] = ray->origin[1] - aabb->origin[1]; if(fabs(displacement[1]) > aabb->extents[1] && displacement[1] * ray->direction[1] >= 0.0f) return 0; displacement[2] = ray->origin[2] - aabb->origin[2]; if(fabs(displacement[2]) > aabb->extents[2] && displacement[2] * ray->direction[2] >= 0.0f) return 0; ray_absolute[0] = (float)fabs(ray->direction[0]); ray_absolute[1] = (float)fabs(ray->direction[1]); ray_absolute[2] = (float)fabs(ray->direction[2]); f = ray->direction[1] * displacement[2] - ray->direction[2] * displacement[1]; if((float)fabs(f) > aabb->extents[1] * ray_absolute[2] + aabb->extents[2] * ray_absolute[1]) return 0; f = ray->direction[2] * displacement[0] - ray->direction[0] * displacement[2]; if((float)fabs(f) > aabb->extents[0] * ray_absolute[2] + aabb->extents[2] * ray_absolute[0]) return 0; f = ray->direction[0] * displacement[1] - ray->direction[1] * displacement[0]; if((float)fabs(f) > aabb->extents[0] * ray_absolute[1] + aabb->extents[1] * ray_absolute[0]) return 0; return 1; } void aabb_orthogonal_transform(aabb_t* dst, const aabb_t* src, const m4x4_t transform) { VectorCopy(src->origin, dst->origin); m4x4_transform_point(transform, dst->origin); dst->extents[0] = (vec_t)(fabs(transform[0] * src->extents[0]) + fabs(transform[4] * src->extents[1]) + fabs(transform[8] * src->extents[2])); dst->extents[1] = (vec_t)(fabs(transform[1] * src->extents[0]) + fabs(transform[5] * src->extents[1]) + fabs(transform[9] * src->extents[2])); dst->extents[2] = (vec_t)(fabs(transform[2] * src->extents[0]) + fabs(transform[6] * src->extents[1]) + fabs(transform[10] * src->extents[2])); } void aabb_for_bbox(aabb_t *aabb, const bbox_t *bbox) { int i; vec3_t temp[3]; VectorCopy(bbox->aabb.origin, aabb->origin); // calculate the AABB extents in local coord space from the OBB extents and axes VectorScale(bbox->axes[0], bbox->aabb.extents[0], temp[0]); VectorScale(bbox->axes[1], bbox->aabb.extents[1], temp[1]); VectorScale(bbox->axes[2], bbox->aabb.extents[2], temp[2]); for(i=0;i<3;i++) aabb->extents[i] = (vec_t)(fabs(temp[0][i]) + fabs(temp[1][i]) + fabs(temp[2][i])); } void aabb_for_area(aabb_t *aabb, vec3_t area_tl, vec3_t area_br, int axis) { aabb_clear(aabb); aabb->extents[axis] = FLT_MAX; aabb_extend_by_point(aabb, area_tl); aabb_extend_by_point(aabb, area_br); } int aabb_oriented_intersect_plane(const aabb_t *aabb, const m4x4_t transform, const vec_t* plane) { vec_t fDist, fIntersect; // calc distance of origin from plane fDist = DotProduct(plane, aabb->origin) + plane[3]; // calc extents distance relative to plane normal fIntersect = (vec_t)(fabs(aabb->extents[0] * DotProduct(plane, transform)) + fabs(aabb->extents[1] * DotProduct(plane, transform+4)) + fabs(aabb->extents[2] * DotProduct(plane, transform+8))); // accept if origin is less than this distance if (fabs(fDist) < fIntersect) return 1; // partially inside else if (fDist < 0) return 2; // totally inside return 0; // totally outside } void aabb_corners(const aabb_t* aabb, vec3_t corners[8]) { vec3_t min, max; VectorSubtract(aabb->origin, aabb->extents, min); VectorAdd(aabb->origin, aabb->extents, max); VectorSet(corners[0], min[0], max[1], max[2]); VectorSet(corners[1], max[0], max[1], max[2]); VectorSet(corners[2], max[0], min[1], max[2]); VectorSet(corners[3], min[0], min[1], max[2]); VectorSet(corners[4], min[0], max[1], min[2]); VectorSet(corners[5], max[0], max[1], min[2]); VectorSet(corners[6], max[0], min[1], min[2]); VectorSet(corners[7], min[0], min[1], min[2]); } void bbox_update_radius(bbox_t *bbox) { bbox->radius = VectorLength(bbox->aabb.extents); } void aabb_for_transformed_aabb(aabb_t* dst, const aabb_t* src, const m4x4_t transform) { if(src->extents[0] < 0 || src->extents[1] < 0 || src->extents[2] < 0) { aabb_clear(dst); return; } VectorCopy(src->origin, dst->origin); m4x4_transform_point(transform, dst->origin); dst->extents[0] = (vec_t)(fabs(transform[0] * src->extents[0]) + fabs(transform[4] * src->extents[1]) + fabs(transform[8] * src->extents[2])); dst->extents[1] = (vec_t)(fabs(transform[1] * src->extents[0]) + fabs(transform[5] * src->extents[1]) + fabs(transform[9] * src->extents[2])); dst->extents[2] = (vec_t)(fabs(transform[2] * src->extents[0]) + fabs(transform[6] * src->extents[1]) + fabs(transform[10] * src->extents[2])); } void bbox_for_oriented_aabb(bbox_t *bbox, const aabb_t *aabb, const m4x4_t matrix, const vec3_t euler, const vec3_t scale) { double rad[3]; double pi_180 = Q_PI / 180; double A, B, C, D, E, F, AD, BD; VectorCopy(aabb->origin, bbox->aabb.origin); m4x4_transform_point(matrix, bbox->aabb.origin); bbox->aabb.extents[0] = aabb->extents[0] * scale[0]; bbox->aabb.extents[1] = aabb->extents[1] * scale[1]; bbox->aabb.extents[2] = aabb->extents[2] * scale[2]; rad[0] = euler[0] * pi_180; rad[1] = euler[1] * pi_180; rad[2] = euler[2] * pi_180; A = cos(rad[0]); B = sin(rad[0]); C = cos(rad[1]); D = sin(rad[1]); E = cos(rad[2]); F = sin(rad[2]); AD = A * -D; BD = B * -D; bbox->axes[0][0] = (vec_t)(C*E); bbox->axes[0][1] = (vec_t)(-BD*E + A*F); bbox->axes[0][2] = (vec_t)(AD*E + B*F); bbox->axes[1][0] = (vec_t)(-C*F); bbox->axes[1][1] = (vec_t)(BD*F + A*E); bbox->axes[1][2] = (vec_t)(-AD*F + B*E); bbox->axes[2][0] = (vec_t)D; bbox->axes[2][1] = (vec_t)(-B*C); bbox->axes[2][2] = (vec_t)(A*C); bbox_update_radius(bbox); } int bbox_intersect_plane(const bbox_t *bbox, const vec_t* plane) { vec_t fDist, fIntersect; // calc distance of origin from plane fDist = DotProduct(plane, bbox->aabb.origin) + plane[3]; // trivial accept/reject using bounding sphere if (fabs(fDist) > bbox->radius) { if (fDist < 0) return 2; // totally inside else return 0; // totally outside } // calc extents distance relative to plane normal fIntersect = (vec_t)(fabs(bbox->aabb.extents[0] * DotProduct(plane, bbox->axes[0])) + fabs(bbox->aabb.extents[1] * DotProduct(plane, bbox->axes[1])) + fabs(bbox->aabb.extents[2] * DotProduct(plane, bbox->axes[2]))); // accept if origin is less than this distance if (fabs(fDist) < fIntersect) return 1; // partially inside else if (fDist < 0) return 2; // totally inside return 0; // totally outside }