source: GTP/trunk/Lib/Geom/shared/GTGeometry/src/GeoLodManager.cpp @ 2470

Revision 2470, 12.7 KB checked in by gumbau, 17 years ago (diff)
Line 
1/*==========================================================================
2 *      (C) 2006 Universitat Jaume I
3 *==========================================================================
4 *      PROYECT:        GAME TOOLS
5 *==========================================================================
6 *      CONTENT:
7 *
8 *
9 *      @file   GeoLodManager.cpp
10 *===========================================================================*/
11
12#include "GeoLodManager.h"
13#include "GeoLodObject.h"
14#include "VertexData.h"
15#include <ntls.h>
16#include <map>
17
18using namespace Geometry;
19
20//#define MINIMUM_VALID_LOD_SIMILARITY 0.1f
21
22namespace Geometry
23{
24        class lodobj_node_t : public Moveable<lodobj_node_t>
25        {
26        public:
27                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; }
28                lodobj_node_t(const lodobj_node_t &n){ operator=(n); }
29                float dist2cam;
30                LodObject *lodobj;
31                bool must_recalculate_distance;
32                uint32 to_update;
33                bool operator < (const lodobj_node_t &o) const { return (dist2cam < o.dist2cam); }
34                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; }
35
36                lodobj_node_t * master;
37                VectorMap<uint32,lodobj_node_t*> disciples;
38                float lodfactor; // virtual lod factor (the lodfactor it seems, not its real internal lodfactor)
39                int precalpos;
40                std::string name;
41        };
42
43        class NodeContainer
44        {
45        public:
46                Vector<lodobj_node_t> lodobj_dists;
47                std::map<uint32,Geometry::Vector3> lodobj_pos; // map key: LodObject UniqueID for efficient ID-based finding
48                Vector3 camera_pos;
49
50                int num_lod_sim;
51
52                NodeContainer(const Geometry::Vector3 &campos)
53                        :camera_pos(campos){
54                }
55
56                ~NodeContainer(void){
57                        for (VectorMap<std::string,lodobj_node_t**>::Iterator it = precalculated_lods.Begin();
58                                                                                                                                  it != precalculated_lods.End(); it ++)
59                        {
60                                lodobj_node_t **nodes = *it;
61                                delete[] nodes;
62                        }
63                }
64
65                void RecalculateDist(lodobj_node_t & node){
66                        std::map<uint32,Vector3>::iterator posit = lodobj_pos.find(node.lodobj->GetUniqueID());
67                        assert(posit!=lodobj_pos.end());
68                        const Vector3 & pos = posit->second;
69
70                        node.dist2cam = CalculateDistToCamera(pos);
71                }
72
73                float CalculateDistToCamera(const Geometry::Vector3 &p) const
74                {
75                        Geometry::Vector3 v = camera_pos - p;
76                        float distquad = v.length();
77                        return distquad;
78                }
79
80                lodobj_node_t * & GetPrecalculatedLOD(const std::string &name, int ipos){
81                        int foundpos = precalculated_lods.Find(name);
82                        if (foundpos==-1)
83                        {
84                                lodobj_node_t **new_precalculated_lods=new lodobj_node_t*[num_lod_sim+1]; // +1 is done to include the highest lod (1.0)
85                                for (int i=0; i<num_lod_sim+1; i++)
86                                        new_precalculated_lods[i]=NULL;
87                                precalculated_lods.Add(name) = new_precalculated_lods;
88                                return new_precalculated_lods[ipos];
89                        }
90                        else
91                        {
92                                return precalculated_lods[foundpos][ipos];
93                        }
94                }
95
96                uint32 lod2precalpos(Real lodfactor){
97                        return (uint32)(num_lod_sim*lodfactor);
98                }
99
100        private:
101                // the following is an array of precalculated lods
102                // for 10 precalculated lods each element is in order [0.1, 0.2, 0.3, 0.4, 0.5, ...]
103                // for 100 precalculated lods each element is in order [0.01, 0.02, 0.03, 0.04, 0.05, ...]
104                // the funcion lod2precalpos translates a LOD factor to a position in the array
105                VectorMap<std::string,lodobj_node_t**> precalculated_lods;
106
107        };
108}
109
110LodManager::LodManager(Real near, Real far, const Geometry::Vector3 &campos, int numslots)
111:lasttime(0),camera_changed(true),node_added(false),
112 near_range(near),far_range(far),
113 always_calculate_lod(false),force_highest_lod(false)
114{
115        lodobj_container=new NodeContainer(campos);
116        lodobj_container->num_lod_sim = numslots;
117}
118
119LodManager::~LodManager(void)
120{
121        delete lodobj_container;
122//      delete[] precalculated_lods;
123}
124
125void Geometry::LodManager::AddLodObj(const std::string &name, LodObject * o, const Geometry::Vector3 &p)
126{
127        float dist = lodobj_container->CalculateDistToCamera(p);
128        lodobj_node_t auxnode(name,dist,o);
129       
130        lodobj_container->lodobj_dists.Add(auxnode);
131        Sort(lodobj_container->lodobj_dists);
132
133        UpdateLODObjectPos(o,p);
134        node_added=true;
135}
136
137
138void Geometry::LodManager::UpdateLOD(void)
139{
140        if (!lodobj_container->lodobj_dists.IsEmpty())
141        {
142                // update distances to the camera
143                if (camera_changed || node_added)
144                {
145                        bool must_resort = false;
146                        for (Vector<lodobj_node_t>::Iterator it=lodobj_container->lodobj_dists.Begin();
147                                                                                                 it!=lodobj_container->lodobj_dists.End();
148                                                                                                 it++)
149                        {                       
150                                lodobj_node_t & node = *it;
151                                if (node.must_recalculate_distance || camera_changed || node_added)
152                                {
153                                        lodobj_container->RecalculateDist(node);
154                                        must_resort=true;
155                                }
156                        }
157/*                      if (must_resort)
158                                Sort(lodobj_container->lodobj_dists);*/
159                }
160
161                // the end of the list is the greater element (greater dist)   
162/*              if (currFPS < targetFPS - 5)
163                {
164                        Vector<lodobj_node_t>::Iterator selnode;
165                        for (selnode=lodobj_container->lodobj_dists.End()-1; selnode!=lodobj_container->lodobj_dists.Begin()-1; selnode--)
166                        {                       
167                        // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor
168                                if (selnode->lodobj->GetCurrentLodFactor()>0.00001f)
169                                {
170                                        if (selnode->to_update==0)
171                                                break;
172                                        else
173                                                selnode->to_update--;
174                                }
175                        }
176
177                        if (selnode!=lodobj_container->lodobj_dists.Begin()-1)
178                        {
179                                // EL DECREMENTO O EL NUMERO DE DECREMENTOS TIENE QUE VENIR EN FUNCION DE LA DIFERENCIA DE FPS
180                                // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor
181                                selnode->lodobj->GoToLod(selnode->lodobj->GetCurrentLodFactor()-0.02f);
182                                selnode->to_update=10; // ESTO DEBE ASIGNARSE BASADO EN LA DISTANCIA A LA CAMARA!
183                        }
184                }
185                if (currFPS > targetFPS + 5)
186                {
187                        Vector<lodobj_node_t>::Iterator selnode;
188                        for (selnode=lodobj_container->lodobj_dists.Begin(); selnode!=lodobj_container->lodobj_dists.End(); selnode++)
189                        {                       
190                        // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor
191                                if (selnode->lodobj->GetCurrentLodFactor()<0.99999f)
192                                {
193                                        if (selnode->to_update==0)
194                                                break;
195                                        else
196                                                selnode->to_update--;
197                                }
198                        }
199
200                        if (selnode!=lodobj_container->lodobj_dists.End())
201                        {
202                        // NO HAY QUE USAR CURRENT LOD FACTOR SINO lodobj_container->lodobj_lodfactor
203                                selnode->lodobj->GoToLod(selnode->lodobj->GetCurrentLodFactor()+0.02f);
204                                selnode->to_update=10; // ESTO DEBE ASIGNARSE BASADO EN LA DISTANCIA A LA CAMARA!
205                        }
206                }*/
207
208                if (camera_changed)
209                {
210                        // por ahora solo sacar el lod basado en la camara
211                        for (Vector<lodobj_node_t>::Iterator it = lodobj_container->lodobj_dists.Begin();
212                                                                                                 it != lodobj_container->lodobj_dists.End();  it ++)
213                        {
214                                float lodfactor = 1.0f - ((it->dist2cam - near_range) / (far_range - near_range));
215                                if (lodfactor < 0.0f)
216                                        lodfactor = 0.0f;
217                                if (lodfactor > 1.0f)
218                                        lodfactor = 1.0f;
219
220                                if (force_highest_lod)
221                                        lodfactor = 1.0f;
222
223                                ChangeLOD(it,lodfactor);
224                        }
225                }                                                         
226        }
227        camera_changed=false;
228        node_added=false;
229}
230
231void Geometry::LodManager::ChangeLOD(lodobj_node_t *lodobjnode, Real lodfactor)
232{
233        LodObject *lodobj = lodobjnode->lodobj;
234
235        if (always_calculate_lod)
236        {
237                lodobj->GoToLod(lodfactor);
238                return;
239        }
240       
241        // if the lodfactor is exactly 1.0 its level of detail must be always regenerated (for closeups)
242        if (lodfactor<1.0f)
243        {
244                // if the desired lod is very similar to the previous one,
245                // then there is no need to change the level of detail at all
246//              float sim = fabsf(lodfactor - lodobjnode->lodfactor);
247//              if (sim <= MINIMUM_VALID_LOD_SIMILARITY)
248//                      return; // do not change anything if the lod is very similar!
249                int desiredpos = lodobj_container->lod2precalpos(lodfactor);
250                int mypos = lodobj_container->lod2precalpos(lodobjnode->lodfactor);
251                if (mypos==desiredpos)
252                        return;
253        }
254
255        lodobj_node_t *found_node = FindMostSimilarRealLOD(lodobjnode->name,lodfactor);
256
257        if (found_node && found_node->lodobj->GetUniqueID()!=lodobj->GetUniqueID())
258        {
259                // DEBUG
260                assert(found_node->master==NULL);
261
262//              float sim = fabsf(lodfactor - found_node->lodfactor);
263//              if (sim <= MINIMUM_VALID_LOD_SIMILARITY)
264                int desiredpos = lodobj_container->lod2precalpos(lodfactor);
265                int foundpos = lodobj_container->lod2precalpos(found_node->lodfactor);
266                if (foundpos==desiredpos)
267                {
268                        lodobj->GetIndexDataInterface()->BorrowIndexData(found_node->lodobj->GetIndexDataInterface());
269                        MakeDiscipleOf(found_node,lodobjnode);
270                }
271                else
272                        ReallyGoToLOD(lodobjnode,lodfactor);
273        }
274        else
275                ReallyGoToLOD(lodobjnode,lodfactor);
276
277        lodobjnode->lodfactor = lodfactor;
278}
279
280void Geometry::LodManager::DeleteMeFromMyMastersDiscipleList(lodobj_node_t *lodobjnode)
281{
282        // if I have a master...
283        if (lodobjnode->master)
284                lodobjnode->master->disciples.RemoveKey(lodobjnode->lodobj->GetUniqueID());     
285}
286
287
288void Geometry::LodManager::MakeIndependent(lodobj_node_t *lodobjnode)
289{
290        DeleteMeFromMyMastersDiscipleList(lodobjnode);
291
292        // next, delete my master
293        lodobjnode->master=NULL;
294}
295
296void Geometry::LodManager::MakeDiscipleOf(lodobj_node_t *master_node, lodobj_node_t *disciple_node)
297{
298        ChangeMaster(disciple_node, master_node);
299
300        // delete my disciples
301        disciple_node->disciples.Clear();
302
303        // first remove me from my master's disciple list
304        DeleteMeFromMyMastersDiscipleList(disciple_node);
305
306        master_node->disciples.GetAdd(disciple_node->lodobj->GetUniqueID()) = disciple_node;
307
308        // set master
309        disciple_node->master = master_node;
310
311        // no one must take me as a master!
312        if (disciple_node->precalpos!=-1)
313        {
314//              lodobj_container->precalculated_lods[disciple_node->precalpos] = NULL;
315                lodobj_container->GetPrecalculatedLOD(master_node->name,disciple_node->precalpos) = NULL;
316                disciple_node->precalpos = -1;
317        }
318}
319
320void Geometry::LodManager::ChangeMaster(lodobj_node_t *lodobj, lodobj_node_t *newmaster)
321{
322        // delete me from my disciples' master list
323        for (VectorMap<uint32,lodobj_node_t*>::Iterator it = lodobj->disciples.Begin(); it != lodobj->disciples.End(); it++)
324        {
325                lodobj_node_t * auxnode = *it;
326                assert(auxnode->master->lodobj->GetUniqueID() == lodobj->lodobj->GetUniqueID());
327//              auxnode->master = NULL;
328                // chanbge the master to the new one
329                auxnode->master = newmaster;
330
331                // add the new disciple to the new master
332                if (newmaster)
333                        newmaster->disciples.GetAdd(auxnode->lodobj->GetUniqueID()) = auxnode;
334        }       
335}
336
337
338void Geometry::LodManager::UpdateDisciples(lodobj_node_t *master_node)
339{
340        for (VectorMap<uint32,lodobj_node_t*>::Iterator it= master_node->disciples.Begin(); it!= master_node->disciples.End(); it++)
341        {
342                lodobj_node_t *disciple_node = *it;
343                disciple_node->lodobj->GetIndexDataInterface()->BorrowIndexData(master_node->lodobj->GetIndexDataInterface());
344                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
345        }
346}
347
348void Geometry::LodManager::ReallyGoToLOD(lodobj_node_t *lodobjnode, Real lodfactor)
349{
350        lodobjnode->lodobj->GoToLod(lodfactor);
351
352        MakeIndependent(lodobjnode);
353        UpdateDisciples(lodobjnode);
354
355        // store the precalculated lod in the table
356        int i = lodobj_container->lod2precalpos(lodfactor);
357
358        assert(i>=0 && i<=lodobj_container->num_lod_sim); // <= por lo del +1
359
360        if (lodobjnode->precalpos != -1)
361                //lodobj_container->precalculated_lods[lodobjnode->precalpos] = NULL;
362                lodobj_container->GetPrecalculatedLOD(lodobjnode->name,lodobjnode->precalpos) = NULL;
363
364//      lodobj_container->precalculated_lods[i] = lodobjnode;
365        lodobj_container->GetPrecalculatedLOD(lodobjnode->name,i) = lodobjnode;
366
367        lodobjnode->precalpos = i;
368}
369
370
371lodobj_node_t * Geometry::LodManager::FindMostSimilarRealLOD(const std::string &name, Real desired_lod)
372{
373        uint32 i = lodobj_container->lod2precalpos(desired_lod);
374        assert(i>=0 && i<=lodobj_container->num_lod_sim); // <= por lo del +1
375//      lodobj_node_t *obj = lodobj_container->precalculated_lods[i];
376        lodobj_node_t *obj = lodobj_container->GetPrecalculatedLOD(name,i);
377
378        return obj;
379}
380
381void Geometry::LodManager::UpdateCamera(const Geometry::Vector3 &p)
382{
383        lodobj_container->camera_pos = p;
384        camera_changed = true;
385}
386
387void Geometry::LodManager::UpdateLODObjectPos(LodObject *o, const Geometry::Vector3 &p)
388{
389        lodobj_container->lodobj_pos[o->GetUniqueID()] = p;
390}
391
392float Geometry::LodManager::SelectRandomDistance(void) const
393{
394        float linear_01 = (float)rand()/(float)RAND_MAX;
395        float exp_01 = linear_01*linear_01;
396        float real_dist = exp_01*near_range + (1.0f-exp_01)*far_range;
397        return real_dist;
398}
Note: See TracBrowser for help on using the repository browser.