email comments from Andew Sampson, "I've contacted Terrex, and obtained the 2.2 version of their trpage library. The library included a fork of OSG 0.9.8's txp plugin, modified to load 2.1+ txp DBs. I've done the work of incorporating the changes made to OSG's txp plugin since 0.9.8 into Terrex's fork. The forked version is now up-to-date with the changes made to OSG 0.9.9 and 1.0. Terrex made a lot of changes (especially differences in whitespace), so the diff between the forked version and OSG 1.0's txp plugin is yucky. I did my best, but keep in mind that this is the result of a 4-way merge (kinda... terrex-0.9.8, stock-0.9.8, stock-0.9.9, stock-1.0). I really want to see this forked version merged back into the main OSG branch. The new features offered by this version of the plugin (2.1+ support, variable LOD support, bug fixes) are worth the trouble." -- Don Tidrow then took this code and added his work. -- Robert Osfield then fixed all the warnings that abound in the trpage code base.
869 lines
26 KiB
C++
869 lines
26 KiB
C++
#include <osg/Group>
|
|
#include <osg/Object>
|
|
#include <osg/Node>
|
|
#include <osg/Notify>
|
|
#include <osg/MatrixTransform>
|
|
#include <osg/BoundingSphere>
|
|
#include <osgDB/Registry>
|
|
#include <osgDB/FileUtils>
|
|
#include <osg/io_utils>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "ReaderWriterTXP.h"
|
|
#include "TXPNode.h"
|
|
#include "TXPArchive.h"
|
|
#include "TXPPagedLOD.h"
|
|
#include "TXPSeamLOD.h"
|
|
#include "TileMapper.h"
|
|
|
|
#define ReaderWriterTXPERROR(s) osg::notify(osg::NOTICE) << "txp::ReaderWriterTXP::" << (s) << " error: "
|
|
|
|
|
|
|
|
namespace
|
|
{
|
|
char gbuf[2048];
|
|
}
|
|
|
|
using namespace txp;
|
|
|
|
int ReaderWriterTXP::_archiveId = 0;
|
|
|
|
osgDB::ReaderWriter::ReadResult ReaderWriterTXP::local_readNode(const std::string& file, const osgDB::ReaderWriter::Options* options)
|
|
{
|
|
|
|
std::string name = osgDB::getSimpleFileName(file);
|
|
|
|
// We load archive.txp
|
|
if (strncmp(name.c_str(),"archive",7)==0)
|
|
{
|
|
std::string fileName = osgDB::findDataFile( file, options );
|
|
if ( fileName.empty() )
|
|
return ReadResult::FILE_NOT_FOUND;
|
|
|
|
osg::ref_ptr<TXPNode> txpNode = new TXPNode;
|
|
txpNode->setArchiveName(fileName);
|
|
if (options)
|
|
{
|
|
txpNode->setOptions(options->getOptionString());
|
|
}
|
|
|
|
if (txpNode->loadArchive())
|
|
{
|
|
TXPArchive* archive = txpNode->getArchive();
|
|
if (archive)
|
|
{
|
|
int id = _archiveId++;
|
|
archive->setId(id);
|
|
// txpNode->setArchive(getArchive(id,osgDB::getFilePath(fileName)));
|
|
getArchive(id,osgDB::getFilePath(fileName));
|
|
}
|
|
return txpNode.get();
|
|
}
|
|
else
|
|
{
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
}
|
|
|
|
// We load tileLOD_XxY_ID.txp
|
|
else if (strncmp(name.c_str(),"tile",4)==0)
|
|
{
|
|
int x,y,lod;
|
|
unsigned int id;
|
|
sscanf(name.c_str(),"tile%d_%dx%d_%d",&lod,&x,&y,&id);
|
|
TXPArchive* archive = getArchive(id,osgDB::getFilePath(file));
|
|
|
|
// The way this is done a 'tile' should only be created for lod 0 only,
|
|
// something is wrong if this is no the case
|
|
if(lod != 0)
|
|
{
|
|
ReaderWriterTXPERROR("ReaderWriterTXP::local_readNode()") << "paged 'tile' should be at lod 0" << std::endl;
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
|
|
trpgEndian endian = archive->GetEndian();
|
|
archive->ReadSubArchive( 0, 0, endian);
|
|
archive->ReadSubArchive( y, x, endian);
|
|
|
|
// std::cout << "Attempted " << x << " " << y << std::endl;
|
|
|
|
TXPArchive::TileInfo info;
|
|
if (!archive->getTileInfo(x,y,lod,info))
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
|
|
std::vector<TXPArchive::TileLocationInfo> childrenLoc;
|
|
osg::ref_ptr<osg::Node> tileContent = getTileContent(info,x,y,lod,archive, childrenLoc);
|
|
|
|
tileContent->setName("TileContent");
|
|
|
|
bool asChildren = false;
|
|
std::string childrenInfoStr;
|
|
|
|
int numLods = archive->getNumLODs();
|
|
|
|
int majorVersion, minorVersion;
|
|
archive->GetVersion(majorVersion, minorVersion);
|
|
if(majorVersion ==2 && minorVersion >=1)
|
|
{
|
|
// Version 2.1 and over
|
|
// The tile table only contains lod 0 and the children
|
|
// info are stored in its parent. SO if we do not want
|
|
// to be forced to reparse the parent we need to save that
|
|
// info. For now we just add it to the node name
|
|
|
|
if(childrenLoc.size() > 0)
|
|
{
|
|
asChildren = true;
|
|
createChildrenLocationString(childrenLoc, childrenInfoStr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lod < (numLods-1))
|
|
asChildren = true;
|
|
}
|
|
|
|
if (asChildren)
|
|
{
|
|
char pagedLODfile[1024];
|
|
sprintf(pagedLODfile,"%s\\subtiles%d_%dx%d_%d",
|
|
archive->getDir(),
|
|
lod,
|
|
x,
|
|
y,
|
|
archive->getId());
|
|
|
|
strcat(pagedLODfile, childrenInfoStr.c_str());
|
|
strcat(pagedLODfile, ".txp");
|
|
|
|
|
|
// there are tile sets which do not maintain the z extents in
|
|
// the tile table. This attempt to address the issue by using
|
|
// the geometry bounding sphere. The downside is that this is
|
|
// not coupled to the generation and may result in runtime cracks
|
|
if (info.center.z() == 0)
|
|
{
|
|
osg::BoundingSphere bSphere = tileContent->getBound();
|
|
|
|
info.center.z() = bSphere.center().z();
|
|
info.radius = bSphere.radius();
|
|
}
|
|
|
|
|
|
osg::ref_ptr<TXPPagedLOD> pagedLOD = new TXPPagedLOD;
|
|
// note: use maximum(info.maxRange,1e7) as just maxRange would result in some corner tiles from being culled out.
|
|
pagedLOD->addChild(tileContent.get(),info.minRange,osg::maximum(info.maxRange,1e7));
|
|
pagedLOD->setFileName(1,pagedLODfile);
|
|
pagedLOD->setRange(1,0,info.minRange);
|
|
pagedLOD->setCenter(info.center);
|
|
pagedLOD->setRadius(info.radius);
|
|
pagedLOD->setPriorityOffset(0,numLods-lod);
|
|
pagedLOD->setPriorityScale(0,1.0f);
|
|
pagedLOD->setNumChildrenThatCannotBeExpired(1);
|
|
pagedLOD->setTileId(x,y,lod);
|
|
|
|
const trpgHeader* header = archive->GetHeader();
|
|
trpgHeader::trpgTileType tileType;
|
|
header->GetTileOriginType(tileType);
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
osg::Vec3d sw(info.bbox._min);
|
|
pagedLOD->setCenter(info.center - sw);
|
|
}
|
|
|
|
return pagedLOD.get();
|
|
}
|
|
else
|
|
return tileContent.get();
|
|
}
|
|
|
|
|
|
// For 2.0 and lower we load subtilesLOD_XxY_ID.txp
|
|
// For 2.1 and over we load subtilesLOD_XxY_ID_NBCHILD_{X_Y_FID_FOFFSET_ZMIN_ZMAX_X_Y_ADDR ....}.txp
|
|
else if (strncmp(name.c_str(),"sub",3)==0)
|
|
{
|
|
int x,y,lod;
|
|
unsigned int id;
|
|
sscanf(name.c_str(),"subtiles%d_%dx%d_%d",&lod,&x,&y,&id);
|
|
TXPArchive* archive = getArchive(id,osgDB::getFilePath(file));
|
|
|
|
int majorVersion, minorVersion;
|
|
archive->GetVersion(majorVersion, minorVersion);
|
|
|
|
std::vector<TXPArchive::TileLocationInfo> childrenLoc;
|
|
|
|
osg::ref_ptr<osg::Group> subtiles = new osg::Group;
|
|
|
|
int numLods = archive->getNumLODs();
|
|
|
|
if(majorVersion == 2 && minorVersion >= 1)
|
|
{
|
|
int nbChild;
|
|
|
|
sscanf(name.c_str(),"subtiles%d_%dx%d_%d_%d",&lod,&x,&y,&id, &nbChild);
|
|
std::vector<TXPArchive::TileLocationInfo> locs;
|
|
bool status = true;
|
|
status = extractChildrenLocations(name, lod, locs, nbChild);
|
|
if(majorVersion >= TRPG_NOMERGE_VERSION_MAJOR && minorVersion >=TRPG_NOMERGE_VERSION_MINOR && archive->GetHeader()->GetIsMaster())
|
|
{
|
|
for(int idx=0;idx<nbChild;idx++)
|
|
{
|
|
//figure out the block row/col
|
|
int blockx,blocky;
|
|
unsigned int denom = (1 << locs[idx].lod); // this should work up to lod 31
|
|
blockx = locs[idx].x/denom;
|
|
blocky = locs[idx].y/denom;
|
|
locs[idx].addr.col = blockx;
|
|
locs[idx].addr.row = blocky;
|
|
}
|
|
}
|
|
|
|
if(!status)
|
|
{
|
|
ReaderWriterTXPERROR("ReaderWriterTXP::local_readNode()") << "'subtile' filename children parsing failed " << std::endl;
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
|
|
const trpgHeader* header = archive->GetHeader();
|
|
trpgHeader::trpgTileType tileType;
|
|
header->GetTileOriginType(tileType);
|
|
|
|
TXPArchive::TileLocationInfo plInfo;
|
|
plInfo.x = x;
|
|
plInfo.y = y;
|
|
plInfo.lod = lod;
|
|
TXPArchive::TileInfo parentInfo;
|
|
archive->getTileInfo(plInfo,parentInfo);
|
|
|
|
for(int idx = 0; idx < nbChild; ++idx)
|
|
{
|
|
std::vector<TXPArchive::TileLocationInfo> childrenChildLoc;
|
|
|
|
TXPArchive::TileLocationInfo& loc = locs[idx];
|
|
|
|
TXPArchive::TileInfo info;
|
|
if (!archive->getTileInfo(loc,info))
|
|
continue;
|
|
|
|
osg::ref_ptr<osg::Node> tileContent = getTileContent(info, loc, archive, childrenChildLoc);
|
|
|
|
tileContent->setName("TileContent");
|
|
|
|
if(childrenChildLoc.size() > 0)
|
|
{
|
|
std::string childInfoStr;
|
|
createChildrenLocationString(childrenChildLoc, childInfoStr);
|
|
|
|
char pagedLODfile[1024];
|
|
sprintf(pagedLODfile,"%s\\subtiles%d_%dx%d_%d%s.txp",
|
|
archive->getDir(),
|
|
loc.lod,
|
|
loc.x,
|
|
loc.y,
|
|
archive->getId(),
|
|
childInfoStr.c_str());
|
|
|
|
// there are tile sets which do not maintain the z extents in
|
|
// the tile table. This attempt to address the issue by using
|
|
// the geometry bounding sphere. The downside is that this is
|
|
// not coupled to the generation and may result in runtime cracks
|
|
if (info.center.z() == 0)
|
|
{
|
|
osg::BoundingSphere bSphere = tileContent->getBound();
|
|
|
|
info.center.z() = bSphere.center().z();
|
|
info.radius = bSphere.radius();
|
|
}
|
|
|
|
osg::ref_ptr<TXPPagedLOD> pagedLOD = new TXPPagedLOD;
|
|
// note: use maximum(info.maxRange,1e7) as just maxRange would result in some corner tiles from being culled out.
|
|
pagedLOD->addChild(tileContent.get(),info.minRange,osg::maximum(info.maxRange,1e7));
|
|
pagedLOD->setFileName(1,pagedLODfile);
|
|
pagedLOD->setRange(1,0,info.minRange);
|
|
pagedLOD->setCenter(info.center);
|
|
pagedLOD->setRadius(info.radius);
|
|
pagedLOD->setPriorityOffset(0,numLods - loc.lod);
|
|
pagedLOD->setPriorityScale(0,1.0f);
|
|
pagedLOD->setNumChildrenThatCannotBeExpired(1);
|
|
pagedLOD->setTileId(loc.x, loc.y, loc.lod);
|
|
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
osg::Vec3d center(info.center - parentInfo.bbox._min);
|
|
osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
|
|
sw[2] = 0.0;
|
|
pagedLOD->setCenter(center - sw);
|
|
osg::Matrix offset;
|
|
offset.setTrans(sw);
|
|
osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
|
|
tform->addChild(pagedLOD.get());
|
|
subtiles->addChild(tform);
|
|
}
|
|
else
|
|
subtiles->addChild(pagedLOD.get());
|
|
subtiles->setUserData(new TileIdentifier(loc.x, loc.y, loc.lod)); // is this really needed?
|
|
}
|
|
else
|
|
{
|
|
subtiles->setUserData(new TileIdentifier(loc.x, loc.y, loc.lod));
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
osg::Vec3d center(info.center - parentInfo.bbox._min);
|
|
osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
|
|
sw[2] = 0.0;
|
|
osg::Matrix offset;
|
|
offset.setTrans(sw);
|
|
osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
|
|
tform->addChild(tileContent.get());
|
|
subtiles->addChild(tform);
|
|
}
|
|
else
|
|
subtiles->addChild(tileContent.get());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
int sizeX, sizeY;
|
|
archive->getLODSize(lod+1,sizeX,sizeY);
|
|
|
|
const trpgHeader* header = archive->GetHeader();
|
|
trpgHeader::trpgTileType tileType;
|
|
header->GetTileOriginType(tileType);
|
|
|
|
TXPArchive::TileInfo parentInfo;
|
|
archive->getTileInfo(x,y,lod,parentInfo);
|
|
|
|
for (int ix = 0; ix < 2; ix++)
|
|
{
|
|
for (int iy = 0; iy < 2; iy++)
|
|
{
|
|
int tileX = x*2+ix;
|
|
int tileY = y*2+iy;
|
|
int tileLOD = lod+1;
|
|
|
|
TXPArchive::TileInfo info;
|
|
if (!archive->getTileInfo(tileX,tileY,tileLOD,info))
|
|
continue;
|
|
|
|
osg::ref_ptr<osg::Node> tileContent = getTileContent(info,tileX,tileY,tileLOD,archive, childrenLoc);
|
|
|
|
tileContent->setName("TileContent");
|
|
|
|
if (tileLOD < (numLods-1))
|
|
{
|
|
char pagedLODfile[1024];
|
|
sprintf(pagedLODfile,"%s\\subtiles%d_%dx%d_%d.txp",
|
|
archive->getDir(),
|
|
tileLOD,
|
|
tileX,
|
|
tileY,
|
|
archive->getId());
|
|
|
|
// there are tile sets which do not maintain the z extents in
|
|
// the tile table. This attempt to address the issue by using
|
|
// the geometry bounding sphere. The downside is that this is
|
|
// not coupled to the generation and may result in runtime cracks
|
|
if (info.center.z() == 0)
|
|
{
|
|
osg::BoundingSphere bSphere = tileContent->getBound();
|
|
|
|
info.center.z() = bSphere.center().z();
|
|
info.radius = bSphere.radius();
|
|
}
|
|
|
|
osg::ref_ptr<TXPPagedLOD> pagedLOD = new TXPPagedLOD;
|
|
// note: use maximum(info.maxRange,1e7) as just maxRange would result in some corner tiles from being culled out.
|
|
pagedLOD->addChild(tileContent.get(),info.minRange,osg::maximum(info.maxRange,1e7));
|
|
pagedLOD->setFileName(1,pagedLODfile);
|
|
pagedLOD->setRange(1,0,info.minRange);
|
|
pagedLOD->setCenter(info.center);
|
|
pagedLOD->setRadius(info.radius);
|
|
pagedLOD->setPriorityOffset(0,numLods-lod);
|
|
pagedLOD->setPriorityScale(0,1.0f);
|
|
pagedLOD->setNumChildrenThatCannotBeExpired(1);
|
|
pagedLOD->setTileId(tileX,tileY,tileLOD);
|
|
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
osg::Vec3d center(info.center - parentInfo.bbox._min);
|
|
osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
|
|
sw[2] = 0.0;
|
|
pagedLOD->setCenter(center - sw);
|
|
osg::Matrix offset;
|
|
offset.setTrans(sw);
|
|
osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
|
|
tform->addChild(pagedLOD.get());
|
|
subtiles->addChild(tform);
|
|
}
|
|
else
|
|
subtiles->addChild(pagedLOD.get());
|
|
}
|
|
else
|
|
{
|
|
subtiles->setUserData(new TileIdentifier(tileX,tileY,tileLOD));
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
osg::Vec3d center(info.center - parentInfo.bbox._min);
|
|
osg::Vec3d sw(info.bbox._min - parentInfo.bbox._min);
|
|
sw[2] = 0.0;
|
|
osg::Matrix offset;
|
|
offset.setTrans(sw);
|
|
osg::MatrixTransform *tform = new osg::MatrixTransform(offset);
|
|
tform->addChild(tileContent.get());
|
|
subtiles->addChild(tform);
|
|
}
|
|
else
|
|
subtiles->addChild(tileContent.get());
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//osg::notify(osg::NOTICE) << "Subtiles for " << x << " " << y << " " << lod << " lodaded" << std::endl;
|
|
return subtiles.get();
|
|
}
|
|
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
|
|
// If you change this then you have to change extractChildrenLocation()
|
|
void ReaderWriterTXP::createChildrenLocationString(const std::vector<TXPArchive::TileLocationInfo>& locs, std::string& locString) const
|
|
{
|
|
std::stringstream theLoc;
|
|
|
|
if(locs.size() == 0)
|
|
{
|
|
theLoc << "_" << locs.size();
|
|
}
|
|
else
|
|
{
|
|
|
|
theLoc << "_" << locs.size() << "_" << "{" ;
|
|
|
|
for(unsigned int idx = 0; idx < locs.size(); ++idx)
|
|
{
|
|
const TXPArchive::TileLocationInfo& loc = locs[idx];
|
|
|
|
theLoc << loc.x
|
|
<< "_"
|
|
<< loc.y
|
|
<< "_"
|
|
<< loc.addr.file
|
|
<< "_"
|
|
<< loc.addr.offset
|
|
<< "_"
|
|
<< loc.zmin
|
|
<< "_"
|
|
<< loc.zmax;
|
|
if(idx != locs.size() -1)
|
|
theLoc << "_";
|
|
}
|
|
}
|
|
|
|
theLoc << "}" << std::ends;
|
|
|
|
locString = theLoc.str();
|
|
}
|
|
bool ReaderWriterTXP::extractChildrenLocations(const std::string& name, int parentLod, std::vector<TXPArchive::TileLocationInfo>& locs, int nbChild) const
|
|
{
|
|
locs.clear();
|
|
|
|
if(nbChild == 0)
|
|
return true;
|
|
|
|
locs.resize(nbChild);
|
|
|
|
// We look for '{', which should be the start of the list of {x,y,addr} children data
|
|
// '}' should end the list.
|
|
// We expect: X,Y,FID,FOFFSET,ZMIN,ZMAX
|
|
std::string::size_type startOfList = name.find_last_of('{');
|
|
if(startOfList == std::string::npos)
|
|
return false;
|
|
|
|
std::string::size_type endOfList = name.find_last_of('}');
|
|
if(endOfList == std::string::npos)
|
|
return false;
|
|
|
|
// Extract the data
|
|
strcpy(gbuf, name.substr(startOfList + 1, endOfList - startOfList - 1).c_str());
|
|
char *token = strtok( gbuf, "_" );
|
|
|
|
int nbTokenRead = 0;
|
|
for(int idx = 0; idx < nbChild; idx++)
|
|
{
|
|
// X
|
|
if(!token)
|
|
break;
|
|
locs[idx].x = atoi(token);
|
|
nbTokenRead++;
|
|
|
|
// Y
|
|
token = strtok(0, "_");
|
|
if(!token)
|
|
break;
|
|
locs[idx].y = atoi(token);
|
|
nbTokenRead++;
|
|
|
|
// FID
|
|
token = strtok(0, "_");
|
|
if(!token)
|
|
break;
|
|
locs[idx].addr.file = atoi(token);
|
|
nbTokenRead++;
|
|
|
|
// OFFSET
|
|
token = strtok(0, "_");
|
|
if(!token)
|
|
break;
|
|
locs[idx].addr.offset = atoi(token);
|
|
nbTokenRead++;
|
|
|
|
// ZMIN
|
|
token = strtok(0, "_");
|
|
if(!token)
|
|
break;
|
|
locs[idx].zmin = (float)atof(token);
|
|
nbTokenRead++;
|
|
|
|
// ZMAX
|
|
token = strtok(0, "_");
|
|
if(!token)
|
|
break;
|
|
locs[idx].zmax= (float)atof(token);
|
|
nbTokenRead++;
|
|
|
|
locs[idx].lod = parentLod+1;
|
|
|
|
|
|
|
|
token = strtok(0, "_");
|
|
}
|
|
|
|
if(nbTokenRead != nbChild*6)
|
|
return false;
|
|
else
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
TXPArchive *ReaderWriterTXP::getArchive(int id, const std::string& dir)
|
|
{
|
|
TXPArchive* archive = NULL;
|
|
|
|
std::map< int,osg::ref_ptr<TXPArchive> >::iterator iter = _archives.find(id);
|
|
|
|
|
|
if (iter != _archives.end())
|
|
{
|
|
archive = iter->second.get();
|
|
}
|
|
|
|
if (archive == NULL)
|
|
{
|
|
#ifdef _WIN32
|
|
const char _PATHD = '\\';
|
|
#elif defined(macintosh)
|
|
const char _PATHD = ':';
|
|
#else
|
|
const char _PATHD = '/';
|
|
#endif
|
|
std::string archiveName = dir+_PATHD+"archive.txp";
|
|
archive = new TXPArchive;
|
|
if (archive->openFile(archiveName) == false)
|
|
{
|
|
ReaderWriterTXPERROR("getArchive()") << "failed to load archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadMaterials() == false)
|
|
{
|
|
ReaderWriterTXPERROR("getArchive()") << "failed to load materials from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadModels() == false)
|
|
{
|
|
ReaderWriterTXPERROR("getArchive()") << "failed to load models from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadLightAttributes() == false)
|
|
{
|
|
ReaderWriterTXPERROR("getArchive()") << "failed to load light attributes from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadTextStyles() == false)
|
|
{
|
|
ReaderWriterTXPERROR("getArchive()") << "failed to load text styles from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
archive->setId(id);
|
|
|
|
_archives[id] = archive;
|
|
}
|
|
|
|
return archive;
|
|
}
|
|
|
|
class SeamFinder: public osg::NodeVisitor
|
|
{
|
|
public:
|
|
SeamFinder(int x, int y, int lod, const TXPArchive::TileInfo& info, TXPArchive *archive ):
|
|
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
|
_x(x), _y(y), _lod(lod), _info(info), _archive(archive)
|
|
{}
|
|
|
|
virtual void apply(osg::Group& group)
|
|
{
|
|
for (unsigned int i = 0; i < group.getNumChildren(); i++)
|
|
{
|
|
osg::Node* child = group.getChild(i);
|
|
osg::Node* seam = seamReplacement(child);
|
|
if (child != seam)
|
|
{
|
|
group.replaceChild(child,seam);
|
|
}
|
|
else
|
|
{
|
|
child->accept(*this);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
osg::Node* seamReplacement(osg::Node* node);
|
|
|
|
int _x, _y, _lod;
|
|
const TXPArchive::TileInfo& _info;
|
|
TXPArchive *_archive;
|
|
};
|
|
|
|
osg::Node* SeamFinder::seamReplacement(osg::Node* node)
|
|
{
|
|
osg::Group* group = node->asGroup();
|
|
if ( group == 0 )
|
|
return node;
|
|
|
|
std::vector<osg::Node*> nonSeamChildren;
|
|
osg::LOD* hiRes = 0;
|
|
osg::LOD* loRes = 0;
|
|
|
|
const trpgHeader* header = _archive->GetHeader();
|
|
trpgHeader::trpgTileType tileType;
|
|
header->GetTileOriginType(tileType);
|
|
|
|
for (unsigned int i = 0; i < group->getNumChildren(); i++)
|
|
{
|
|
osg::LOD* lod = dynamic_cast<osg::LOD*>(group->getChild(i));
|
|
if (lod == 0)
|
|
{
|
|
nonSeamChildren.push_back(group->getChild(i));
|
|
continue;
|
|
}
|
|
|
|
bool nonSeamChild = true;
|
|
|
|
// looks like the problem is in here - likely due to seamLOD info
|
|
// not being adjusted properly in tiled databases
|
|
// seam center is outside the bounding box of the tile
|
|
osg::Vec3 lodCenter = lod->getCenter();
|
|
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
trpg2dPoint tileExtents;
|
|
header->GetTileSize(0, tileExtents);
|
|
osg::BoundingBox bbox;
|
|
_archive->getExtents(bbox);
|
|
osg::Vec3 offset(0.0, 0.0, 0.0);
|
|
|
|
int divider = (0x1 << _lod);
|
|
// calculate which tile model is located in
|
|
tileExtents.x /= divider;
|
|
tileExtents.y /= divider;
|
|
offset[0] = _x*tileExtents.x;// + tileExtents.x*0.5;
|
|
offset[1] = _y*tileExtents.y;// + tileExtents.y*0.5;
|
|
lodCenter += offset;
|
|
}
|
|
|
|
if (!_info.bbox.contains(lodCenter))
|
|
{
|
|
// seams have center as the neighbour tile
|
|
osg::Vec3 d = _info.center - lodCenter;
|
|
if (((fabs(d.x())-_info.size.x()) > 0.0001) && ((fabs(d.y())-_info.size.y()) > 0.0001))
|
|
{
|
|
nonSeamChildren.push_back(lod);
|
|
continue;
|
|
}
|
|
|
|
// low res seam has min/max ranges of lod+1 range/lod 0 range
|
|
if ((fabs((float)_info.minRange-lod->getMinRange(0))<0.001)&&(fabs((float)_info.lod0Range-lod->getMaxRange(0))<0.001))
|
|
{
|
|
|
|
if (loRes==0)
|
|
{
|
|
loRes = lod;
|
|
nonSeamChild = false;
|
|
}
|
|
}
|
|
|
|
// hi res seam has min/max ranges of 0 range/lod+1 range
|
|
if ((lod->getMinRange(0)==0.0f)&&(fabs(_info.minRange-lod->getMaxRange(0))<0.001))
|
|
{
|
|
if (hiRes==0)
|
|
{
|
|
hiRes = lod;
|
|
nonSeamChild = false;
|
|
}
|
|
}
|
|
}
|
|
if (nonSeamChild)
|
|
{
|
|
nonSeamChildren.push_back(lod);
|
|
}
|
|
}
|
|
|
|
if (loRes)
|
|
{
|
|
int dx = 0;
|
|
int dy = 0;
|
|
int lod = _lod;
|
|
osg::Vec3 lodCenter = loRes->getCenter();
|
|
|
|
if(tileType == trpgHeader::TileLocal)
|
|
{
|
|
trpg2dPoint tileExtents;
|
|
header->GetTileSize(0, tileExtents);
|
|
osg::BoundingBox bbox;
|
|
_archive->getExtents(bbox);
|
|
osg::Vec3 offset(0.0, 0.0, 0.0);
|
|
|
|
int divider = (0x1 << _lod);
|
|
// calculate which tile model is located in
|
|
tileExtents.x /= divider;
|
|
tileExtents.y /= divider;
|
|
offset[0] = _x*tileExtents.x;// + tileExtents.x*0.5;
|
|
offset[1] = _y*tileExtents.y;// + tileExtents.y*0.5;
|
|
lodCenter += offset;
|
|
}
|
|
|
|
osg::Vec3 delta = lodCenter-_info.center;
|
|
if (fabs(delta.x())>fabs(delta.y()))
|
|
{
|
|
if ( delta.x() < 0.0 )
|
|
--dx; // west
|
|
else
|
|
dx++; // east
|
|
}
|
|
else
|
|
{
|
|
if ( delta.y() < 0.0 )
|
|
--dy; // south
|
|
else
|
|
++dy; // north
|
|
}
|
|
|
|
TXPSeamLOD* seam = new TXPSeamLOD(_x, _y, lod, dx, dy);
|
|
seam->setCenter(loRes->getCenter());
|
|
seam->addChild(loRes->getChild(0)); // low res
|
|
if (hiRes)
|
|
{
|
|
seam->addChild(hiRes->getChild(0)); // high res
|
|
}
|
|
|
|
if (nonSeamChildren.empty())
|
|
{
|
|
return seam;
|
|
}
|
|
else
|
|
{
|
|
osg::Group* newGroup = new osg::Group;
|
|
|
|
newGroup->addChild(seam);
|
|
|
|
for (unsigned int i = 0; i < nonSeamChildren.size(); i++)
|
|
newGroup->addChild(nonSeamChildren[i]);
|
|
|
|
return newGroup;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
osg::Node* ReaderWriterTXP::getTileContent(const TXPArchive::TileInfo &info, int x, int y, int lod, TXPArchive* archive, std::vector<TXPArchive::TileLocationInfo>& childrenLoc)
|
|
{
|
|
if ( archive == 0 )
|
|
return false;
|
|
|
|
int majorVersion, minorVersion;
|
|
archive->GetVersion(majorVersion, minorVersion);
|
|
|
|
double realMinRange = info.minRange;
|
|
double realMaxRange = info.maxRange;
|
|
double usedMaxRange = osg::maximum(info.maxRange,1e7);
|
|
osg::Vec3 tileCenter;
|
|
osg::Group* tileGroup = archive->getTileContent(x,y,lod,realMinRange,realMaxRange,usedMaxRange,tileCenter, childrenLoc);
|
|
|
|
// if group has only one child, then simply use its child.
|
|
while (tileGroup->getNumChildren()==1 && tileGroup->getChild(0)->asGroup())
|
|
{
|
|
tileGroup = tileGroup->getChild(0)->asGroup();
|
|
}
|
|
|
|
bool doSeam = false;
|
|
if(majorVersion == 2 && minorVersion >= 1)
|
|
doSeam = (childrenLoc.size() > 0);
|
|
else
|
|
doSeam = (lod < (archive->getNumLODs() - 1));
|
|
|
|
// Handle seams
|
|
if (doSeam)
|
|
{
|
|
SeamFinder sfv(x,y,lod,info,archive);
|
|
tileGroup->accept(sfv);
|
|
}
|
|
|
|
return tileGroup;
|
|
}
|
|
|
|
// this version only gets called if the TXP version is >= than 2.1
|
|
osg::Node* ReaderWriterTXP::getTileContent(const TXPArchive::TileInfo &info, const TXPArchive::TileLocationInfo& loc, TXPArchive* archive, std::vector<TXPArchive::TileLocationInfo>& childrenLoc)
|
|
{
|
|
if ( archive == 0 )
|
|
return false;
|
|
|
|
// int numLods = archive->getNumLODs();
|
|
|
|
double realMinRange = info.minRange;
|
|
double realMaxRange = info.maxRange;
|
|
double usedMaxRange = osg::maximum(info.maxRange,1e7);
|
|
osg::Vec3 tileCenter;
|
|
osg::Group* tileGroup = archive->getTileContent(loc,realMinRange,realMaxRange,usedMaxRange,tileCenter, childrenLoc);
|
|
|
|
// if group has only one child, then simply use its child.
|
|
while (tileGroup->getNumChildren()==1 && tileGroup->getChild(0)->asGroup())
|
|
{
|
|
tileGroup = tileGroup->getChild(0)->asGroup();
|
|
}
|
|
|
|
// Handle seams
|
|
if (childrenLoc.size() > 0)
|
|
{
|
|
SeamFinder sfv(loc.x, loc.y, loc.lod, info, archive);
|
|
tileGroup->accept(sfv);
|
|
}
|
|
|
|
return tileGroup;
|
|
}
|
|
|
|
osgDB::RegisterReaderWriterProxy<ReaderWriterTXP> g_txpReaderWriterProxy;
|
|
|