/*========================================================================== * (C) 2006 Universitat Jaume I *========================================================================== * PROYECT: GAME TOOLS *========================================================================== * CONTENT: * * * @file GeoLodManager.cpp *===========================================================================*/ #include "GeoLodManager.h" #include "GeoLodObject.h" #include "VertexData.h" #include #include using namespace Geometry; //#define MINIMUM_VALID_LOD_SIMILARITY 0.1f namespace Geometry { class lodobj_node_t : public Moveable { public: lodobj_node_t(const std::string &nam, float dist, LodObject *o):dist2cam(dist),lodobj(o),name(nam){ must_recalculate_distance=false; to_update=0; master=NULL; lodfactor=o->GetCurrentLodFactor(); precalpos=-1; } lodobj_node_t(const lodobj_node_t &n){ operator=(n); } float dist2cam; LodObject *lodobj; bool must_recalculate_distance; uint32 to_update; bool operator < (const lodobj_node_t &o) const { return (dist2cam < o.dist2cam); } void operator = (const lodobj_node_t &n){ dist2cam=n.dist2cam; lodobj=n.lodobj; must_recalculate_distance=n.must_recalculate_distance; master=n.master; lodfactor=n.lodfactor; precalpos=-1; name=n.name; } lodobj_node_t * master; VectorMap disciples; float lodfactor; // virtual lod factor (the lodfactor it seems, not its real internal lodfactor) int precalpos; std::string name; }; class NodeContainer { public: Vector lodobj_dists; std::map lodobj_pos; // map key: LodObject UniqueID for efficient ID-based finding Vector3 camera_pos; int num_lod_sim; NodeContainer(const Geometry::Vector3 &campos) :camera_pos(campos){ } ~NodeContainer(void){ for (VectorMap::Iterator it = precalculated_lods.Begin(); it != precalculated_lods.End(); it ++) { lodobj_node_t **nodes = *it; delete[] nodes; } } void RecalculateDist(lodobj_node_t & node){ std::map::iterator posit = lodobj_pos.find(node.lodobj->GetUniqueID()); assert(posit!=lodobj_pos.end()); const Vector3 & pos = posit->second; node.dist2cam = CalculateDistToCamera(pos); } float CalculateDistToCamera(const Geometry::Vector3 &p) const { Geometry::Vector3 v = camera_pos - p; float distquad = v.length(); return distquad; } lodobj_node_t * & GetPrecalculatedLOD(const std::string &name, int ipos){ int foundpos = precalculated_lods.Find(name); if (foundpos==-1) { lodobj_node_t **new_precalculated_lods=new lodobj_node_t*[num_lod_sim+1]; // +1 is done to include the highest lod (1.0) for (int i=0; i precalculated_lods; }; } LodManager::LodManager(Real near, Real far, const Geometry::Vector3 &campos, int numslots) :lasttime(0),camera_changed(true),node_added(false), near_range(near),far_range(far), always_calculate_lod(false),force_highest_lod(false) { lodobj_container=new NodeContainer(campos); lodobj_container->num_lod_sim = numslots; } LodManager::~LodManager(void) { delete lodobj_container; // delete[] precalculated_lods; } void Geometry::LodManager::AddLodObj(const std::string &name, LodObject * o, const Geometry::Vector3 &p) { float dist = lodobj_container->CalculateDistToCamera(p); lodobj_node_t auxnode(name,dist,o); lodobj_container->lodobj_dists.Add(auxnode); Sort(lodobj_container->lodobj_dists); UpdateLODObjectPos(o,p); node_added=true; } void Geometry::LodManager::UpdateLOD(uint32 currFPS, uint32 targetFPS) { if (!lodobj_container->lodobj_dists.IsEmpty()) { // update distances to the camera if (camera_changed || node_added) { bool must_resort = false; for (Vector::Iterator it=lodobj_container->lodobj_dists.Begin(); it!=lodobj_container->lodobj_dists.End(); it++) { lodobj_node_t & node = *it; if (node.must_recalculate_distance || camera_changed || node_added) { lodobj_container->RecalculateDist(node); must_resort=true; } } /* if (must_resort) Sort(lodobj_container->lodobj_dists);*/ } // the end of the list is the greater element (greater dist) /* if (currFPS < targetFPS - 5) { Vector::Iterator selnode; for (selnode=lodobj_container->lodobj_dists.End()-1; selnode!=lodobj_container->lodobj_dists.Begin()-1; selnode--) { // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor if (selnode->lodobj->GetCurrentLodFactor()>0.00001f) { if (selnode->to_update==0) break; else selnode->to_update--; } } if (selnode!=lodobj_container->lodobj_dists.Begin()-1) { // EL DECREMENTO O EL NUMERO DE DECREMENTOS TIENE QUE VENIR EN FUNCION DE LA DIFERENCIA DE FPS // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor selnode->lodobj->GoToLod(selnode->lodobj->GetCurrentLodFactor()-0.02f); selnode->to_update=10; // ESTO DEBE ASIGNARSE BASADO EN LA DISTANCIA A LA CAMARA! } } if (currFPS > targetFPS + 5) { Vector::Iterator selnode; for (selnode=lodobj_container->lodobj_dists.Begin(); selnode!=lodobj_container->lodobj_dists.End(); selnode++) { // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor if (selnode->lodobj->GetCurrentLodFactor()<0.99999f) { if (selnode->to_update==0) break; else selnode->to_update--; } } if (selnode!=lodobj_container->lodobj_dists.End()) { // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor selnode->lodobj->GoToLod(selnode->lodobj->GetCurrentLodFactor()+0.02f); selnode->to_update=10; // ESTO DEBE ASIGNARSE BASADO EN LA DISTANCIA A LA CAMARA! } }*/ if (camera_changed) { // por ahora solo sacar el lod basado en la camara for (Vector::Iterator it = lodobj_container->lodobj_dists.Begin(); it != lodobj_container->lodobj_dists.End(); it ++) { float lodfactor = - ((it->dist2cam - near_range) / (far_range - near_range)); if (lodfactor < 0.0f) lodfactor = 0.0f; if (lodfactor > 1.0f) lodfactor = 1.0f; if (force_highest_lod) lodfactor = 1.0f; ChangeLOD(it,lodfactor); } } } camera_changed=false; node_added=false; } void Geometry::LodManager::ChangeLOD(lodobj_node_t *lodobjnode, Real lodfactor) { LodObject *lodobj = lodobjnode->lodobj; if (always_calculate_lod) { lodobj->GoToLod(lodfactor); return; } // if the lodfactor is exactly 1.0 its level of detail must be always regenerated (for closeups) if (lodfactor<1.0f) { // if the desired lod is very similar to the previous one, // then there is no need to change the level of detail at all // float sim = fabsf(lodfactor - lodobjnode->lodfactor); // if (sim <= MINIMUM_VALID_LOD_SIMILARITY) // return; // do not change anything if the lod is very similar! int desiredpos = lodobj_container->lod2precalpos(lodfactor); int mypos = lodobj_container->lod2precalpos(lodobjnode->lodfactor); if (mypos==desiredpos) return; } lodobj_node_t *found_node = FindMostSimilarRealLOD(lodobjnode->name,lodfactor); if (found_node && found_node->lodobj->GetUniqueID()!=lodobj->GetUniqueID()) { // DEBUG assert(found_node->master==NULL); // float sim = fabsf(lodfactor - found_node->lodfactor); // if (sim <= MINIMUM_VALID_LOD_SIMILARITY) int desiredpos = lodobj_container->lod2precalpos(lodfactor); int foundpos = lodobj_container->lod2precalpos(found_node->lodfactor); if (foundpos==desiredpos) { lodobj->GetIndexDataInterface()->BorrowIndexData(found_node->lodobj->GetIndexDataInterface()); MakeDiscipleOf(found_node,lodobjnode); } else ReallyGoToLOD(lodobjnode,lodfactor); } else ReallyGoToLOD(lodobjnode,lodfactor); lodobjnode->lodfactor = lodfactor; } void Geometry::LodManager::DeleteMeFromMyMastersDiscipleList(lodobj_node_t *lodobjnode) { // if I have a master... if (lodobjnode->master) lodobjnode->master->disciples.RemoveKey(lodobjnode->lodobj->GetUniqueID()); } void Geometry::LodManager::MakeIndependent(lodobj_node_t *lodobjnode) { DeleteMeFromMyMastersDiscipleList(lodobjnode); // next, delete my master lodobjnode->master=NULL; } void Geometry::LodManager::MakeDiscipleOf(lodobj_node_t *master_node, lodobj_node_t *disciple_node) { ChangeMaster(disciple_node, master_node); // delete my disciples disciple_node->disciples.Clear(); // first remove me from my master's disciple list DeleteMeFromMyMastersDiscipleList(disciple_node); master_node->disciples.GetAdd(disciple_node->lodobj->GetUniqueID()) = disciple_node; // set master disciple_node->master = master_node; // no one must take me as a master! if (disciple_node->precalpos!=-1) { // lodobj_container->precalculated_lods[disciple_node->precalpos] = NULL; lodobj_container->GetPrecalculatedLOD(master_node->name,disciple_node->precalpos) = NULL; disciple_node->precalpos = -1; } } void Geometry::LodManager::ChangeMaster(lodobj_node_t *lodobj, lodobj_node_t *newmaster) { // delete me from my disciples' master list for (VectorMap::Iterator it = lodobj->disciples.Begin(); it != lodobj->disciples.End(); it++) { lodobj_node_t * auxnode = *it; assert(auxnode->master->lodobj->GetUniqueID() == lodobj->lodobj->GetUniqueID()); // auxnode->master = NULL; // chanbge the master to the new one auxnode->master = newmaster; // add the new disciple to the new master if (newmaster) newmaster->disciples.GetAdd(auxnode->lodobj->GetUniqueID()) = auxnode; } } void Geometry::LodManager::UpdateDisciples(lodobj_node_t *master_node) { for (VectorMap::Iterator it= master_node->disciples.Begin(); it!= master_node->disciples.End(); it++) { lodobj_node_t *disciple_node = *it; disciple_node->lodobj->GetIndexDataInterface()->BorrowIndexData(master_node->lodobj->GetIndexDataInterface()); disciple_node->lodfactor = master_node->lodobj->GetCurrentLodFactor(); // we can use currentlodfactor here because the master is supposed to be at its REAL level of detail } } void Geometry::LodManager::ReallyGoToLOD(lodobj_node_t *lodobjnode, Real lodfactor) { lodobjnode->lodobj->GoToLod(lodfactor); MakeIndependent(lodobjnode); UpdateDisciples(lodobjnode); // store the precalculated lod in the table int i = lodobj_container->lod2precalpos(lodfactor); assert(i>=0 && i<=lodobj_container->num_lod_sim); // <= por lo del +1 if (lodobjnode->precalpos != -1) //lodobj_container->precalculated_lods[lodobjnode->precalpos] = NULL; lodobj_container->GetPrecalculatedLOD(lodobjnode->name,lodobjnode->precalpos) = NULL; // lodobj_container->precalculated_lods[i] = lodobjnode; lodobj_container->GetPrecalculatedLOD(lodobjnode->name,i) = lodobjnode; lodobjnode->precalpos = i; } lodobj_node_t * Geometry::LodManager::FindMostSimilarRealLOD(const std::string &name, Real desired_lod) { uint32 i = lodobj_container->lod2precalpos(desired_lod); assert(i>=0 && i<=lodobj_container->num_lod_sim); // <= por lo del +1 // lodobj_node_t *obj = lodobj_container->precalculated_lods[i]; lodobj_node_t *obj = lodobj_container->GetPrecalculatedLOD(name,i); return obj; } void Geometry::LodManager::UpdateCamera(const Geometry::Vector3 &p) { lodobj_container->camera_pos = p; camera_changed = true; } void Geometry::LodManager::UpdateLODObjectPos(LodObject *o, const Geometry::Vector3 &p) { lodobj_container->lodobj_pos[o->GetUniqueID()] = p; } float Geometry::LodManager::SelectRandomDistance(void) const { float linear_01 = (float)rand()/(float)RAND_MAX; float exp_01 = linear_01*linear_01; float real_dist = exp_01*near_range + (1.0f-exp_01)*far_range; return real_dist; }