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

Revision 1526, 12.7 KB checked in by gumbau, 18 years ago (diff)

Updated modules to the new interface and the new simplification algorithm improvements.

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