493 lines
13 KiB
C
493 lines
13 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"
|
||
|
*/
|
||
|
#pragma once
|
||
|
|
||
|
/***********************************************************************/
|
||
|
/** SPHEREPACK.H: Contains all of the support code for Sphere Trees. */
|
||
|
/** */
|
||
|
/** Written by John W. Ratcliff jratcliff@att.net */
|
||
|
/***********************************************************************/
|
||
|
|
||
|
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "vector.h" // need 3d vector support.
|
||
|
#include "pool.h" // need memory pool template class.
|
||
|
#include "sphere.h" // Need sphere base class.
|
||
|
#include "frustum.h" // Need to know what a frustum is.
|
||
|
|
||
|
#define DEMO 0// true if running in windows test app
|
||
|
|
||
|
enum SpherePackFlag
|
||
|
{
|
||
|
|
||
|
SPF_SUPERSPHERE =(1<<0), // this is a supersphere, allocated and deleted by us
|
||
|
SPF_ROOT_TREE =(1<<1), // member of the root tree
|
||
|
SPF_LEAF_TREE =(1<<2), // member of the leaf node tree
|
||
|
SPF_ROOTNODE =(1<<3), // this is the root node
|
||
|
SPF_RECOMPUTE =(1<<4), // needs recomputed bounding sphere
|
||
|
SPF_INTEGRATE =(1<<5), // needs to be reintegrated into tree
|
||
|
// Frame-to-frame view frustum status. Only does callbacks when a
|
||
|
// state change occurs.
|
||
|
SPF_HIDDEN =(1<<6), // outside of view frustum
|
||
|
SPF_PARTIAL =(1<<7), // partially inside view frustum
|
||
|
SPF_INSIDE =(1<<8) // completely inside view frustum
|
||
|
};
|
||
|
|
||
|
class SpherePackFactory; // forward reference the factory.
|
||
|
class SpherePack; // forward reference the sphere package
|
||
|
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable:4100)
|
||
|
|
||
|
// Virtual base class, used to implement callbacks for RayTracing,
|
||
|
// range testing, and frustum culling.
|
||
|
class SpherePackCallback
|
||
|
{
|
||
|
public:
|
||
|
SpherePackCallback() {}
|
||
|
virtual ~SpherePackCallback() {}
|
||
|
|
||
|
virtual void VisibilityCallback(const Frustum &f, // frustum clipped against
|
||
|
SpherePack *sphere, // leaf node sphere in question
|
||
|
ViewState state) // new state it is in.
|
||
|
{};
|
||
|
|
||
|
virtual void RayTraceCallback(const Vector3d &p1, // source pos of ray
|
||
|
const Vector3d &dir, // direction of ray
|
||
|
float distance, // distance of ray
|
||
|
const Vector3d §, // intersection location
|
||
|
SpherePack *sphere) // sphere ray hit
|
||
|
{};
|
||
|
|
||
|
virtual void RangeTestCallback(const Vector3d &searchpos, // position we are performing range test against.
|
||
|
float distance, // squared distance we are range searching against.
|
||
|
SpherePack *sphere,
|
||
|
ViewState state) // sphere within range, VS_PARTIAL if sphere straddles range test
|
||
|
{};
|
||
|
|
||
|
virtual void PointTest2dCallback(const Vector3d &searchpos, // position we are performing range test against.
|
||
|
SpherePack *sphere,
|
||
|
ViewState state) // sphere within range, VS_PARTIAL if sphere straddles range test
|
||
|
{};
|
||
|
|
||
|
private:
|
||
|
};
|
||
|
|
||
|
#pragma warning(pop)
|
||
|
|
||
|
class SpherePack : public Sphere
|
||
|
{
|
||
|
public:
|
||
|
SpherePack(void)
|
||
|
{
|
||
|
mUserData = 0; // default user data is null
|
||
|
mFactory = 0; // factory we are a member of
|
||
|
mNext = 0; // linked list pointers
|
||
|
mPrevious = 0;
|
||
|
mParent = 0;
|
||
|
mNextSibling = 0; // our brothers and sisters at this level.
|
||
|
mPrevSibling = 0;
|
||
|
mChildren = 0; // our children.
|
||
|
mChildCount = 0; // number of children we have.
|
||
|
mFifo1 = 0; // our FIFO1 location if we have one.
|
||
|
mFifo2 = 0; // our FIFO2 location if we have one.
|
||
|
SetRadius(0); // default radius
|
||
|
mCenter.Set(0,0,0); // default center position.
|
||
|
IS_SPHERE = false;
|
||
|
};
|
||
|
virtual ~SpherePack()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void Init(SpherePackFactory *factory, // factory we belong to
|
||
|
const Vector3d &pos, // center of sphere
|
||
|
float radius, // radius of sphere
|
||
|
void *userdata, bool isSphere) // user data
|
||
|
{
|
||
|
IS_SPHERE = isSphere;
|
||
|
mUserData = userdata;
|
||
|
mParent = 0;
|
||
|
mNextSibling = 0;
|
||
|
mPrevSibling = 0;
|
||
|
mFlags = 0;
|
||
|
mFifo1 = 0;
|
||
|
mFifo2 = 0;
|
||
|
mFactory = factory;
|
||
|
mCenter = pos;
|
||
|
SetRadius(radius);
|
||
|
};
|
||
|
|
||
|
// Access to SpherePack bit flags.
|
||
|
void SetSpherePackFlag(SpherePackFlag flag) { mFlags|=flag; };
|
||
|
void ClearSpherePackFlag(SpherePackFlag flag) { mFlags&=~flag; };
|
||
|
bool HasSpherePackFlag(SpherePackFlag flag) const
|
||
|
{
|
||
|
if ( mFlags & flag ) return true;
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
|
||
|
void SetParent(SpherePack *pack) { mParent = pack; };
|
||
|
SpherePack * GetParent(void) const { return mParent; };
|
||
|
|
||
|
// Sphere has a new position.
|
||
|
inline void NewPos(const Vector3d &pos);
|
||
|
// Sphere has a new position and radius
|
||
|
inline void NewPosRadius(const Vector3d &pos,float radius);
|
||
|
|
||
|
void Unlink(void)
|
||
|
{
|
||
|
if ( mFifo1 ) // if we belong to fifo1, null us out
|
||
|
{
|
||
|
*mFifo1 = 0;
|
||
|
mFifo1 = 0;
|
||
|
}
|
||
|
|
||
|
if ( mFifo2 ) // if we belong to fifo2, null us out
|
||
|
{
|
||
|
*mFifo2 = 0;
|
||
|
mFifo2 = 0;
|
||
|
}
|
||
|
|
||
|
if ( mParent ) mParent->LostChild(this);
|
||
|
|
||
|
assert( !mChildren ); // can't unlink guys with children!
|
||
|
|
||
|
mParent = 0; // got no father anymore
|
||
|
}
|
||
|
|
||
|
|
||
|
void AddChild(SpherePack *pack)
|
||
|
{
|
||
|
|
||
|
SpherePack *my_child = mChildren;
|
||
|
mChildren = pack; // new head of list
|
||
|
|
||
|
pack->SetNextSibling(my_child); // his next is my old next
|
||
|
pack->SetPrevSibling(0); // at head of list, no previous
|
||
|
pack->SetParent(this);
|
||
|
|
||
|
if ( my_child ) my_child->SetPrevSibling(pack); // previous now this..
|
||
|
|
||
|
mChildCount++;
|
||
|
|
||
|
#if defined(_DEBUG)
|
||
|
float dist = DistanceSquared(pack);
|
||
|
float radius = sqrtf(dist) + pack->GetRadius();
|
||
|
|
||
|
assert( radius <= GetRadius()+0.0001f );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void SetNextSibling(SpherePack *child) { mNextSibling = child; }
|
||
|
void SetPrevSibling(SpherePack *child) { mPrevSibling = child; }
|
||
|
|
||
|
SpherePack * _GetNextSibling(void) const
|
||
|
{
|
||
|
return mNextSibling;
|
||
|
}
|
||
|
SpherePack * _GetPrevSibling(void) const
|
||
|
{
|
||
|
return mPrevSibling;
|
||
|
}
|
||
|
SpherePack * GetChildren(void) const { return mChildren; }
|
||
|
|
||
|
SpherePack * GetNext(void) const { return mNext; };
|
||
|
SpherePack * GetPrevious(void) const { return mPrevious; };
|
||
|
|
||
|
void SetNext(SpherePack *pack) { mNext = pack; };
|
||
|
void SetPrevious(SpherePack *pack) { mPrevious = pack; };
|
||
|
|
||
|
void * GetUserData(void) const { return mUserData; };
|
||
|
void SetUserData(void *data, bool isSphere) { mUserData = data; IS_SPHERE=isSphere;};
|
||
|
|
||
|
float DistanceSquared(const SpherePack *pack) const { return mCenter.DistanceSq( pack->mCenter ); };
|
||
|
|
||
|
inline void LostChild(SpherePack *pack);
|
||
|
|
||
|
const Vector3d& GetPos(void) const { return mCenter; };
|
||
|
|
||
|
inline void Render(unsigned int color);
|
||
|
|
||
|
inline bool Recompute(float gravy);
|
||
|
|
||
|
int GetChildCount(void) const { return mChildCount; };
|
||
|
|
||
|
#if DEMO
|
||
|
void SetColor(unsigned int color) { mColor = color; };
|
||
|
unsigned int GetColor(void) const { return mColor; };
|
||
|
#endif
|
||
|
|
||
|
void SetFifo1(SpherePack **fifo)
|
||
|
{
|
||
|
mFifo1 = fifo;
|
||
|
};
|
||
|
|
||
|
void SetFifo2(SpherePack **fifo)
|
||
|
{
|
||
|
mFifo2 = fifo;
|
||
|
};
|
||
|
|
||
|
void ComputeBindingDistance(SpherePack *parent)
|
||
|
{
|
||
|
mBindingDistance = parent->GetRadius() - GetRadius();
|
||
|
if ( mBindingDistance <= 0 )
|
||
|
mBindingDistance = 0;
|
||
|
else
|
||
|
mBindingDistance*=mBindingDistance;
|
||
|
}
|
||
|
|
||
|
void VisibilityTest(const Frustum &f,
|
||
|
SpherePackCallback *callback,
|
||
|
ViewState state);
|
||
|
|
||
|
void RayTrace(const Vector3d &p1, // origin of Ray
|
||
|
const Vector3d &dir, // direction of Ray
|
||
|
float distance, // length of ray.
|
||
|
SpherePackCallback *callback);
|
||
|
|
||
|
|
||
|
void RangeTest(const Vector3d &p,
|
||
|
float distance,
|
||
|
SpherePackCallback *callback,
|
||
|
ViewState state);
|
||
|
|
||
|
void PointTest2d(const Vector3d &p,
|
||
|
SpherePackCallback *callback,
|
||
|
ViewState state);
|
||
|
|
||
|
void Reset(void);
|
||
|
|
||
|
private:
|
||
|
SpherePack *mNext;
|
||
|
SpherePack *mPrevious; // used by pool memory management linked list code
|
||
|
|
||
|
SpherePack *mParent;
|
||
|
SpherePack *mChildren; // *my* children
|
||
|
|
||
|
SpherePack *mNextSibling; // doubly linked list of my brothers
|
||
|
SpherePack *mPrevSibling; // and sisters
|
||
|
|
||
|
SpherePack **mFifo1; // address of location inside of fifo1
|
||
|
SpherePack **mFifo2; // address of location inside of fifo2
|
||
|
|
||
|
long mFlags; // my bit flags.
|
||
|
long mChildCount; // number of children
|
||
|
|
||
|
float mBindingDistance;
|
||
|
|
||
|
void *mUserData;
|
||
|
|
||
|
SpherePackFactory *mFactory; // the factory we are a member of.
|
||
|
#if DEMO
|
||
|
unsigned long mColor;
|
||
|
#endif
|
||
|
|
||
|
public:
|
||
|
bool IS_SPHERE;
|
||
|
};
|
||
|
|
||
|
class SpherePackFifo
|
||
|
{
|
||
|
public:
|
||
|
SpherePackFifo(int fifosize)
|
||
|
{
|
||
|
mCount = 0;
|
||
|
mSP = 0;
|
||
|
mBottom = 0;
|
||
|
mFifoSize = fifosize;
|
||
|
mFifo = new SpherePack *[mFifoSize];
|
||
|
};
|
||
|
|
||
|
virtual ~SpherePackFifo(void)
|
||
|
{
|
||
|
delete [] mFifo;
|
||
|
};
|
||
|
|
||
|
SpherePack ** Push(SpherePack *sphere)
|
||
|
{
|
||
|
mCount++;
|
||
|
SpherePack **ret = &mFifo[mSP];
|
||
|
mFifo[mSP] = sphere;
|
||
|
mSP++;
|
||
|
if ( mSP == mFifoSize ) mSP = 0;
|
||
|
return ret;
|
||
|
};
|
||
|
|
||
|
SpherePack * Pop(void)
|
||
|
{
|
||
|
while ( mSP != mBottom )
|
||
|
{
|
||
|
mCount--;
|
||
|
SpherePack *ret = mFifo[mBottom];
|
||
|
mBottom++;
|
||
|
if ( mBottom == mFifoSize ) mBottom = 0;
|
||
|
if ( ret ) return ret;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool Flush(SpherePack *pack)
|
||
|
{
|
||
|
if ( mSP == mBottom ) return false;
|
||
|
int i = mBottom;
|
||
|
while ( i != mSP )
|
||
|
{
|
||
|
if ( mFifo[i] == pack )
|
||
|
{
|
||
|
mFifo[i] = 0;
|
||
|
return true;
|
||
|
}
|
||
|
i++;
|
||
|
if ( i == mFifoSize ) i = 0;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
int GetCount(void) const { return mCount; };
|
||
|
|
||
|
private:
|
||
|
int mCount;
|
||
|
int mSP; // stack pointer
|
||
|
int mBottom;
|
||
|
int mFifoSize;
|
||
|
SpherePack **mFifo;
|
||
|
};
|
||
|
|
||
|
|
||
|
class SpherePackFactory : public SpherePackCallback
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
SpherePackFactory(int maxspheres,
|
||
|
float rootsize,
|
||
|
float leafsize,
|
||
|
float gravy);
|
||
|
|
||
|
|
||
|
virtual ~SpherePackFactory(void);
|
||
|
|
||
|
void Process(void);
|
||
|
|
||
|
SpherePack *AddSphere_(const Vector3d &pos,
|
||
|
float radius,
|
||
|
void *userdata,
|
||
|
bool isSphere,
|
||
|
int flags=SPF_LEAF_TREE);
|
||
|
|
||
|
void AddIntegrate(SpherePack *pack); // add to the integration FIFO
|
||
|
void AddRecompute(SpherePack *recompute); // add to the recomputation (balancing) FIFO.
|
||
|
|
||
|
void Integrate(SpherePack *pack,SpherePack *supersphere,float node_size);
|
||
|
|
||
|
void Render(void);
|
||
|
|
||
|
void Remove(SpherePack *pack);
|
||
|
// see if any other spheres are contained within this one, if so
|
||
|
// collapse them and inherit their children.
|
||
|
#if DEMO
|
||
|
unsigned int GetColor(void);
|
||
|
#endif
|
||
|
|
||
|
void FrustumTest(const Frustum &f,SpherePackCallback *callback);
|
||
|
|
||
|
void RayTrace(const Vector3d &p1, // source
|
||
|
const Vector3d &p2, // dest
|
||
|
SpherePackCallback *callback);
|
||
|
|
||
|
void RangeTest(const Vector3d ¢er,float radius,SpherePackCallback *callback);
|
||
|
void PointTest2d(const Vector3d ¢er, SpherePackCallback *callback);
|
||
|
|
||
|
virtual void RayTraceCallback(const Vector3d &p1, // source pos of ray
|
||
|
const Vector3d &dir, // direction of ray
|
||
|
float distance, // distance of ray
|
||
|
const Vector3d §, // intersection location
|
||
|
SpherePack *sphere);
|
||
|
|
||
|
virtual void RangeTestCallback(const Vector3d &p,float distance,SpherePack *sphere,ViewState state);
|
||
|
virtual void PointTest2dCallback(const Vector3d &p, SpherePack *sphere,ViewState state);
|
||
|
|
||
|
virtual void VisibilityCallback(const Frustum &f,SpherePack *sphere,ViewState state);
|
||
|
|
||
|
|
||
|
void Reset(void);
|
||
|
|
||
|
private:
|
||
|
|
||
|
|
||
|
SpherePack *mRoot; // 1024x1024 root node of all active spheres.
|
||
|
SpherePack *mLeaf; // 1024x1024 root node of all active spheres.
|
||
|
SpherePackCallback *mCallback;
|
||
|
|
||
|
Pool< SpherePack > mSpheres; // all spheres possibly represented.
|
||
|
|
||
|
SpherePackFifo *mIntegrate; // integration fifo
|
||
|
SpherePackFifo *mRecompute; // recomputation fifo
|
||
|
|
||
|
#if DEMO
|
||
|
#define MAXCOLORS 12
|
||
|
int mColorCount;
|
||
|
unsigned int mColors[MAXCOLORS];
|
||
|
#endif
|
||
|
|
||
|
float mMaxRootSize; // maximum size of a root node supersphere
|
||
|
float mMaxLeafSize; // maximum size of the leaf node supersphere
|
||
|
float mSuperSphereGravy; // binding distance gravy.
|
||
|
};
|
||
|
|
||
|
//*** Source code placed in header file so that it will be inlined!
|
||
|
void SpherePack::NewPos(const Vector3d &pos)
|
||
|
{
|
||
|
mCenter = pos; // set our new center position.
|
||
|
|
||
|
// is we have a parent (meaning we are a valid leaf node) and we have not already been flagged for re-integration, then.....
|
||
|
if (mParent && !HasSpherePackFlag(SPF_INTEGRATE))
|
||
|
{
|
||
|
float dist = DistanceSquared(mParent); // compute squared distance to our parent.
|
||
|
|
||
|
if (dist >= mBindingDistance) // if that exceeds our binding distance...
|
||
|
{
|
||
|
// If our parent, is not already marked to be recomputed (rebalance the sphere), then add him to the recomputation fifo.
|
||
|
mFactory->AddRecompute(mParent);
|
||
|
|
||
|
// Unlink ourselves from the parent sphere and place ourselves into the root node.
|
||
|
Unlink();
|
||
|
mFactory->AddIntegrate(this); // add ourselves to the re-integration fifo.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void SpherePack::NewPosRadius(const Vector3d &pos,float radius)
|
||
|
{
|
||
|
// New position and, possibly, a new radius.
|
||
|
mCenter = pos;
|
||
|
|
||
|
if (mParent && !HasSpherePackFlag(SPF_INTEGRATE))
|
||
|
{
|
||
|
if (radius != GetRadius())
|
||
|
{
|
||
|
SetRadius(radius);
|
||
|
ComputeBindingDistance(mParent);
|
||
|
}
|
||
|
|
||
|
mFactory->AddRecompute(mParent);
|
||
|
|
||
|
float dist = DistanceSquared(mParent);
|
||
|
|
||
|
if (dist >= mBindingDistance)
|
||
|
{
|
||
|
Unlink();
|
||
|
mFactory->AddIntegrate(this);
|
||
|
}
|
||
|
}
|
||
|
}
|