941 lines
31 KiB
C++
941 lines
31 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "ReaderWriterTXP.h"
|
|
#include "TXPNode.h"
|
|
#include "TXPArchive.h"
|
|
#include "TXPPagedLOD.h"
|
|
#include "TXPSeamLOD.h"
|
|
#include "TileMapper.h"
|
|
|
|
#define ReaderWriterTXPERROR(s) OSG_NOTICE << "txp::ReaderWriterTXP::" << (s) << " error: "
|
|
|
|
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());
|
|
}
|
|
|
|
|
|
//modified by Brad Anderegg on May-27-08
|
|
//calling getArchive will create a new TXPArchive if the specified one does not exist
|
|
//we will set our osgdb loader options on the archive and set the appropriate archive on
|
|
//the txpNode.
|
|
int id = ++_archiveId;
|
|
osg::ref_ptr< TXPArchive > archive = createArchive(id,osgDB::getFilePath(fileName));
|
|
|
|
if (archive != NULL)
|
|
{
|
|
archive->setId(id);
|
|
|
|
if (options && options->getOptionString().find("loadMaterialsToStateSet")!=std::string::npos)
|
|
{
|
|
archive->SetMaterialAttributesToStateSetVar(true);
|
|
}
|
|
|
|
txpNode->loadArchive(archive.get());
|
|
|
|
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;
|
|
if (sscanf(name.c_str(),"tile%d_%dx%d_%u",&lod,&x,&y,&id)!=4)
|
|
{
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
|
|
osg::ref_ptr< TXPArchive > archive = getArchive(id,osgDB::getFilePath(file));
|
|
if (archive == NULL)
|
|
return ReadResult::ERROR_IN_READING_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.get(), 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)
|
|
{
|
|
|
|
std::stringstream pagedLODfile;
|
|
pagedLODfile<<archive->getDir()<<"\\subtiles"<<lod<<"_"<<x<<"x"<<y<<"_"<<archive->getId()<<childrenInfoStr<<".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.str());
|
|
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;
|
|
if (sscanf(name.c_str(),"subtiles%d_%dx%d_%u",&lod,&x,&y,&id)!=4)
|
|
{
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
|
|
osg::ref_ptr< TXPArchive > archive = getArchive(id,osgDB::getFilePath(file));
|
|
if (archive == NULL)
|
|
{
|
|
return ReadResult::ERROR_IN_READING_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;
|
|
|
|
int numItemsRead = sscanf(name.c_str(),"subtiles%d_%dx%d_%u_%d",&lod,&x,&y,&id, &nbChild);
|
|
if (numItemsRead!=5)
|
|
{
|
|
ReaderWriterTXPERROR("ReaderWriterTXP::local_readNode()") << "'subtile' filename children parsing failed " << std::endl;
|
|
return ReadResult::ERROR_IN_READING_FILE;
|
|
}
|
|
|
|
|
|
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.get(), childrenChildLoc);
|
|
|
|
tileContent->setName("TileContent");
|
|
|
|
subtiles->setUserData(new TileIdentifier(loc.x, loc.y, loc.lod)); // is this really needed?
|
|
|
|
if(childrenChildLoc.size() > 0)
|
|
{
|
|
std::string childInfoStr;
|
|
createChildrenLocationString(childrenChildLoc, childInfoStr);
|
|
|
|
char pagedLODfile[1084];
|
|
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());
|
|
}
|
|
}
|
|
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.get(), childrenLoc);
|
|
|
|
tileContent->setName("TileContent");
|
|
|
|
if (tileLOD < (numLods-1))
|
|
{
|
|
char pagedLODfile[1094];
|
|
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_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
|
|
std::string gbuf(name.substr(startOfList + 1, endOfList - startOfList - 1));
|
|
char *token = gbuf.empty() ? 0: strtok( &gbuf[0], "_" );
|
|
|
|
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 = osg::asciiToFloat(token);
|
|
nbTokenRead++;
|
|
|
|
// ZMAX
|
|
token = strtok(0, "_");
|
|
if(!token)
|
|
break;
|
|
locs[idx].zmax = osg::asciiToFloat(token);
|
|
nbTokenRead++;
|
|
|
|
locs[idx].lod = parentLod+1;
|
|
|
|
|
|
|
|
token = strtok(0, "_");
|
|
}
|
|
|
|
if(nbTokenRead != nbChild*6)
|
|
return false;
|
|
else
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
std::string ReaderWriterTXP::getArchiveName(const std::string& dir)
|
|
{
|
|
#ifdef _WIN32
|
|
const char _PATHD = '\\';
|
|
#elif defined(macintosh)
|
|
const char _PATHD = ':';
|
|
#else
|
|
const char _PATHD = '/';
|
|
#endif
|
|
|
|
return dir+_PATHD+"archive.txp";
|
|
}
|
|
|
|
osg::ref_ptr< TXPArchive > ReaderWriterTXP::getArchive(int id, const std::string& dir)
|
|
{
|
|
osg::ref_ptr< TXPArchive > archive = NULL;
|
|
|
|
std::map< int,osg::ref_ptr<TXPArchive> >::iterator iter = _archives.find(id);
|
|
|
|
if (iter != _archives.end())
|
|
{
|
|
archive = iter->second.get();
|
|
}
|
|
else
|
|
{
|
|
std::string archiveName = getArchiveName(dir);
|
|
ReaderWriterTXPERROR("getArchive()") << "archive id " << id << " not found: \"" << archiveName << "\"" << std::endl;
|
|
}
|
|
return archive;
|
|
}
|
|
|
|
osg::ref_ptr< TXPArchive > ReaderWriterTXP::createArchive(int id, const std::string& dir)
|
|
{
|
|
std::string archiveName = getArchiveName(dir);
|
|
|
|
osg::ref_ptr< TXPArchive > archive = getArchive(id, dir);
|
|
if (archive != NULL)
|
|
{
|
|
ReaderWriterTXPERROR("createArchive()") << "archive id " << id << " already exists: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
archive = new TXPArchive;
|
|
if (archive->openFile(archiveName) == false)
|
|
{
|
|
ReaderWriterTXPERROR("createArchive()") << "failed to load archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadMaterials() == false)
|
|
{
|
|
ReaderWriterTXPERROR("createArchive()") << "failed to load materials from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadModels() == false)
|
|
{
|
|
ReaderWriterTXPERROR("createArchive()") << "failed to load models from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadLightAttributes() == false)
|
|
{
|
|
ReaderWriterTXPERROR("createArchive()") << "failed to load light attributes from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
if (archive->loadTextStyles() == false)
|
|
{
|
|
ReaderWriterTXPERROR("createArchive()") << "failed to load text styles from archive: \"" << archiveName << "\"" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
archive->setId(id);
|
|
|
|
_archives[id] = archive;
|
|
|
|
return archive;
|
|
}
|
|
|
|
bool ReaderWriterTXP::removeArchive( int id )
|
|
{
|
|
OSG_INFO<<"ReaderWriterTXP::removeArchive(id="<<id<<")"<<std::endl;
|
|
//return (_archives.erase(id) >= 1);
|
|
bool result=_archives.erase(id) >= 1;
|
|
OSG_WARN<<"remove archive " << id << " size " << _archives.size()
|
|
<< " result " << result << std::endl;
|
|
return result;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
SeamFinder& operator = (const SeamFinder&) { return *this; }
|
|
|
|
int _x, _y, _lod;
|
|
const TXPArchive::TileInfo& _info;
|
|
TXPArchive *_archive;
|
|
};
|
|
|
|
#define equalDoubles(a, b) (fabs(a-b) < 0.001)
|
|
|
|
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))
|
|
{
|
|
const osg::LOD::RangeList& rangeList = lod->getRangeList();
|
|
if (!rangeList.size())
|
|
{
|
|
// TODO: Warn here
|
|
continue;
|
|
}
|
|
|
|
TXPArchive::TileInfo lod_plus_one_info;
|
|
if (!this->_archive->getTileInfo(_x,_y,_lod+1,lod_plus_one_info))
|
|
{
|
|
// TODO: Warn here
|
|
continue;
|
|
}
|
|
|
|
double lod_plus_oneSwitchInDistance = lod_plus_one_info.maxRange;
|
|
double lod0SwitchInDistance = _info.lod0Range;
|
|
|
|
// low res seam has min/max ranges of lod+1 range/lod 0 range
|
|
if (equalDoubles(lod_plus_oneSwitchInDistance,rangeList.at(0).first) && equalDoubles(lod0SwitchInDistance,rangeList.at(0).second))
|
|
{
|
|
if (loRes==0)
|
|
{
|
|
loRes = lod;
|
|
nonSeamChild = false;
|
|
}
|
|
}
|
|
else
|
|
// hi res seam has min/max ranges of 0 range/lod+1 range
|
|
if (rangeList.at(0).first==0.0 && equalDoubles(lod_plus_oneSwitchInDistance,rangeList.at(0).second))
|
|
{
|
|
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 0;
|
|
|
|
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.
|
|
// if the node is a transform, then stop processing so as to not loose the transformation
|
|
while (tileGroup && !tileGroup->asTransform() &&
|
|
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 (tileGroup && 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 0;
|
|
|
|
// 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.
|
|
// if the node is a transform, then stop processing so as to not loose the transformation
|
|
while (tileGroup && !tileGroup->asTransform() &&
|
|
tileGroup->getNumChildren()==1 && tileGroup->getChild(0)->asGroup())
|
|
{
|
|
tileGroup = tileGroup->getChild(0)->asGroup();
|
|
}
|
|
|
|
// Handle seams
|
|
if (tileGroup && childrenLoc.size() > 0)
|
|
{
|
|
SeamFinder sfv(loc.x, loc.y, loc.lod, info, archive);
|
|
tileGroup->accept(sfv);
|
|
}
|
|
|
|
return tileGroup;
|
|
}
|
|
|
|
REGISTER_OSGPLUGIN(txp, ReaderWriterTXP)
|
|
|