1 #include <osg/Group>
2 #include <osg/Object>
3 #include <osg/Node>
4 #include <osg/Notify>
5 #include <osg/MatrixTransform>
6 #include <osg/BoundingSphere>
7 #include <osgDB/Registry>
8 #include <osgDB/FileUtils>
9 #include <osg/io_utils>
10 
11 #include <iostream>
12 #include <sstream>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "ReaderWriterTXP.h"
17 #include "TXPNode.h"
18 #include "TXPArchive.h"
19 #include "TXPPagedLOD.h"
20 #include "TXPSeamLOD.h"
21 #include "TileMapper.h"
22 
23 #define ReaderWriterTXPERROR(s) OSG_NOTICE << "txp::ReaderWriterTXP::" << (s) << " error: "
24 
25 
26 
27 namespace
28 {
29     char gbuf[2048];
30 }
31 
32 using namespace txp;
33 
34 int ReaderWriterTXP::_archiveId = 0;
35 
local_readNode(const std::string & file,const osgDB::ReaderWriter::Options * options)36 osgDB::ReaderWriter::ReadResult ReaderWriterTXP::local_readNode(const std::string& file, const osgDB::ReaderWriter::Options* options)
37 {
38 
39     std::string name = osgDB::getSimpleFileName(file);
40 
41     // We load archive.txp
42     if (strncmp(name.c_str(),"archive",7)==0)
43     {
44         std::string fileName = osgDB::findDataFile( file, options );
45         if ( fileName.empty() )
46             return ReadResult::FILE_NOT_FOUND;
47 
48         osg::ref_ptr<TXPNode> txpNode = new TXPNode;
49         txpNode->setArchiveName(fileName);
50         if (options)
51         {
52             txpNode->setOptions(options->getOptionString());
53         }
54 
55 
56         //modified by Brad Anderegg on May-27-08
57         //calling getArchive will create a new TXPArchive if the specified one does not exist
58         //we will set our osgdb loader options on the archive and set the appropriate archive on
59         //the txpNode.
60         int id = ++_archiveId;
61         osg::ref_ptr< TXPArchive > archive = createArchive(id,osgDB::getFilePath(fileName));
62 
63         if (archive != NULL)
64         {
65             archive->setId(id);
66 
67             if (options && options->getOptionString().find("loadMaterialsToStateSet")!=std::string::npos)
68             {
69                archive->SetMaterialAttributesToStateSetVar(true);
70             }
71 
72             txpNode->loadArchive(archive.get());
73 
74             return txpNode.get();
75         }
76         else
77         {
78             return ReadResult::ERROR_IN_READING_FILE;
79         }
80     }
81 
82     // We load tileLOD_XxY_ID.txp
83     else if (strncmp(name.c_str(),"tile",4)==0)
84     {
85         int x,y,lod;
86         unsigned int id;
87         sscanf(name.c_str(),"tile%d_%dx%d_%u",&lod,&x,&y,&id);
88         osg::ref_ptr< TXPArchive > archive = getArchive(id,osgDB::getFilePath(file));
89         if (archive == NULL)
90             return ReadResult::ERROR_IN_READING_FILE;
91 
92         // The way this is done a 'tile' should only be created for lod 0 only,
93         // something is wrong if this is no the case
94         if(lod != 0)
95         {
96             ReaderWriterTXPERROR("ReaderWriterTXP::local_readNode()") << "paged 'tile' should be at lod 0" << std::endl;
97             return ReadResult::ERROR_IN_READING_FILE;
98         }
99 
100         trpgEndian endian = archive->GetEndian();
101         archive->ReadSubArchive( 0, 0, endian);
102         archive->ReadSubArchive( y, x, endian);
103 
104 //    std::cout << "Attempted " << x << " " << y << std::endl;
105 
106         TXPArchive::TileInfo info;
107         if (!archive->getTileInfo(x,y,lod,info))
108             return ReadResult::ERROR_IN_READING_FILE;
109 
110         std::vector<TXPArchive::TileLocationInfo> childrenLoc;
111         osg::ref_ptr<osg::Node> tileContent = getTileContent(info,x,y,lod,archive.get(), childrenLoc);
112 
113         tileContent->setName("TileContent");
114 
115         bool asChildren = false;
116         std::string childrenInfoStr;
117 
118         int numLods = archive->getNumLODs();
119 
120         int majorVersion, minorVersion;
121         archive->GetVersion(majorVersion, minorVersion);
122         if(majorVersion ==2 && minorVersion >=1)
123         {
124             // Version 2.1 and over
125             // The tile table only contains lod 0 and the children
126             // info are stored in its parent. SO if we do not want
127             // to be forced to reparse the parent we need to save that
128             // info. For now we just add it to the node name
129 
130             if(childrenLoc.size() > 0)
131             {
132                 asChildren = true;
133                 createChildrenLocationString(childrenLoc, childrenInfoStr);
134             }
135         }
136         else
137         {
138             if (lod < (numLods-1)) asChildren = true;
139         }
140 
141         if (asChildren)
142         {
143             char pagedLODfile[1024];
144             sprintf(pagedLODfile,"%s\\subtiles%d_%dx%d_%d",
145             archive->getDir(),
146             lod,
147             x,
148             y,
149             archive->getId());
150 
151             strcat(pagedLODfile, childrenInfoStr.c_str());
152             strcat(pagedLODfile, ".txp");
153 
154 
155             // there are tile sets which do not maintain the z extents in
156             // the tile table.  This attempt to address the issue by using
157             // the geometry bounding sphere.  The downside is that this is
158             // not coupled to the generation and may result in runtime cracks
159             if (info.center.z() == 0)
160             {
161                 osg::BoundingSphere bSphere = tileContent->getBound();
162 
163                 info.center.z() = bSphere.center().z();
164                 info.radius = bSphere.radius();
165             }
166 
167 
168             osg::ref_ptr<TXPPagedLOD> pagedLOD = new TXPPagedLOD;
169             // note: use maximum(info.maxRange,1e7) as just maxRange would result in some corner tiles from being culled out.
170             pagedLOD->addChild(tileContent.get(),info.minRange,osg::maximum(info.maxRange,1e7));
171             pagedLOD->setFileName(1,pagedLODfile);
172             pagedLOD->setRange(1,0,info.minRange);
173             pagedLOD->setCenter(info.center);
174             pagedLOD->setRadius(info.radius);
175             pagedLOD->setPriorityOffset(0,numLods-lod);
176             pagedLOD->setPriorityScale(0,1.0f);
177             pagedLOD->setNumChildrenThatCannotBeExpired(1);
178             pagedLOD->setTileId(x,y,lod);
179 
180             const trpgHeader* header = archive->GetHeader();
181             trpgHeader::trpgTileType tileType;
182             header->GetTileOriginType(tileType);
183             if(tileType == trpgHeader::TileLocal)
184             {
185                 osg::Vec3d sw(info.bbox._min);
186                 pagedLOD->setCenter(info.center - sw);
187             }
188 
189             return pagedLOD.get();
190         }
191         else
192             return tileContent.get();
193     }
194 
195 
196     // For 2.0 and lower we load subtilesLOD_XxY_ID.txp
197     // For 2.1 and over  we load subtilesLOD_XxY_ID_NBCHILD_{X_Y_FID_FOFFSET_ZMIN_ZMAX_X_Y_ADDR ....}.txp
198     else if (strncmp(name.c_str(),"sub",3)==0)
199     {
200         int x,y,lod;
201         unsigned int id;
202         sscanf(name.c_str(),"subtiles%d_%dx%d_%u",&lod,&x,&y,&id);
203         osg::ref_ptr< TXPArchive > archive = getArchive(id,osgDB::getFilePath(file));
204         if (archive == NULL)
205             return ReadResult::ERROR_IN_READING_FILE;
206 
207         int majorVersion, minorVersion;
208         archive->GetVersion(majorVersion, minorVersion);
209 
210         std::vector<TXPArchive::TileLocationInfo> childrenLoc;
211 
212         osg::ref_ptr<osg::Group> subtiles = new osg::Group;
213 
214         int numLods = archive->getNumLODs();
215 
216         if(majorVersion == 2  && minorVersion >= 1)
217         {
218             int nbChild;
219 
220             sscanf(name.c_str(),"subtiles%d_%dx%d_%u_%d",&lod,&x,&y,&id, &nbChild);
221             std::vector<TXPArchive::TileLocationInfo> locs;
222             bool status = true;
223             status = extractChildrenLocations(name, lod, locs, nbChild);
224             if(majorVersion >= TRPG_NOMERGE_VERSION_MAJOR && minorVersion >=TRPG_NOMERGE_VERSION_MINOR && archive->GetHeader()->GetIsMaster())
225             {
226                 for(int idx=0;idx<nbChild;idx++)
227                 {
228                     //figure out the block row/col
229                     int blockx,blocky;
230                     unsigned int denom = (1 << locs[idx].lod); // this should work up to lod 31
231                     blockx = locs[idx].x/denom;
232                     blocky = locs[idx].y/denom;
233                     locs[idx].addr.col = blockx;
234                     locs[idx].addr.row = blocky;
235                 }
236             }
237 
238             if(!status)
239             {
240                 ReaderWriterTXPERROR("ReaderWriterTXP::local_readNode()") << "'subtile' filename children parsing failed " << std::endl;
241                 return ReadResult::ERROR_IN_READING_FILE;
242             }
243 
244             const trpgHeader* header = archive->GetHeader();
245             trpgHeader::trpgTileType tileType;
246             header->GetTileOriginType(tileType);
247 
248             TXPArchive::TileLocationInfo plInfo;
249             plInfo.x = x;
250             plInfo.y = y;
251             plInfo.lod = lod;
252             TXPArchive::TileInfo parentInfo;
253             archive->getTileInfo(plInfo,parentInfo);
254 
255             for(int idx = 0; idx < nbChild; ++idx)
256             {
257                 std::vector<TXPArchive::TileLocationInfo> childrenChildLoc;
258 
259                 TXPArchive::TileLocationInfo& loc = locs[idx];
260 
261                 TXPArchive::TileInfo info;
262                 if (!archive->getTileInfo(loc,info))
263                     continue;
264 
265                 osg::ref_ptr<osg::Node> tileContent = getTileContent(info, loc, archive.get(), childrenChildLoc);
266 
267                 tileContent->setName("TileContent");
268 
269                 if(childrenChildLoc.size() > 0)
270                 {
271                     std::string childInfoStr;
272                     createChildrenLocationString(childrenChildLoc, childInfoStr);
273 
274                     char pagedLODfile[1024];
275                     sprintf(pagedLODfile,"%s\\subtiles%d_%dx%d_%d%s.txp",
276                         archive->getDir(),
277                         loc.lod,
278                         loc.x,
279                         loc.y,
280                         archive->getId(),
281                         childInfoStr.c_str());
282 
283                     // there are tile sets which do not maintain the z extents in
284                     // the tile table.  This attempt to address the issue by using
285                     // the geometry bounding sphere.  The downside is that this is
286                     // not coupled to the generation and may result in runtime cracks
287                     if (info.center.z() == 0)
288                     {
289                         osg::BoundingSphere bSphere = tileContent->getBound();
290 
291                         info.center.z() = bSphere.center().z();
292                         info.radius = bSphere.radius();
293                     }
294 
295                     osg::ref_ptr<TXPPagedLOD> pagedLOD = new TXPPagedLOD;
296                             // note: use maximum(info.maxRange,1e7) as just maxRange would result in some corner tiles from being culled out.
297                     pagedLOD->addChild(tileContent.get(),info.minRange,osg::maximum(info.maxRange,1e7));
298                     pagedLOD->setFileName(1,pagedLODfile);
299                     pagedLOD->setRange(1,0,info.minRange);
300                     pagedLOD->setCenter(info.center);
301                     pagedLOD->setRadius(info.radius);
302                     pagedLOD->setPriorityOffset(0,numLods - loc.lod);
303                     pagedLOD->setPriorityScale(0,1.0f);
304                     pagedLOD->setNumChildrenThatCannotBeExpired(1);
305                     pagedLOD->setTileId(loc.x, loc.y, loc.lod);
306 
307                     if(tileType == trpgHeader::TileLocal)
308                     {
309                         osg::Vec3d center(info.center - parentInfo.bbox._min);
310                         osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
311                         sw[2] = 0.0;
312                         pagedLOD->setCenter(center - sw);
313                         osg::Matrix offset;
314                         offset.setTrans(sw);
315                         osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
316                         tform->addChild(pagedLOD.get());
317                         subtiles->addChild(tform);
318                     }
319                     else
320                         subtiles->addChild(pagedLOD.get());
321                         subtiles->setUserData(new TileIdentifier(loc.x, loc.y, loc.lod)); // is this really needed?
322                 }
323                 else
324                 {
325                     subtiles->setUserData(new TileIdentifier(loc.x, loc.y, loc.lod));
326                     if(tileType == trpgHeader::TileLocal)
327                     {
328                         osg::Vec3d center(info.center - parentInfo.bbox._min);
329                         osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
330                         sw[2] = 0.0;
331                         osg::Matrix offset;
332                         offset.setTrans(sw);
333                         osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
334                         tform->addChild(tileContent.get());
335                         subtiles->addChild(tform);
336                     }
337                     else
338                         subtiles->addChild(tileContent.get());
339                 }
340             }
341         }
342         else
343         {
344 
345             int sizeX, sizeY;
346             archive->getLODSize(lod+1,sizeX,sizeY);
347 
348             const trpgHeader* header = archive->GetHeader();
349             trpgHeader::trpgTileType tileType;
350             header->GetTileOriginType(tileType);
351 
352             TXPArchive::TileInfo parentInfo;
353             archive->getTileInfo(x,y,lod,parentInfo);
354 
355             for (int ix = 0; ix < 2; ix++)
356             {
357                 for (int iy = 0; iy < 2; iy++)
358                 {
359                     int tileX = x*2+ix;
360                     int tileY = y*2+iy;
361                     int tileLOD = lod+1;
362 
363                     TXPArchive::TileInfo info;
364                     if (!archive->getTileInfo(tileX,tileY,tileLOD,info))
365                     continue;
366 
367                     osg::ref_ptr<osg::Node> tileContent = getTileContent(info,tileX,tileY,tileLOD,archive.get(), childrenLoc);
368 
369                     tileContent->setName("TileContent");
370 
371                     if (tileLOD < (numLods-1))
372                     {
373                         char pagedLODfile[1024];
374                         sprintf(pagedLODfile,"%s\\subtiles%d_%dx%d_%d.txp",
375                             archive->getDir(),
376                             tileLOD,
377                             tileX,
378                             tileY,
379                             archive->getId());
380 
381                         // there are tile sets which do not maintain the z extents in
382                         // the tile table.  This attempt to address the issue by using
383                         // the geometry bounding sphere.  The downside is that this is
384                         // not coupled to the generation and may result in runtime cracks
385                         if (info.center.z() == 0)
386                         {
387                             osg::BoundingSphere bSphere = tileContent->getBound();
388 
389                             info.center.z() = bSphere.center().z();
390                             info.radius = bSphere.radius();
391                         }
392 
393                         osg::ref_ptr<TXPPagedLOD> pagedLOD = new TXPPagedLOD;
394                                     // note: use maximum(info.maxRange,1e7) as just maxRange would result in some corner tiles from being culled out.
395                         pagedLOD->addChild(tileContent.get(),info.minRange,osg::maximum(info.maxRange,1e7));
396                         pagedLOD->setFileName(1,pagedLODfile);
397                         pagedLOD->setRange(1,0,info.minRange);
398                         pagedLOD->setCenter(info.center);
399                         pagedLOD->setRadius(info.radius);
400                         pagedLOD->setPriorityOffset(0,numLods-lod);
401                         pagedLOD->setPriorityScale(0,1.0f);
402                         pagedLOD->setNumChildrenThatCannotBeExpired(1);
403                         pagedLOD->setTileId(tileX,tileY,tileLOD);
404 
405                         if(tileType == trpgHeader::TileLocal)
406                         {
407                             osg::Vec3d center(info.center - parentInfo.bbox._min);
408                             osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
409                             sw[2] = 0.0;
410                             pagedLOD->setCenter(center - sw);
411                             osg::Matrix offset;
412                             offset.setTrans(sw);
413                             osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
414                             tform->addChild(pagedLOD.get());
415                             subtiles->addChild(tform);
416                         }
417                         else
418                             subtiles->addChild(pagedLOD.get());
419                     }
420                     else
421                     {
422                         subtiles->setUserData(new TileIdentifier(tileX,tileY,tileLOD));
423                         if(tileType == trpgHeader::TileLocal)
424                         {
425                             osg::Vec3d center(info.center - parentInfo.bbox._min);
426                             osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
427                             sw[2] = 0.0;
428                             osg::Matrix offset;
429                             offset.setTrans(sw);
430                             osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
431                             tform->addChild(tileContent.get());
432                             subtiles->addChild(tform);
433                         }
434                         else
435                             subtiles->addChild(tileContent.get());
436                     }
437 
438                 }
439             }
440         }
441 
442         //OSG_NOTICE << "Subtiles for " << x << " " << y << " " << lod << " lodaded" << std::endl;
443         return subtiles.get();
444     }
445 
446     return ReadResult::ERROR_IN_READING_FILE;
447 }
448 
449 // If you change this then you have to change extractChildrenLocation()
createChildrenLocationString(const std::vector<TXPArchive::TileLocationInfo> & locs,std::string & locString) const450 void ReaderWriterTXP::createChildrenLocationString(const std::vector<TXPArchive::TileLocationInfo>& locs, std::string& locString) const
451 {
452     std::stringstream theLoc;
453 
454     if(locs.size() == 0)
455     {
456         theLoc << "_" << locs.size();
457     }
458     else
459     {
460 
461         theLoc << "_" << locs.size() << "_" << "{" ;
462 
463         for(unsigned int idx = 0; idx < locs.size(); ++idx)
464         {
465             const TXPArchive::TileLocationInfo& loc = locs[idx];
466 
467             theLoc << loc.x
468                    << "_"
469                    << loc.y
470                    << "_"
471                    << loc.addr.file
472                    << "_"
473                    << loc.addr.offset
474                    << "_"
475                    << loc.zmin
476                    << "_"
477                    << loc.zmax;
478             if(idx != locs.size() -1)
479                 theLoc << "_";
480         }
481     }
482 
483     theLoc << "}" << std::ends;
484 
485     locString = theLoc.str();
486 }
extractChildrenLocations(const std::string & name,int parentLod,std::vector<TXPArchive::TileLocationInfo> & locs,int nbChild) const487 bool ReaderWriterTXP::extractChildrenLocations(const std::string& name, int parentLod, std::vector<TXPArchive::TileLocationInfo>& locs, int nbChild) const
488 {
489     locs.clear();
490 
491     if(nbChild == 0)
492         return true;
493 
494     locs.resize(nbChild);
495 
496     // We look for '{', which should be the start of the list of {x,y,addr} children data
497     // '}' should end the list.
498     // We expect: X,Y,FID,FOFFSET,ZMIN,ZMAX
499     std::string::size_type startOfList = name.find_last_of('{');
500     if(startOfList == std::string::npos)
501         return false;
502 
503     std::string::size_type endOfList = name.find_last_of('}');
504     if(endOfList == std::string::npos)
505         return false;
506 
507     // Extract the data
508     strcpy(gbuf, name.substr(startOfList + 1, endOfList - startOfList - 1).c_str());
509     char *token = strtok( gbuf, "_" );
510 
511     int nbTokenRead = 0;
512     for(int idx = 0; idx < nbChild; idx++)
513     {
514         // X
515         if(!token)
516             break;
517         locs[idx].x = atoi(token);
518         nbTokenRead++;
519 
520         // Y
521         token = strtok(0, "_");
522         if(!token)
523             break;
524         locs[idx].y = atoi(token);
525         nbTokenRead++;
526 
527         // FID
528         token = strtok(0, "_");
529         if(!token)
530             break;
531         locs[idx].addr.file = atoi(token);
532         nbTokenRead++;
533 
534         // OFFSET
535         token = strtok(0, "_");
536         if(!token)
537             break;
538         locs[idx].addr.offset = atoi(token);
539         nbTokenRead++;
540 
541         // ZMIN
542         token = strtok(0, "_");
543         if(!token)
544             break;
545         locs[idx].zmin = osg::asciiToFloat(token);
546         nbTokenRead++;
547 
548         // ZMAX
549         token = strtok(0, "_");
550         if(!token)
551             break;
552         locs[idx].zmax = osg::asciiToFloat(token);
553         nbTokenRead++;
554 
555         locs[idx].lod = parentLod+1;
556 
557 
558 
559         token = strtok(0, "_");
560     }
561 
562     if(nbTokenRead != nbChild*6)
563         return false;
564     else
565         return true;
566 
567 
568 }
569 
getArchiveName(const std::string & dir)570 std::string ReaderWriterTXP::getArchiveName(const std::string& dir)
571 {
572 #ifdef _WIN32
573     const char _PATHD = '\\';
574 #elif defined(macintosh)
575     const char _PATHD = ':';
576 #else
577     const char _PATHD = '/';
578 #endif
579 
580     return dir+_PATHD+"archive.txp";
581 }
582 
getArchive(int id,const std::string & dir)583 osg::ref_ptr< TXPArchive > ReaderWriterTXP::getArchive(int id, const std::string& dir)
584 {
585     osg::ref_ptr< TXPArchive > archive = NULL;
586 
587     std::map< int,osg::ref_ptr<TXPArchive> >::iterator iter = _archives.find(id);
588 
589     if (iter != _archives.end())
590     {
591         archive = iter->second.get();
592     }
593     else
594     {
595         std::string archiveName = getArchiveName(dir);
596         ReaderWriterTXPERROR("getArchive()") << "archive id " << id << " not found: \"" << archiveName << "\"" << std::endl;
597     }
598     return archive;
599 }
600 
createArchive(int id,const std::string & dir)601 osg::ref_ptr< TXPArchive > ReaderWriterTXP::createArchive(int id, const std::string& dir)
602 {
603     std::string archiveName = getArchiveName(dir);
604 
605     osg::ref_ptr< TXPArchive > archive = getArchive(id, dir);
606     if (archive != NULL)
607     {
608         ReaderWriterTXPERROR("createArchive()") << "archive id " << id << " already exists: \"" << archiveName << "\"" << std::endl;
609         return NULL;
610     }
611 
612     archive = new TXPArchive;
613     if (archive->openFile(archiveName) == false)
614     {
615         ReaderWriterTXPERROR("createArchive()") << "failed to load archive: \"" << archiveName << "\"" << std::endl;
616         return NULL;
617     }
618 
619     if (archive->loadMaterials() == false)
620     {
621         ReaderWriterTXPERROR("createArchive()") << "failed to load materials from archive: \"" << archiveName << "\"" << std::endl;
622         return NULL;
623     }
624 
625     if (archive->loadModels() == false)
626     {
627         ReaderWriterTXPERROR("createArchive()") << "failed to load models from archive: \"" << archiveName << "\"" << std::endl;
628         return NULL;
629     }
630 
631     if (archive->loadLightAttributes() == false)
632     {
633         ReaderWriterTXPERROR("createArchive()") << "failed to load light attributes from archive: \"" << archiveName << "\"" << std::endl;
634         return NULL;
635     }
636 
637     if (archive->loadTextStyles() == false)
638     {
639         ReaderWriterTXPERROR("createArchive()") << "failed to load text styles from archive: \"" << archiveName << "\"" << std::endl;
640         return NULL;
641     }
642 
643     archive->setId(id);
644 
645     _archives[id] = archive;
646 
647     return archive;
648 }
649 
removeArchive(int id)650 bool ReaderWriterTXP::removeArchive( int id )
651 {
652     OSG_INFO<<"ReaderWriterTXP::removeArchive(id="<<id<<")"<<std::endl;
653     //return (_archives.erase(id) >= 1);
654     bool result=_archives.erase(id) >= 1;
655     OSG_WARN<<"remove archive " << id << " size " << _archives.size()
656         << " result " << result << std::endl;
657     return result;
658 
659 }
660 
661 class SeamFinder: public osg::NodeVisitor
662 {
663 public:
SeamFinder(int x,int y,int lod,const TXPArchive::TileInfo & info,TXPArchive * archive)664     SeamFinder(int x, int y, int lod, const TXPArchive::TileInfo& info, TXPArchive *archive ):
665     osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
666     _x(x), _y(y), _lod(lod), _info(info), _archive(archive)
667     {}
668 
apply(osg::Group & group)669     virtual void apply(osg::Group& group)
670     {
671         for (unsigned int i = 0; i < group.getNumChildren(); i++)
672         {
673             osg::Node* child = group.getChild(i);
674             osg::Node* seam = seamReplacement(child);
675             if (child != seam)
676             {
677                 group.replaceChild(child,seam);
678             }
679             else
680             {
681                 child->accept(*this);
682             }
683         }
684     }
685 
686 protected:
687     osg::Node* seamReplacement(osg::Node* node);
688 
operator =(const SeamFinder &)689     SeamFinder& operator = (const SeamFinder&) { return *this; }
690 
691     int _x, _y, _lod;
692     const TXPArchive::TileInfo& _info;
693     TXPArchive *_archive;
694 };
695 
696 #define equalDoubles(a, b) (fabs(a-b) < 0.001)
697 
seamReplacement(osg::Node * node)698 osg::Node* SeamFinder::seamReplacement(osg::Node* node)
699 {
700     osg::Group* group = node->asGroup();
701     if ( group == 0 )
702         return node;
703 
704     std::vector<osg::Node*> nonSeamChildren;
705     osg::LOD* hiRes = 0;
706     osg::LOD* loRes = 0;
707 
708     const trpgHeader* header = _archive->GetHeader();
709     trpgHeader::trpgTileType tileType;
710     header->GetTileOriginType(tileType);
711 
712     for (unsigned int i = 0; i < group->getNumChildren(); i++)
713     {
714         osg::LOD* lod = dynamic_cast<osg::LOD*>(group->getChild(i));
715         if (lod == 0)
716         {
717             nonSeamChildren.push_back(group->getChild(i));
718             continue;
719         }
720 
721         bool nonSeamChild = true;
722 
723         // looks like the problem is in here - likely due to seamLOD info
724         // not being adjusted properly in tiled databases
725             // seam center is outside the bounding box of the tile
726         osg::Vec3 lodCenter = lod->getCenter();
727 
728         if(tileType == trpgHeader::TileLocal)
729         {
730             trpg2dPoint tileExtents;
731             header->GetTileSize(0, tileExtents);
732             osg::BoundingBox bbox;
733             _archive->getExtents(bbox);
734             osg::Vec3 offset(0.0, 0.0, 0.0);
735 
736             int divider = (0x1 << _lod);
737             // calculate which tile model is located in
738             tileExtents.x /= divider;
739             tileExtents.y /= divider;
740             offset[0] = _x*tileExtents.x;// + tileExtents.x*0.5;
741             offset[1] = _y*tileExtents.y;// + tileExtents.y*0.5;
742             lodCenter += offset;
743         }
744 
745         if (!_info.bbox.contains(lodCenter))
746         {
747             const osg::LOD::RangeList& rangeList = lod->getRangeList();
748             if (!rangeList.size())
749             {
750                 // TODO: Warn here
751                 continue;
752             }
753 
754             TXPArchive::TileInfo lod_plus_one_info;
755             if (!this->_archive->getTileInfo(_x,_y,_lod+1,lod_plus_one_info))
756             {
757                 // TODO: Warn here
758                 continue;
759             }
760 
761             double lod_plus_oneSwitchInDistance =  lod_plus_one_info.maxRange;
762             double lod0SwitchInDistance =  _info.lod0Range;
763 
764             // low res seam has min/max ranges of lod+1 range/lod 0 range
765             if (equalDoubles(lod_plus_oneSwitchInDistance,rangeList.at(0).first) && equalDoubles(lod0SwitchInDistance,rangeList.at(0).second))
766             {
767                 if (loRes==0)
768                 {
769                     loRes = lod;
770                     nonSeamChild = false;
771                 }
772             }
773             else
774             // hi res seam has min/max ranges of 0 range/lod+1 range
775             if (rangeList.at(0).first==0.0 && equalDoubles(lod_plus_oneSwitchInDistance,rangeList.at(0).second))
776             {
777                 if (hiRes==0)
778                 {
779                     hiRes = lod;
780                     nonSeamChild = false;
781                 }
782             }
783         }
784         if (nonSeamChild)
785         {
786             nonSeamChildren.push_back(lod);
787         }
788     }
789 
790     if (loRes)
791     {
792         int dx = 0;
793         int dy = 0;
794         int lod = _lod;
795         osg::Vec3 lodCenter = loRes->getCenter();
796 
797         if(tileType == trpgHeader::TileLocal)
798         {
799             trpg2dPoint tileExtents;
800             header->GetTileSize(0, tileExtents);
801             osg::BoundingBox bbox;
802             _archive->getExtents(bbox);
803             osg::Vec3 offset(0.0, 0.0, 0.0);
804 
805             int divider = (0x1 << _lod);
806             // calculate which tile model is located in
807             tileExtents.x /= divider;
808             tileExtents.y /= divider;
809             offset[0] = _x*tileExtents.x;// + tileExtents.x*0.5;
810             offset[1] = _y*tileExtents.y;// + tileExtents.y*0.5;
811             lodCenter += offset;
812         }
813 
814         osg::Vec3 delta = lodCenter-_info.center;
815         if (fabs(delta.x())>fabs(delta.y()))
816         {
817             if ( delta.x() < 0.0 )
818                 --dx;    // west
819             else
820                 dx++;                  // east
821         }
822         else
823         {
824             if ( delta.y() < 0.0 )
825                 --dy;    // south
826             else
827                 ++dy;                  // north
828         }
829 
830         TXPSeamLOD* seam = new TXPSeamLOD(_x, _y, lod, dx, dy);
831         seam->setCenter(loRes->getCenter());
832         seam->addChild(loRes->getChild(0));        // low res
833         if (hiRes)
834         {
835             seam->addChild(hiRes->getChild(0));    // high res
836         }
837 
838         if (nonSeamChildren.empty())
839         {
840             return seam;
841         }
842         else
843         {
844             osg::Group* newGroup = new osg::Group;
845 
846             newGroup->addChild(seam);
847 
848             for (unsigned int i = 0; i < nonSeamChildren.size(); i++)
849                 newGroup->addChild(nonSeamChildren[i]);
850 
851             return newGroup;
852         }
853     }
854 
855     return node;
856 }
857 
getTileContent(const TXPArchive::TileInfo & info,int x,int y,int lod,TXPArchive * archive,std::vector<TXPArchive::TileLocationInfo> & childrenLoc)858 osg::Node* ReaderWriterTXP::getTileContent(const TXPArchive::TileInfo &info, int x, int y, int lod, TXPArchive* archive,  std::vector<TXPArchive::TileLocationInfo>& childrenLoc)
859 {
860     if ( archive == 0 )
861         return 0;
862 
863     int majorVersion, minorVersion;
864     archive->GetVersion(majorVersion, minorVersion);
865 
866     double realMinRange = info.minRange;
867     double realMaxRange = info.maxRange;
868     double  usedMaxRange = osg::maximum(info.maxRange,1e7);
869     osg::Vec3 tileCenter;
870     osg::Group* tileGroup = archive->getTileContent(x,y,lod,realMinRange,realMaxRange,usedMaxRange,tileCenter, childrenLoc);
871 
872     // if group has only one child, then simply use its child.
873     // if the node is a transform, then stop processing so as to not loose the transformation
874     while (tileGroup && !tileGroup->asTransform() &&
875            tileGroup->getNumChildren()==1 && tileGroup->getChild(0)->asGroup())
876     {
877         tileGroup = tileGroup->getChild(0)->asGroup();
878     }
879 
880     bool doSeam = false;
881     if(majorVersion == 2 && minorVersion >= 1)
882         doSeam = (childrenLoc.size() > 0);
883     else
884         doSeam = (lod < (archive->getNumLODs() - 1));
885 
886     // Handle seams
887     if (tileGroup && doSeam)
888     {
889         SeamFinder sfv(x,y,lod,info,archive);
890         tileGroup->accept(sfv);
891     }
892 
893     return tileGroup;
894 }
895 
896 // this version only gets called if the TXP version is >= than 2.1
getTileContent(const TXPArchive::TileInfo & info,const TXPArchive::TileLocationInfo & loc,TXPArchive * archive,std::vector<TXPArchive::TileLocationInfo> & childrenLoc)897 osg::Node* ReaderWriterTXP::getTileContent(const TXPArchive::TileInfo &info, const TXPArchive::TileLocationInfo& loc, TXPArchive* archive,  std::vector<TXPArchive::TileLocationInfo>& childrenLoc)
898 {
899     if ( archive == 0 )
900         return 0;
901 
902     // int numLods = archive->getNumLODs();
903 
904     double realMinRange = info.minRange;
905     double realMaxRange = info.maxRange;
906     double usedMaxRange = osg::maximum(info.maxRange,1e7);
907     osg::Vec3 tileCenter;
908     osg::Group* tileGroup = archive->getTileContent(loc,realMinRange,realMaxRange,usedMaxRange,tileCenter, childrenLoc);
909 
910     // if group has only one child, then simply use its child.
911     // if the node is a transform, then stop processing so as to not loose the transformation
912     while (tileGroup && !tileGroup->asTransform() &&
913            tileGroup->getNumChildren()==1 && tileGroup->getChild(0)->asGroup())
914     {
915         tileGroup = tileGroup->getChild(0)->asGroup();
916     }
917 
918     // Handle seams
919     if (tileGroup && childrenLoc.size() > 0)
920     {
921         SeamFinder sfv(loc.x, loc.y, loc.lod, info, archive);
922         tileGroup->accept(sfv);
923     }
924 
925     return tileGroup;
926 }
927 
928 REGISTER_OSGPLUGIN(txp, ReaderWriterTXP)
929 
930