/* 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); }