879 lines
20 KiB
C++
879 lines
20 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 "spherepack.h"
|
|
|
|
#if DEMO
|
|
int PrintText(int x, int y, int color, char* output, ...);
|
|
int DrawLine(int x1, int y1, int x2, int y2, int color);
|
|
int DrawCircle(int locx, int locy, int radius, int color);
|
|
#endif
|
|
|
|
SpherePackFactory::SpherePackFactory(int maxspheres, float rootsize, float leafsize, float gravy)
|
|
{
|
|
NANOBEGIN
|
|
maxspheres *= 4; // include room for both trees, the root node and leaf node tree, and the superspheres
|
|
mMaxRootSize = rootsize;
|
|
mMaxLeafSize = leafsize;
|
|
mSuperSphereGravy = gravy;
|
|
mIntegrate = new SpherePackFifo(maxspheres);
|
|
mRecompute = new SpherePackFifo(maxspheres);
|
|
|
|
mSpheres.Set(maxspheres); // init pool to hold all possible SpherePack instances.
|
|
|
|
Vector3d p(0,0,0);
|
|
|
|
mRoot = mSpheres.GetFreeLink(); // initially empty
|
|
mRoot->Init(this,p,6553600,0, false);
|
|
mRoot->SetSpherePackFlag(SpherePackFlag(SPF_SUPERSPHERE | SPF_ROOTNODE | SPF_ROOT_TREE));
|
|
|
|
#if DEMO
|
|
mRoot->SetColor(0x00FFFFFF);
|
|
#endif
|
|
|
|
mLeaf = mSpheres.GetFreeLink();; // initially empty
|
|
mLeaf->Init(this,p,1638400,0,false);
|
|
mLeaf->SetSpherePackFlag(SpherePackFlag(SPF_SUPERSPHERE | SPF_ROOTNODE | SPF_LEAF_TREE));
|
|
|
|
#if DEMO
|
|
mLeaf->SetColor(0x00FFFFFF);
|
|
mColorCount = 0;
|
|
|
|
mColors[0] = 0x00FF0000;
|
|
mColors[1] = 0x0000FF00;
|
|
mColors[2] = 0x000000FF;
|
|
mColors[3] = 0x00FFFF00;
|
|
mColors[4] = 0x00FF00FF;
|
|
mColors[5] = 0x0000FFFF;
|
|
mColors[6] = 0x00FF8080;
|
|
mColors[7] = 0x0000FF80;
|
|
mColors[8] = 0x000080FF;
|
|
mColors[9] = 0x00FFFF80;
|
|
mColors[10] = 0x00FF80FF;
|
|
mColors[11] = 0x0080FFFF;
|
|
|
|
#endif
|
|
NANOEND
|
|
}
|
|
|
|
SpherePackFactory::~SpherePackFactory(void)
|
|
{
|
|
delete mIntegrate; // free up integration fifo
|
|
delete mRecompute; // free up recomputation fifo.
|
|
}
|
|
|
|
void SpherePackFactory::Process(void)
|
|
{
|
|
{
|
|
// First recompute anybody that needs to be recomputed!!
|
|
// When leaf node spheres exit their parent sphere, then the parent sphere needs to be rebalanced. In fact,it may now be empty and
|
|
// need to be removed.
|
|
// This is the location where (n) number of spheres in the recomputation FIFO are allowed to be rebalanced in the tree.
|
|
int maxrecompute = mRecompute->GetCount();
|
|
for (int i = 0; i < maxrecompute; ++i)
|
|
{
|
|
SpherePack * pack = mRecompute->Pop();
|
|
if (!pack) break;
|
|
pack->SetFifo1(0); // no longer on the fifo!!
|
|
bool kill = pack->Recompute(mSuperSphereGravy);
|
|
if (kill) Remove(pack);
|
|
}
|
|
}
|
|
|
|
{
|
|
// Now, process the integration step.
|
|
int maxintegrate = mIntegrate->GetCount();
|
|
|
|
for (int i = 0; i < maxintegrate; ++i)
|
|
{
|
|
SpherePack * pack = mIntegrate->Pop();
|
|
if (!pack)
|
|
break;
|
|
pack->SetFifo2(0);
|
|
|
|
if (pack->HasSpherePackFlag(SPF_ROOT_TREE))
|
|
Integrate(pack,mRoot,mMaxRootSize); // integrate this one single dude against the root node.
|
|
else
|
|
Integrate(pack,mLeaf,mMaxLeafSize); // integrate this one single dude against the root node.
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SpherePack * SpherePackFactory::AddSphere_(const Vector3d &pos,
|
|
float radius,
|
|
void *userdata,
|
|
bool isSphere,
|
|
int flags)
|
|
{
|
|
|
|
SpherePack *pack = mSpheres.GetFreeLink();
|
|
|
|
assert(pack);
|
|
|
|
if (pack)
|
|
{
|
|
if (flags & SPF_ROOT_TREE)
|
|
{
|
|
pack->Init(this,pos,radius,userdata, isSphere);
|
|
pack->SetSpherePackFlag(SPF_ROOT_TREE); // member of the leaf node tree!
|
|
AddIntegrate(pack); // add to integration list.
|
|
}
|
|
else
|
|
{
|
|
pack->Init(this,pos,radius,userdata, isSphere);
|
|
pack->SetSpherePackFlag(SPF_LEAF_TREE); // member of the leaf node tree!
|
|
AddIntegrate(pack); // add to integration list.
|
|
}
|
|
}
|
|
|
|
return pack;
|
|
}
|
|
|
|
void SpherePackFactory::AddIntegrate(SpherePack *pack)
|
|
{
|
|
if (pack->HasSpherePackFlag(SPF_ROOT_TREE))
|
|
mRoot->AddChild(pack);
|
|
else
|
|
mLeaf->AddChild(pack);
|
|
|
|
pack->SetSpherePackFlag(SPF_INTEGRATE); // still needs to be integrated!
|
|
SpherePack **fifo = mIntegrate->Push(pack); // add it to the integration stack.
|
|
pack->SetFifo2(fifo);
|
|
}
|
|
|
|
void SpherePackFactory::AddRecompute(SpherePack *recompute)
|
|
{
|
|
if (!recompute->HasSpherePackFlag(SPF_RECOMPUTE))
|
|
{
|
|
if (recompute->GetChildCount())
|
|
{
|
|
recompute->SetSpherePackFlag(SPF_RECOMPUTE); // needs to be recalculated!
|
|
SpherePack **fifo = mRecompute->Push(recompute);
|
|
recompute->SetFifo1(fifo);
|
|
}
|
|
else
|
|
{
|
|
Remove(recompute);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SpherePackFactory::Render(void)
|
|
{
|
|
#if DEMO
|
|
mRoot->Render(mRoot->GetColor());
|
|
mLeaf->Render(mLeaf->GetColor());
|
|
#endif
|
|
}
|
|
|
|
|
|
void SpherePack::Render(unsigned int /*color*/)
|
|
{
|
|
#if DEMO
|
|
if (!HasSpherePackFlag(SPF_ROOTNODE))
|
|
{
|
|
|
|
if (HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
color = mColor;
|
|
}
|
|
else
|
|
{
|
|
if (mParent->HasSpherePackFlag(SPF_ROOTNODE)) color = 0x00FFFFFF;
|
|
}
|
|
#if DEMO
|
|
DrawCircle(int(mCenter.x), int(mCenter.y),int(GetRadius()),color);
|
|
#endif
|
|
if (HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
if (HasSpherePackFlag(SPF_LEAF_TREE))
|
|
{
|
|
|
|
#if DEMO
|
|
DrawCircle(int(mCenter.x), int(mCenter.y),int(GetRadius()),color);
|
|
#endif
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!sphere->IS_SPHERE)
|
|
puts("SpherePack::Render");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) GetUserData();
|
|
|
|
link = link->GetParent();
|
|
|
|
if (link && !link->HasSpherePackFlag(SPF_ROOTNODE))
|
|
{
|
|
DrawLine(int(mCenter.x), int(mCenter.y),
|
|
int(link->mCenter.x), int(link->mCenter.y),
|
|
link->GetColor());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if DEMO
|
|
DrawCircle(int(mCenter.x), int(mCenter.y),int(GetRadius())+3,color);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mChildren)
|
|
{
|
|
SpherePack *pack = mChildren;
|
|
|
|
while (pack)
|
|
{
|
|
pack->Render(color);
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool SpherePack::Recompute(float gravy)
|
|
{
|
|
if (!mChildren) return true; // kill it!
|
|
if (HasSpherePackFlag(SPF_ROOTNODE)) return false; // don't recompute root nodes!
|
|
|
|
#if 1
|
|
// recompute bounding sphere!
|
|
Vector3d total(0,0,0);
|
|
int count=0;
|
|
SpherePack *pack = mChildren;
|
|
while (pack)
|
|
{
|
|
total+=pack->mCenter;
|
|
count++;
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
|
|
if (count)
|
|
{
|
|
float recip = 1.0f / float(count);
|
|
total*=recip;
|
|
|
|
Vector3d oldpos = mCenter;
|
|
|
|
#ifdef __STATIC_RANGE__
|
|
assert(total.IsInStaticRange());
|
|
#endif
|
|
mCenter = total; // new origin!
|
|
float maxradius = 0;
|
|
|
|
pack = mChildren;
|
|
|
|
while (pack)
|
|
{
|
|
float dist = DistanceSquared(pack);
|
|
float radius = sqrtf(dist) + pack->GetRadius();
|
|
if (radius > maxradius)
|
|
{
|
|
maxradius = radius;
|
|
if ((maxradius+gravy) >= GetRadius())
|
|
{
|
|
#ifdef __STATIC_RANGE__
|
|
assert(oldpos.IsInStaticRange());
|
|
#endif
|
|
mCenter = oldpos;
|
|
ClearSpherePackFlag(SPF_RECOMPUTE);
|
|
return false;
|
|
}
|
|
}
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
|
|
maxradius+=gravy;
|
|
|
|
SetRadius(maxradius);
|
|
|
|
// now all children have to recompute binding distance!!
|
|
pack = mChildren;
|
|
|
|
while (pack)
|
|
{
|
|
pack->ComputeBindingDistance(this);
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
ClearSpherePackFlag(SPF_RECOMPUTE);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void SpherePack::LostChild(SpherePack *t)
|
|
{
|
|
assert(mChildCount);
|
|
assert(mChildren);
|
|
|
|
#ifdef _DEBUG // debug validation code.
|
|
|
|
SpherePack *pack = mChildren;
|
|
bool found = false;
|
|
while (pack)
|
|
{
|
|
if (pack == t)
|
|
{
|
|
assert(!found);
|
|
found = true;
|
|
}
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
assert(found);
|
|
|
|
#endif
|
|
|
|
// first patch old linked list.. his previous now points to his next
|
|
SpherePack *prev = t->_GetPrevSibling();
|
|
|
|
if (prev)
|
|
{
|
|
SpherePack *next = t->_GetNextSibling();
|
|
prev->SetNextSibling(next); // my previous now points to my next
|
|
if (next) next->SetPrevSibling(prev);
|
|
// list is patched!
|
|
}
|
|
else
|
|
{
|
|
SpherePack *next = t->_GetNextSibling();
|
|
mChildren = next;
|
|
if (mChildren) mChildren->SetPrevSibling(0);
|
|
}
|
|
|
|
mChildCount--;
|
|
|
|
if (!mChildCount && HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
mFactory->Remove(this);
|
|
}
|
|
}
|
|
|
|
void SpherePackFactory::Remove(SpherePack*pack)
|
|
{
|
|
|
|
if (pack->HasSpherePackFlag(SPF_ROOTNODE)) return; // CAN NEVER REMOVE THE ROOT NODE EVER!!!
|
|
|
|
if (pack->HasSpherePackFlag(SPF_SUPERSPHERE) && pack->HasSpherePackFlag(SPF_LEAF_TREE))
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!pack->IS_SPHERE)
|
|
puts("SpherePackFactory::Remove");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) pack->GetUserData();
|
|
|
|
Remove(link);
|
|
}
|
|
|
|
pack->Unlink();
|
|
|
|
mSpheres.Release(pack);
|
|
}
|
|
|
|
#if DEMO
|
|
unsigned int SpherePackFactory::GetColor(void)
|
|
{
|
|
unsigned int ret = mColors[mColorCount];
|
|
mColorCount++;
|
|
if (mColorCount == MAXCOLORS) mColorCount = 0;
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
void SpherePackFactory::Integrate(SpherePack *pack,
|
|
SpherePack *supersphere,
|
|
float node_size)
|
|
{
|
|
// ok..time to integrate this sphere with the tree
|
|
// first find which supersphere we are closest to the center of
|
|
|
|
SpherePack *search = supersphere->GetChildren();
|
|
|
|
SpherePack *nearest1 = 0; // nearest supersphere we are completely
|
|
float neardist1 = 1e38f; // enclosed within
|
|
|
|
SpherePack *nearest2 = 0; // supersphere we must grow the least to
|
|
float neardist2 = 1e38f; // add ourselves to.
|
|
|
|
//int scount = 1;
|
|
|
|
while (search)
|
|
{
|
|
if (search->HasSpherePackFlag(SPF_SUPERSPHERE) && !search->HasSpherePackFlag(SPF_ROOTNODE) && search->GetChildCount())
|
|
{
|
|
|
|
float dist = pack->DistanceSquared(search);
|
|
|
|
if (nearest1)
|
|
{
|
|
if (dist < neardist1)
|
|
{
|
|
|
|
float d = sqrtf(dist)+pack->GetRadius();
|
|
|
|
if (d <= search->GetRadius())
|
|
{
|
|
neardist1 = dist;
|
|
nearest1 = search;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
float d = (sqrtf(dist) + pack->GetRadius())-search->GetRadius();
|
|
|
|
if (d < neardist2)
|
|
{
|
|
if (d < 0)
|
|
{
|
|
neardist1 = dist;
|
|
nearest1 = search;
|
|
}
|
|
else
|
|
{
|
|
neardist2 = d;
|
|
nearest2 = search;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
search = search->_GetNextSibling();
|
|
}
|
|
|
|
// ok...now..on exit let's see what we got.
|
|
if (nearest1)
|
|
{
|
|
// if we are inside an existing supersphere, we are all good!
|
|
// we need to detach item from wherever it is, and then add it to
|
|
// this supersphere as a child.
|
|
pack->Unlink();
|
|
nearest1->AddChild(pack);
|
|
pack->ComputeBindingDistance(nearest1);
|
|
nearest1->Recompute(mSuperSphereGravy);
|
|
|
|
if (nearest1->HasSpherePackFlag(SPF_LEAF_TREE))
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!nearest1->IS_SPHERE)
|
|
puts("SpherePackFactory::Integrate1");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) nearest1->GetUserData();
|
|
link->NewPosRadius(nearest1->GetPos(), nearest1->GetRadius());
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
bool newsphere = true;
|
|
|
|
if (nearest2)
|
|
{
|
|
float newsize = neardist2 + nearest2->GetRadius() + mSuperSphereGravy;
|
|
|
|
if (newsize <= node_size)
|
|
{
|
|
pack->Unlink();
|
|
|
|
nearest2->SetRadius(newsize);
|
|
nearest2->AddChild(pack);
|
|
nearest2->Recompute(mSuperSphereGravy);
|
|
pack->ComputeBindingDistance(nearest2);
|
|
|
|
if (nearest2->HasSpherePackFlag(SPF_LEAF_TREE))
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!nearest2->IS_SPHERE)
|
|
puts("SpherePackFactory::Integrate2");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) nearest2->GetUserData();
|
|
link->NewPosRadius(nearest2->GetPos(), nearest2->GetRadius());
|
|
}
|
|
|
|
newsphere = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (newsphere)
|
|
{
|
|
assert(supersphere->HasSpherePackFlag(SPF_ROOTNODE));
|
|
// we are going to create a new superesphere around this guy!
|
|
pack->Unlink();
|
|
|
|
SpherePack *parent = mSpheres.GetFreeLink();
|
|
assert(parent);
|
|
parent->Init(this, pack->GetPos(), pack->GetRadius()+mSuperSphereGravy, 0, false);
|
|
|
|
if (supersphere->HasSpherePackFlag(SPF_ROOT_TREE))
|
|
parent->SetSpherePackFlag(SPF_ROOT_TREE);
|
|
else
|
|
parent->SetSpherePackFlag(SPF_LEAF_TREE);
|
|
|
|
parent->SetSpherePackFlag(SPF_SUPERSPHERE);
|
|
#if DEMO
|
|
parent->SetColor(GetColor());
|
|
#endif
|
|
parent->AddChild(pack);
|
|
|
|
supersphere->AddChild(parent);
|
|
|
|
parent->Recompute(mSuperSphereGravy);
|
|
pack->ComputeBindingDistance(parent);
|
|
|
|
if (parent->HasSpherePackFlag(SPF_LEAF_TREE))
|
|
{
|
|
// need to create parent association!
|
|
SpherePack *link = AddSphere_(parent->GetPos(), parent->GetRadius(), parent, true, SPF_ROOT_TREE);
|
|
parent->SetUserData(link, true); // hook him up!!
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
pack->ClearSpherePackFlag(SPF_INTEGRATE); // we've been integrated!
|
|
}
|
|
|
|
|
|
void SpherePackFactory::FrustumTest(const Frustum &f,SpherePackCallback *callback)
|
|
{
|
|
// test case here, just traverse children.
|
|
mCallback = callback;
|
|
mRoot->VisibilityTest(f,this,VS_PARTIAL);
|
|
}
|
|
|
|
|
|
void SpherePack::VisibilityTest(const Frustum &f,SpherePackCallback *callback,ViewState state)
|
|
{
|
|
|
|
if (state == VS_PARTIAL)
|
|
{
|
|
state = f.ViewVolumeTest(mCenter, GetRadius());
|
|
#if DEMO
|
|
if (state != VS_OUTSIDE)
|
|
{
|
|
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
|
|
|
|
if (state == VS_OUTSIDE)
|
|
{
|
|
if (HasSpherePackFlag(SPF_HIDDEN)) return; // no state change
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_INSIDE | SPF_PARTIAL));
|
|
SetSpherePackFlag(SPF_HIDDEN);
|
|
}
|
|
else
|
|
{
|
|
if (state == VS_INSIDE)
|
|
{
|
|
if (HasSpherePackFlag(SPF_INSIDE)) return; // no state change
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_PARTIAL | SPF_HIDDEN));
|
|
SetSpherePackFlag(SPF_INSIDE);
|
|
}
|
|
else
|
|
{
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_HIDDEN | SPF_INSIDE));
|
|
SetSpherePackFlag(SPF_PARTIAL);
|
|
}
|
|
}
|
|
|
|
SpherePack *pack = mChildren;
|
|
|
|
while (pack)
|
|
{
|
|
pack->VisibilityTest(f,callback,state);
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
switch (state)
|
|
{
|
|
case VS_INSIDE:
|
|
if (!HasSpherePackFlag(SPF_INSIDE))
|
|
{
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_HIDDEN | SPF_PARTIAL));
|
|
SetSpherePackFlag(SPF_INSIDE);
|
|
callback->VisibilityCallback(f,this,state);
|
|
}
|
|
break;
|
|
case VS_OUTSIDE:
|
|
if (!HasSpherePackFlag(SPF_HIDDEN))
|
|
{
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_INSIDE | SPF_PARTIAL));
|
|
SetSpherePackFlag(SPF_HIDDEN);
|
|
callback->VisibilityCallback(f,this,state);
|
|
}
|
|
break;
|
|
case VS_PARTIAL:
|
|
//if (!HasSpherePackFlag(SPF_PARTIAL))
|
|
{
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_INSIDE | SPF_HIDDEN));
|
|
SetSpherePackFlag(SPF_PARTIAL);
|
|
callback->VisibilityCallback(f,this,state);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void SpherePackFactory::RayTrace(const Vector3d &p1,
|
|
const Vector3d &p2,
|
|
SpherePackCallback *callback)
|
|
{
|
|
// test case here, just traverse children.
|
|
Vector3d dir = p2;
|
|
float dist = dir.Normalize();
|
|
mCallback = callback;
|
|
mRoot->RayTrace(p1,dir,dist,this);
|
|
}
|
|
|
|
#include "../EterBase/Debug.h"
|
|
|
|
void SpherePackFactory::RangeTest(const Vector3d ¢er,float radius,SpherePackCallback *callback)
|
|
{
|
|
#ifdef __STATIC_RANGE__
|
|
if (!center.IsInStaticRange())
|
|
{
|
|
TraceError("SpherePackFactory::RangeTest - RANGE ERROR %f, %f, %f",
|
|
center.x, center.y, center.z);
|
|
assert("SpherePackFactory::RangeTest - RANGE ERROR");
|
|
return;
|
|
}
|
|
#endif
|
|
mCallback = callback;
|
|
mRoot->RangeTest(center,radius,this,VS_PARTIAL);
|
|
}
|
|
|
|
void SpherePackFactory::PointTest2d(const Vector3d ¢er, SpherePackCallback *callback)
|
|
{
|
|
#ifdef __STATIC_RANGE__
|
|
if (!center.IsInStaticRange())
|
|
{
|
|
TraceError("SpherePackFactory::RangeTest2d - RANGE ERROR %f, %f, %f",
|
|
center.x, center.y, center.z);
|
|
assert("SpherePackFactory::RangeTest2d - RANGE ERROR");
|
|
return;
|
|
}
|
|
#endif
|
|
mCallback = callback;
|
|
|
|
#ifdef SPHERELIB_STRICT
|
|
mRoot->PointTest2d(center, this,VS_PARTIAL);
|
|
extern bool MAPOUTDOOR_GET_HEIGHT_TRACE;
|
|
if (MAPOUTDOOR_GET_HEIGHT_TRACE)
|
|
puts("================================================");
|
|
#else
|
|
mRoot->PointTest2d(center, this,VS_PARTIAL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
void SpherePack::RangeTest(const Vector3d &p,
|
|
float distance,
|
|
SpherePackCallback *callback,
|
|
ViewState state)
|
|
{
|
|
|
|
if (state == VS_PARTIAL)
|
|
{
|
|
float d = p.Distance(mCenter);
|
|
if ((d-distance) > GetRadius()) return;;
|
|
if ((GetRadius()+d) < distance) state = VS_INSIDE;
|
|
}
|
|
|
|
if (HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
#if DEMO
|
|
if (state == VS_PARTIAL)
|
|
{
|
|
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
|
|
}
|
|
#endif
|
|
SpherePack *pack = mChildren;
|
|
while (pack)
|
|
{
|
|
pack->RangeTest(p,distance,callback,state);
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
callback->RangeTestCallback(p,distance,this,state);
|
|
}
|
|
}
|
|
|
|
void SpherePack::PointTest2d(const Vector3d &p,
|
|
SpherePackCallback *callback,
|
|
ViewState state)
|
|
{
|
|
if (state == VS_PARTIAL)
|
|
{
|
|
float dx=p.x-mCenter.x;
|
|
float dy=p.y-mCenter.y;
|
|
float distSquare = (dx*dx)+(dy*dy);
|
|
|
|
if (distSquare > GetRadius2()) return;;
|
|
if (GetRadius2() < -distSquare) state = VS_INSIDE;
|
|
}
|
|
|
|
if (HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
#if DEMO
|
|
if (state == VS_PARTIAL)
|
|
{
|
|
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
|
|
}
|
|
#endif
|
|
SpherePack *pack = mChildren;
|
|
while (pack)
|
|
{
|
|
pack->PointTest2d(p, callback, state);
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
extern bool MAPOUTDOOR_GET_HEIGHT_TRACE;
|
|
if (MAPOUTDOOR_GET_HEIGHT_TRACE)
|
|
{
|
|
float dx=p.x-mCenter.x;
|
|
float dy=p.y-mCenter.y;
|
|
float distSquare = (dx*dx)+(dy*dy);
|
|
printf("--- (%f, %f) dist %f radius %f isSphere %d\n", mCenter.x, mCenter.y, distSquare, GetRadius(), IS_SPHERE);
|
|
}
|
|
#endif
|
|
callback->PointTest2dCallback(p, this, state);
|
|
}
|
|
}
|
|
|
|
void SpherePackFactory::RangeTestCallback(const Vector3d &p,float distance,SpherePack *sphere,ViewState state)
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!sphere->IS_SPHERE)
|
|
puts("SpherePackFactory::RangeTestCallback");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) sphere->GetUserData();
|
|
if (link) link->RangeTest(p,distance,mCallback,state);
|
|
};
|
|
|
|
void SpherePackFactory::PointTest2dCallback(const Vector3d &p, SpherePack *sphere,ViewState state)
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!sphere->IS_SPHERE)
|
|
puts("SpherePackFactory::PointTest2dCallback");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) sphere->GetUserData();
|
|
if (link) link->PointTest2d(p, mCallback,state);
|
|
};
|
|
|
|
void SpherePack::RayTrace(const Vector3d &p1,
|
|
const Vector3d &dir,
|
|
float distance,
|
|
SpherePackCallback *callback)
|
|
{
|
|
bool hit = false;
|
|
|
|
if (HasSpherePackFlag(SPF_SUPERSPHERE))
|
|
{
|
|
|
|
hit = RayIntersectionInFront(p1,dir,0);
|
|
|
|
if (hit)
|
|
{
|
|
#if DEMO
|
|
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
|
|
#endif
|
|
SpherePack *pack = mChildren;
|
|
|
|
while (pack)
|
|
{
|
|
pack->RayTrace(p1,dir,distance,callback);
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Vector3d sect;
|
|
hit = RayIntersection(p1,dir,distance,§);
|
|
if (hit)
|
|
{
|
|
callback->RayTraceCallback(p1,dir,distance,sect,this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SpherePackFactory::RayTraceCallback(const Vector3d &p1, // source pos of ray
|
|
const Vector3d &dir, // direction of ray
|
|
float distance, // distance of ray
|
|
const Vector3d &/*sect*/, // intersection location
|
|
SpherePack *sphere)
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!sphere->IS_SPHERE)
|
|
puts("SpherePackFactory::RayTraceCallback");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) sphere->GetUserData();
|
|
if (link) link->RayTrace(p1,dir,distance,mCallback);
|
|
};
|
|
|
|
|
|
|
|
|
|
void SpherePackFactory::Reset(void)
|
|
{
|
|
mRoot->Reset();
|
|
mLeaf->Reset();
|
|
}
|
|
|
|
|
|
void SpherePack::Reset(void)
|
|
{
|
|
ClearSpherePackFlag(SpherePackFlag(SPF_HIDDEN | SPF_PARTIAL | SPF_INSIDE));
|
|
|
|
SpherePack *pack = mChildren;
|
|
while (pack)
|
|
{
|
|
pack->Reset();
|
|
pack = pack->_GetNextSibling();
|
|
}
|
|
}
|
|
|
|
void SpherePackFactory::VisibilityCallback(const Frustum &f,SpherePack *sphere,ViewState state)
|
|
{
|
|
#ifdef SPHERELIB_STRICT
|
|
if (!sphere->IS_SPHERE)
|
|
puts("SpherePackFactory::VisibilityCallback");
|
|
#endif
|
|
SpherePack *link = (SpherePack *) sphere->GetUserData();
|
|
if (link) link->VisibilityTest(f,mCallback,state);
|
|
}
|