/* Copyright (C) John W. Ratcliff, 2001. * All rights reserved worldwide. * * This software is provided "as is" without express or implied * warranties. You may freely copy and compile this source into * applications you distribute provided that the copyright text * below is included in the resulting source code, for example: * "Portions Copyright (C) John W. Ratcliff, 2001" */ #include "Stdafx.h" #include "sphere.h" #include #include #include #include bool Vector3d::IsInStaticRange() const { const float LIMIT = 3276700.0f; if (x-LIMIT) if (y-LIMIT) if (z-LIMIT) return true; return false; } void Sphere::Set(const Vector3d ¢er, float radius) { #ifdef __STATIC_RANGE__ assert(center.IsInStaticRange()); #endif mCenter = center; mRadius = radius; mRadius2 = radius*radius; } //ray-sphere intersection test from Graphics Gems p.388 // **NOTE** There is a bug in this Graphics Gem. If the origin // of the ray is *inside* the sphere being tested, it reports the // wrong intersection location. This code has a fix for the bug. bool Sphere::RayIntersection(const Vector3d &rayOrigin, const Vector3d &dir, Vector3d *intersect) { //notation: //point E = rayOrigin //point O = sphere center Vector3d EO = mCenter - rayOrigin; Vector3d V = dir; float dist2 = EO.x*EO.x + EO.y*EO.y + EO.z * EO.z; // Bug Fix For Gem, if origin is *inside* the sphere, invert the // direction vector so that we get a valid intersection location. if ( dist2 < mRadius2 ) V*=-1; float v = EO.Dot(V); float disc = mRadius2 - (EO.Length2() - v*v); if (disc > 0.0f) { if ( intersect ) { float d = sqrtf(disc); //float dist2 = rayOrigin.DistanceSq(mCenter); *intersect = rayOrigin + V*(v-d); } return true; } return false; } // bool Sphere::RayIntersection(const Vector3d &rayOrigin, const Vector3d &V, float distance, Vector3d *intersect) { Vector3d sect; bool hit = RayIntersectionInFront(rayOrigin,V,§); if ( hit ) { float d = rayOrigin.DistanceSq(sect); if ( d > (distance*distance) ) return false; if ( intersect ) *intersect = sect; return true; } return false; } bool Sphere::RayIntersectionInFront(const Vector3d &rayOrigin, const Vector3d &V, Vector3d *intersect) { Vector3d sect; bool hit = RayIntersection(rayOrigin,V,§); if ( hit ) { Vector3d dir = sect - rayOrigin; float dot = dir.Dot(V); if ( dot >= 0 ) // then it's in front! { if ( intersect ) *intersect = sect; return true; } } return false; } void Sphere::Report(void) { } /* An Efficient Bounding Sphere by Jack Ritter from "Graphics Gems", Academic Press, 1990 */ /* Routine to calculate tight bounding sphere over */ /* a set of points in 3D */ /* This contains the routine find_bounding_sphere(), */ /* the struct definition, and the globals used for parameters. */ /* The abs() of all coordinates must be < BIGNUMBER */ /* Code written by Jack Ritter and Lyle Rains. */ #define BIGNUMBER 100000000.0 /* hundred million */ void Sphere::Compute(const SphereInterface &source) { Vector3d xmin,xmax,ymin,ymax,zmin,zmax,dia1,dia2; /* FIRST PASS: find 6 minima/maxima points */ xmin.Set(BIGNUMBER,BIGNUMBER,BIGNUMBER); xmax.Set(-BIGNUMBER,-BIGNUMBER,-BIGNUMBER); ymin.Set(BIGNUMBER,BIGNUMBER,BIGNUMBER); ymax.Set(-BIGNUMBER,-BIGNUMBER,-BIGNUMBER); zmin.Set(BIGNUMBER,BIGNUMBER,BIGNUMBER); zmax.Set(-BIGNUMBER,-BIGNUMBER,-BIGNUMBER); int count = source.GetVertexCount(); for (int i=0; ixmax.GetX()) xmax = caller_p; if (caller_p.GetY()ymax.GetY()) ymax = caller_p; if (caller_p.GetZ()zmax.GetZ()) zmax = caller_p; } /* Set xspan = distance between the 2 points xmin & xmax (squared) */ float dx = xmax.GetX() - xmin.GetX(); float dy = xmax.GetY() - xmin.GetY(); float dz = xmax.GetZ() - xmin.GetZ(); float xspan = dx*dx + dy*dy + dz*dz; /* Same for y & z spans */ dx = ymax.GetX() - ymin.GetX(); dy = ymax.GetY() - ymin.GetY(); dz = ymax.GetZ() - ymin.GetZ(); float yspan = dx*dx + dy*dy + dz*dz; dx = zmax.GetX() - zmin.GetX(); dy = zmax.GetY() - zmin.GetY(); dz = zmax.GetZ() - zmin.GetZ(); float zspan = dx*dx + dy*dy + dz*dz; /* Set points dia1 & dia2 to the maximally separated pair */ dia1 = xmin; dia2 = xmax; /* assume xspan biggest */ float maxspan = xspan; if (yspan>maxspan) { maxspan = yspan; dia1 = ymin; dia2 = ymax; } if (zspan>maxspan) { dia1 = zmin; dia2 = zmax; } /* dia1,dia2 is a diameter of initial sphere */ /* calc initial center */ mCenter.SetX( (dia1.GetX()+dia2.GetX())*0.5f ); mCenter.SetY( (dia1.GetY()+dia2.GetY())*0.5f ); mCenter.SetZ( (dia1.GetZ()+dia2.GetZ())*0.5f ); /* calculate initial radius**2 and radius */ dx = dia2.GetX()-mCenter.GetX(); /* x component of radius vector */ dy = dia2.GetY()-mCenter.GetY(); /* y component of radius vector */ dz = dia2.GetZ()-mCenter.GetZ(); /* z component of radius vector */ mRadius2 = dx*dx + dy*dy + dz*dz; mRadius = float(sqrt(mRadius2)); /* SECOND PASS: increment current sphere */ for (int j=0; j mRadius2) /* do r**2 test first */ { /* this point is outside of current sphere */ float old_to_p = float(sqrt(old_to_p_sq)); /* calc radius of new sphere */ mRadius = (mRadius + old_to_p) * 0.5f; mRadius2 = mRadius*mRadius; /* for next r**2 compare */ float old_to_new = old_to_p - mRadius; /* calc center of new sphere */ float recip = 1.0f /old_to_p; float cx = (mRadius*mCenter.GetX() + old_to_new*caller_p.GetX()) * recip; float cy = (mRadius*mCenter.GetY() + old_to_new*caller_p.GetY()) * recip; float cz = (mRadius*mCenter.GetZ() + old_to_new*caller_p.GetZ()) * recip; mCenter.Set(cx,cy,cz); } } }