/* This code provided under the terms of the Id Software LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. All changes and additions to the original source which have been developed by other contributors (see CONTRIBUTORS) are provided under the terms of the license the contributors choose (see LICENSE), to the extent permitted by the LICENSE_ID. If you did not receive a copy of the contributor license, please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #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 }