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