249 lines
6.4 KiB
C++
249 lines
6.4 KiB
C++
/* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
bool Vector3d::IsInStaticRange() const
|
|
{
|
|
const float LIMIT = 3276700.0f;
|
|
if (x<LIMIT && x>-LIMIT)
|
|
if (y<LIMIT && y>-LIMIT)
|
|
if (z<LIMIT && 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; i<count; i++)
|
|
{
|
|
Vector3d caller_p;
|
|
source.GetVertex(i,caller_p);
|
|
|
|
if (caller_p.GetX()<xmin.GetX()) xmin = caller_p; /* New xminimum point */
|
|
if (caller_p.GetX()>xmax.GetX()) xmax = caller_p;
|
|
if (caller_p.GetY()<ymin.GetY()) ymin = caller_p;
|
|
if (caller_p.GetY()>ymax.GetY()) ymax = caller_p;
|
|
if (caller_p.GetZ()<zmin.GetZ()) zmin = 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<count; j++)
|
|
{
|
|
Vector3d caller_p;
|
|
source.GetVertex(j,caller_p);
|
|
dx = caller_p.GetX()-mCenter.GetX();
|
|
dy = caller_p.GetY()-mCenter.GetY();
|
|
dz = caller_p.GetZ()-mCenter.GetZ();
|
|
float old_to_p_sq = dx*dx + dy*dy + dz*dz;
|
|
if (old_to_p_sq > 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);
|
|
}
|
|
}
|
|
}
|