955 lines
27 KiB
C++
955 lines
27 KiB
C++
#include <osg/Notify>
|
|
#include <osg/Geode>
|
|
#include <osg/ShapeDrawable>
|
|
#include <osg/AlphaFunc>
|
|
#include <osg/Group>
|
|
#include <osg/Image>
|
|
#include <osg/Texture2D>
|
|
#include <osg/Material>
|
|
#include <osg/TexEnv>
|
|
#include <osg/CullFace>
|
|
#include <osg/Light>
|
|
#include <osg/StateSet>
|
|
#include <osg/Point>
|
|
#include <osg/BlendFunc>
|
|
#include <osg/MatrixTransform>
|
|
#include <osgDB/FileUtils>
|
|
#include <osgDB/FileUtils>
|
|
#include <osgDB/FileNameUtils>
|
|
#include <osgDB/fstream>
|
|
#include <osgDB/ReadFile>
|
|
#include <osgDB/WriteFile>
|
|
#include <osgSim/Sector>
|
|
#include <osgSim/LightPoint>
|
|
#include <osgSim/LightPointNode>
|
|
#include <osgSim/BlinkSequence>
|
|
#include <iostream>
|
|
|
|
#if defined(linux)
|
|
# include <unistd.h>
|
|
# define _access access
|
|
#endif
|
|
|
|
#include "TXPArchive.h"
|
|
#include "TXPParser.h"
|
|
#include "TileMapper.h"
|
|
|
|
#include <OpenThreads/ScopedLock>
|
|
|
|
using namespace txp;
|
|
|
|
#define TXPArchiveERROR(s) osg::notify(osg::NOTICE) << "txp::TXPArchive::" << (s) << " error: "
|
|
|
|
|
|
void TXPArchive::SetTexMap(int key,osg::ref_ptr<osg::Texture2D> ref)
|
|
{
|
|
_texmap[key] = ref;
|
|
}
|
|
|
|
osg::ref_ptr<osg::Texture2D> TXPArchive::GetTexMapEntry(int key)
|
|
{
|
|
return _texmap[key];
|
|
}
|
|
|
|
void TXPArchive::SetStatesMap(int key,osg::ref_ptr<osg::StateSet> ref)
|
|
{
|
|
_statesMap[key] = ref;
|
|
}
|
|
|
|
osg::ref_ptr<osg::StateSet> TXPArchive::GetStatesMapEntry(int key)
|
|
{
|
|
return _statesMap[key];
|
|
}
|
|
|
|
TXPArchive::TXPArchive():
|
|
trpgr_Archive(),
|
|
_id(-1),
|
|
_numLODs(0),
|
|
_swExtents(0.0,0.0),
|
|
_neExtents(0.0,0.0),
|
|
_majorVersion(-1),
|
|
_minorVersion(-1),
|
|
_isMaster(false),
|
|
_loadMaterialsToStateSet(false)
|
|
{
|
|
}
|
|
|
|
TXPArchive::~TXPArchive()
|
|
{
|
|
CloseFile();
|
|
}
|
|
|
|
|
|
void TXPArchive::getExtents(osg::BoundingBox& extents)
|
|
{
|
|
TileInfo sw, ne;
|
|
trpg2iPoint tileExtents;
|
|
|
|
this->GetHeader()->GetLodSize(0, tileExtents);
|
|
this->getTileInfo(0, 0, 0, sw);
|
|
this->getTileInfo(tileExtents.x-1, tileExtents.y-1, 0, ne);
|
|
extents.set(sw.bbox._min, sw.bbox._max);
|
|
extents.expandBy(ne.bbox);
|
|
}
|
|
|
|
|
|
bool TXPArchive::openFile(const std::string& archiveName)
|
|
{
|
|
std::string path = osgDB::getFilePath(archiveName);
|
|
std::string name = osgDB::getSimpleFileName(archiveName);
|
|
|
|
if(path.empty())
|
|
{
|
|
SetDirectory(".");
|
|
}
|
|
else
|
|
{
|
|
// push the path to the front of the list so that all subsequenct
|
|
// files get loaded relative to this if possible.
|
|
osgDB::getDataFilePathList().push_front(path);
|
|
SetDirectory(path.c_str());
|
|
}
|
|
|
|
if (!OpenFile(name.c_str()))
|
|
{
|
|
TXPArchiveERROR("openFile()") << "couldn't open archive: " << archiveName << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (!ReadHeader(false))
|
|
{
|
|
TXPArchiveERROR("openFile()") << "couldn't read header for archive: " << archiveName << std::endl;
|
|
return false;
|
|
}
|
|
|
|
const trpgHeader *header = GetHeader();
|
|
if (header)
|
|
{
|
|
header->GetNumLods(_numLODs);
|
|
header->GetExtents(_swExtents,_neExtents);
|
|
header->GetVersion(_majorVersion, _minorVersion);
|
|
_isMaster = header->GetIsMaster();
|
|
}
|
|
|
|
int numTextures;
|
|
texTable.GetNumTextures(numTextures);
|
|
|
|
int numModel;
|
|
modelTable.GetNumModels(numModel);
|
|
_models.clear();
|
|
|
|
int numMaterials;
|
|
materialTable.GetNumMaterial(numMaterials);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TXPArchive::loadMaterial(int ix)
|
|
{
|
|
int i = ix;
|
|
|
|
|
|
if (GetStatesMapEntry(ix).get())
|
|
return true;
|
|
|
|
osg::StateSet* osg_state_set = new osg::StateSet;
|
|
|
|
const trpgMaterial *mat;
|
|
mat = materialTable.GetMaterialRef(0,i);
|
|
|
|
// Set texture
|
|
int numMatTex;
|
|
mat->GetNumTexture(numMatTex);
|
|
|
|
// TODO : multitextuting
|
|
// also note that multitexturing in terrapage can came from two sides
|
|
// - multiple textures per material, and multiple materials per geometry
|
|
// Note: Only in theory. The only type you'll encounter is multiple
|
|
// materials per polygon.
|
|
if( numMatTex )
|
|
{
|
|
osg::Material *osg_material = new osg::Material;
|
|
|
|
float64 alpha;
|
|
mat->GetAlpha(alpha);
|
|
|
|
trpgColor color;
|
|
mat->GetAmbient(color);
|
|
osg_material->setAmbient( osg::Material::FRONT_AND_BACK ,
|
|
osg::Vec4(color.red, color.green, color.blue, alpha));
|
|
mat->GetDiffuse(color);
|
|
osg_material->setDiffuse(osg::Material::FRONT_AND_BACK ,
|
|
osg::Vec4(color.red, color.green, color.blue, alpha));
|
|
|
|
mat->GetSpecular(color);
|
|
osg_material->setSpecular(osg::Material::FRONT_AND_BACK ,
|
|
osg::Vec4(color.red, color.green, color.blue, alpha));
|
|
mat->GetEmission(color);
|
|
osg_material->setEmission(osg::Material::FRONT_AND_BACK ,
|
|
osg::Vec4(color.red, color.green, color.blue, alpha));
|
|
|
|
float64 shinines;
|
|
mat->GetShininess(shinines);
|
|
osg_material->setShininess(osg::Material::FRONT_AND_BACK , (float)shinines);
|
|
|
|
osg_material->setAlpha(osg::Material::FRONT_AND_BACK ,(float)alpha);
|
|
osg_state_set->setAttributeAndModes(osg_material, osg::StateAttribute::ON);
|
|
|
|
SetUserDataToMaterialAttributes(*osg_state_set, *mat);
|
|
|
|
if( alpha < 1.0f )
|
|
{
|
|
osg_state_set->setMode(GL_BLEND,osg::StateAttribute::ON);
|
|
osg_state_set->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
|
}
|
|
|
|
int alphaFunc;
|
|
mat->GetAlphaFunc(alphaFunc);
|
|
if( alphaFunc>=GL_NEVER && alphaFunc<=GL_ALWAYS)
|
|
{
|
|
float64 ref;
|
|
mat->GetAlphaRef(ref);
|
|
osg::AlphaFunc *osg_alpha_func = new osg::AlphaFunc;
|
|
osg_alpha_func->setFunction((osg::AlphaFunc::ComparisonFunction)alphaFunc,(float)ref);
|
|
osg_state_set->setAttributeAndModes(osg_alpha_func, osg::StateAttribute::ON);
|
|
}
|
|
|
|
for (int ntex = 0; ntex < numMatTex; ntex ++ )
|
|
{
|
|
int texId;
|
|
trpgTextureEnv texEnv;
|
|
mat->GetTexture(ntex,texId,texEnv);
|
|
|
|
// Set up texture environment
|
|
osg::TexEnv *osg_texenv = new osg::TexEnv();
|
|
int32 te_mode;
|
|
texEnv.GetEnvMode(te_mode);
|
|
switch( te_mode )
|
|
{
|
|
case trpgTextureEnv::Alpha :
|
|
osg_texenv->setMode(osg::TexEnv::REPLACE);
|
|
break;
|
|
case trpgTextureEnv::Decal:
|
|
osg_texenv->setMode(osg::TexEnv::DECAL);
|
|
break;
|
|
case trpgTextureEnv::Blend :
|
|
osg_texenv->setMode(osg::TexEnv::BLEND);
|
|
break;
|
|
case trpgTextureEnv::Modulate :
|
|
osg_texenv->setMode(osg::TexEnv::MODULATE);
|
|
break;
|
|
}
|
|
|
|
osg_state_set->setTextureAttribute(ntex,osg_texenv);
|
|
|
|
int wrap_s, wrap_t;
|
|
texEnv.GetWrap(wrap_s, wrap_t);
|
|
|
|
loadTexture(texId);
|
|
osg::Texture2D* osg_texture = GetTexMapEntry(texId).get();
|
|
if(osg_texture)
|
|
{
|
|
|
|
osg_texture->setWrap(osg::Texture2D::WRAP_S, wrap_s == trpgTextureEnv::Repeat ? osg::Texture2D::REPEAT: osg::Texture2D::CLAMP_TO_EDGE );
|
|
osg_texture->setWrap(osg::Texture2D::WRAP_T, wrap_t == trpgTextureEnv::Repeat ? osg::Texture2D::REPEAT: osg::Texture2D::CLAMP_TO_EDGE );
|
|
|
|
// -----------
|
|
// Min filter
|
|
// -----------
|
|
int32 minFilter;
|
|
texEnv.GetMinFilter(minFilter);
|
|
switch (minFilter)
|
|
{
|
|
case trpgTextureEnv::Point:
|
|
case trpgTextureEnv::Nearest:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
|
break;
|
|
case trpgTextureEnv::Linear:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
|
|
break;
|
|
case trpgTextureEnv::MipmapPoint:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST_MIPMAP_NEAREST);
|
|
break;
|
|
case trpgTextureEnv::MipmapLinear:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST_MIPMAP_LINEAR);
|
|
break;
|
|
case trpgTextureEnv::MipmapBilinear:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST);
|
|
break;
|
|
case trpgTextureEnv::MipmapTrilinear:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_LINEAR);
|
|
break;
|
|
default:
|
|
osg_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
|
|
break;
|
|
}
|
|
|
|
|
|
// -----------
|
|
// Mag filter
|
|
// -----------
|
|
int32 magFilter;
|
|
texEnv.GetMagFilter(magFilter);
|
|
switch (magFilter)
|
|
{
|
|
case trpgTextureEnv::Point:
|
|
case trpgTextureEnv::Nearest:
|
|
osg_texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
|
|
break;
|
|
case trpgTextureEnv::Linear:
|
|
default:
|
|
osg_texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
|
|
break;
|
|
}
|
|
|
|
// pass on to the stateset.
|
|
osg_state_set->setTextureAttributeAndModes(ntex,osg_texture, osg::StateAttribute::ON);
|
|
|
|
if(osg_texture->getImage() && osg_texture->getImage()->isImageTranslucent())
|
|
{
|
|
osg_state_set->setMode(GL_BLEND,osg::StateAttribute::ON);
|
|
osg_state_set->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
|
}
|
|
}
|
|
}
|
|
|
|
int cullMode;
|
|
mat->GetCullMode(cullMode);
|
|
|
|
// Culling mode in txp means opposite from osg i.e. Front-> show front face
|
|
if( cullMode != trpgMaterial::FrontAndBack)
|
|
{
|
|
osg::CullFace* cull_face = new osg::CullFace;
|
|
switch (cullMode)
|
|
{
|
|
case trpgMaterial::Front:
|
|
cull_face->setMode(osg::CullFace::BACK);
|
|
break;
|
|
case trpgMaterial::Back:
|
|
cull_face->setMode(osg::CullFace::FRONT);
|
|
break;
|
|
}
|
|
osg_state_set->setAttributeAndModes(cull_face, osg::StateAttribute::ON);
|
|
}
|
|
}
|
|
SetStatesMap(i,osg_state_set);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TXPArchive::loadMaterials()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool TXPArchive::loadTexture(int i)
|
|
{
|
|
if (GetTexMapEntry(i).get())
|
|
return true;
|
|
|
|
bool separateGeo = false;
|
|
int majorVer,minorVer;
|
|
GetVersion(majorVer,minorVer);
|
|
if((majorVer >= TRPG_NOMERGE_VERSION_MAJOR) && (minorVer>=TRPG_NOMERGE_VERSION_MINOR))
|
|
{
|
|
separateGeo = true;
|
|
}
|
|
trpgrImageHelper image_helper(this->GetEndian(),getDir(),materialTable,texTable,separateGeo);
|
|
|
|
const trpgTexture *tex;
|
|
tex = texTable.GetTextureRef(i);
|
|
if (!tex)
|
|
return false;
|
|
|
|
trpgTexture::ImageMode mode;
|
|
tex->GetImageMode(mode);
|
|
if(mode == trpgTexture::External)
|
|
{
|
|
char texName[ 1024 ];
|
|
texName[ 0 ] = 0;
|
|
tex->GetName(texName,1023);
|
|
|
|
// Create a texture by name.
|
|
osg::ref_ptr<osg::Texture2D> osg_texture = new osg::Texture2D();
|
|
|
|
// make sure the Texture unref's the Image after apply, when it is no longer needed.
|
|
osg_texture->setUnRefImageDataAfterApply(true);
|
|
|
|
// Load Texture and Create Texture State
|
|
std::string filename = osgDB::getSimpleFileName(texName);
|
|
std::string path(getDir());
|
|
#ifdef _WIN32
|
|
const char _PATHD = '\\';
|
|
#elif defined(macintosh)
|
|
const char _PATHD = ':';
|
|
#else
|
|
const char _PATHD = '/';
|
|
#endif
|
|
if( path == "." )
|
|
path = "";
|
|
else
|
|
path += _PATHD ;
|
|
|
|
std::string theFile = path + filename ;
|
|
osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(theFile);
|
|
if (image.valid())
|
|
{
|
|
osg_texture->setImage(image.get());
|
|
}
|
|
else
|
|
{
|
|
osg::notify(osg::WARN) << "TrPageArchive::LoadMaterials() error: "
|
|
<< "couldn't open image: " << filename << std::endl;
|
|
}
|
|
SetTexMap(i,osg_texture);
|
|
}
|
|
else if( mode == trpgTexture::Local )
|
|
{
|
|
SetTexMap(i,getLocalTexture(image_helper,tex));
|
|
}
|
|
else if( mode == trpgTexture::Template )
|
|
{
|
|
SetTexMap(i,0L);
|
|
}
|
|
else
|
|
{
|
|
SetTexMap(i,0L);
|
|
}
|
|
|
|
return (GetTexMapEntry(i).get() != 0);
|
|
}
|
|
|
|
bool TXPArchive::loadModel(int ix)
|
|
{
|
|
trpgModel *mod = modelTable.GetModelRef(ix);
|
|
int type;
|
|
if(!mod)
|
|
return false;
|
|
mod->GetType(type);
|
|
|
|
// Only dealing with external models currently
|
|
if (type == trpgModel::External)
|
|
{
|
|
char name[1024];
|
|
mod->GetName(name,1023);
|
|
|
|
// Load the model. It's probably not TerraPage
|
|
osg::Node *osg_model = osgDB::readNodeFile( name );
|
|
if ( !osg_model )
|
|
{
|
|
osg::notify(osg::WARN) << "TrPageArchive::LoadModels() error: "
|
|
<< "failed to load model: "
|
|
<< name << std::endl;
|
|
}
|
|
// Do this even if it's NULL
|
|
_models[ ix ] = osg_model;
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
trpgMemReadBuffer buf(GetEndian());
|
|
mod->Read(buf);
|
|
Group *osg_model = parse->ParseScene(buf, m_gstates , m_models);
|
|
m_models.push_back(osg_model);
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
bool TXPArchive::loadModels()
|
|
{
|
|
osg::notify(osg::INFO) << "txp:: Loading models ..." << std::endl;
|
|
|
|
int numModel;
|
|
modelTable.GetNumModels(numModel);
|
|
|
|
// use a pointer to the models map to bootstrap our map
|
|
trpgModelTable::ModelMapType *mt = modelTable.GetModelMap();
|
|
trpgModelTable::ModelMapType::iterator itr = mt->begin();
|
|
for ( ; itr != mt->end( ); itr++)
|
|
{
|
|
loadModel(itr->first);
|
|
}
|
|
osg::notify(osg::INFO) << "txp:: ... done." << std::endl;
|
|
return true;
|
|
}
|
|
|
|
bool TXPArchive::loadLightAttributes()
|
|
{
|
|
osg::notify(osg::INFO) << "txp:: Loading light attributes ..." << std::endl;
|
|
|
|
trpgLightTable::LightMapType *lm = lightTable.getLightMap();
|
|
trpgLightTable::LightMapType::iterator itr = lm->begin();
|
|
for ( ; itr != lm->end() ; itr++)
|
|
{
|
|
trpgLightAttr* ref = &itr->second;
|
|
|
|
osgSim::LightPointNode* osgLight = new osgSim::LightPointNode();
|
|
|
|
osg::Point* osgPoint = new osg::Point();
|
|
|
|
osgSim::LightPoint lp ;
|
|
lp._on = true;
|
|
|
|
trpgColor col;
|
|
ref->GetFrontColor(col);
|
|
lp._color = osg::Vec4(col.red, col.green,col.blue, 1.0);
|
|
|
|
float64 inten;
|
|
ref->GetFrontIntensity(inten);
|
|
lp._intensity = inten;
|
|
|
|
trpgLightAttr::PerformerAttr perfAttr;
|
|
ref->GetPerformerAttr(perfAttr);
|
|
|
|
// point part
|
|
//osgPoint->setSize(perfAttr.actualSize);
|
|
osgPoint->setSize(5);
|
|
osgPoint->setMaxSize(perfAttr.maxPixelSize);
|
|
osgPoint->setMinSize(perfAttr.minPixelSize);
|
|
osgPoint->setFadeThresholdSize(perfAttr.transparentFallofExp);
|
|
//numbers that are going to appear are "experimental"
|
|
osgPoint->setDistanceAttenuation(osg::Vec3(0.0001, 0.0005, 0.00000025));
|
|
// osgPoint->setDistanceAttenuation(osg::Vec3(1.0, 0.0, 1.0));
|
|
|
|
osg::StateSet* stateSet = new osg::StateSet();
|
|
stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
|
stateSet->setMode(GL_POINT_SMOOTH, osg::StateAttribute::ON);
|
|
stateSet->setAttributeAndModes(osgPoint, osg::StateAttribute::ON );
|
|
stateSet->setAttributeAndModes(new osg::BlendFunc, osg::StateAttribute::ON);
|
|
|
|
osgLight->setMaxPixelSize(perfAttr.maxPixelSize);
|
|
osgLight->setMinPixelSize(perfAttr.minPixelSize);
|
|
|
|
// float64 clamp;
|
|
// ref->GetPerformerTpClamp(clamp);
|
|
// osgLight->setMaxVisibleDistance2(clamp);
|
|
|
|
trpg3dPoint normal;
|
|
ref->GetNormal(normal);
|
|
|
|
// lp._radius = clamp;
|
|
|
|
trpgLightAttr::LightDirectionality direc;
|
|
ref->GetDirectionality(direc);
|
|
if( direc == trpgLightAttr::trpg_Unidirectional)
|
|
{
|
|
osgSim::AzimElevationSector* sec = new osgSim::AzimElevationSector();
|
|
float64 tmp;
|
|
ref->GetHLobeAngle(tmp);
|
|
float64 tmpfade;
|
|
ref->GetLobeFalloff(tmpfade);
|
|
sec->setAzimuthRange(-tmp/2.0,tmp/2.0,tmpfade);
|
|
|
|
ref->GetVLobeAngle(tmp);
|
|
sec->setElevationRange(0,tmp, tmpfade);
|
|
|
|
lp._sector = sec;
|
|
osgLight->addLightPoint(lp);
|
|
}
|
|
else if( direc == trpgLightAttr::trpg_Bidirectional )
|
|
{
|
|
osgSim::AzimElevationSector* front = new osgSim::AzimElevationSector();
|
|
float64 tmp;
|
|
ref->GetHLobeAngle(tmp);
|
|
float64 tmpfade;
|
|
ref->GetLobeFalloff(tmpfade);
|
|
front->setAzimuthRange(-tmp/2.0,tmp/2.0,tmpfade);
|
|
|
|
ref->GetVLobeAngle(tmp);
|
|
front->setElevationRange(0,tmp, tmpfade);
|
|
|
|
lp._sector = front;
|
|
osgLight->addLightPoint(lp);
|
|
|
|
osgSim::AzimElevationSector* back = new osgSim::AzimElevationSector();
|
|
back->setAzimuthRange(osg::PI-tmp/2.0,osg::PI+tmp/2.0,tmpfade);
|
|
back->setElevationRange(0,tmp, tmpfade);
|
|
lp._sector = back;
|
|
osgLight->addLightPoint(lp);
|
|
}
|
|
else
|
|
{
|
|
osgLight->addLightPoint(lp);
|
|
}
|
|
|
|
addLightAttribute(osgLight, stateSet, osg::Vec3(normal.x,normal.y,normal.z),itr->first);
|
|
}
|
|
|
|
osg::notify(osg::INFO) << "txp:: ... done." << std::endl;
|
|
return true;
|
|
}
|
|
|
|
void trim(std::string& str)
|
|
{
|
|
while (!str.empty() && isspace(str[str.length()-1]))
|
|
str.erase(str.length()-1);
|
|
while (!str.empty() && isspace(str[0]))
|
|
str.erase(0,1);
|
|
}
|
|
bool TXPArchive::loadTextStyles()
|
|
{
|
|
const trpgTextStyleTable *textStyleTable = GetTextStyleTable();
|
|
if ( !textStyleTable )
|
|
return false;
|
|
if ( textStyleTable->GetNumStyle() < 1 )
|
|
return true;
|
|
|
|
// try fontmap.txt
|
|
std::map< std::string, std::string > fontmap;
|
|
|
|
std::string fmapfname = std::string(getDir())+"\\fontmap.txt";
|
|
osgDB::ifstream fmapfile;
|
|
fmapfile.open(fmapfname.c_str(),std::ios::in);
|
|
|
|
if (fmapfile.is_open())
|
|
{
|
|
osg::notify(osg::INFO) << "txp:: Font map file found: " << fmapfname << std::endl;
|
|
std::string line;
|
|
while (std::getline(fmapfile,line))
|
|
{
|
|
std::string::size_type ix = line.find_first_of('=');
|
|
if (ix != std::string::npos)
|
|
{
|
|
std::string fontname = line.substr(0,ix);
|
|
std::string fontfilename = line.substr(ix+1,line.length()-ix+1);
|
|
|
|
trim(fontname);
|
|
trim(fontfilename);
|
|
|
|
fontmap[fontname] = fontfilename;
|
|
|
|
}
|
|
}
|
|
fmapfile.close();
|
|
}
|
|
else
|
|
{
|
|
osg::notify(osg::NOTICE) << "txp:: No font map file found: " << fmapfname << std::endl;
|
|
osg::notify(osg::NOTICE) << "txp:: All fonts defaulted to arial.ttf" << std::endl;
|
|
}
|
|
|
|
const trpgTextStyleTable::StyleMapType *smap = textStyleTable->getStyleMap();
|
|
trpgTextStyleTable::StyleMapType::const_iterator itr = smap->begin();
|
|
for ( ; itr != smap->end(); itr++)
|
|
{
|
|
const trpgTextStyle *textStyle = &itr->second;
|
|
if ( !textStyle )
|
|
continue;
|
|
|
|
const std::string *fontName = textStyle->GetFont();
|
|
if ( !fontName )
|
|
continue;
|
|
|
|
std::string fontfilename = fontmap[*fontName];
|
|
if ( !fontfilename.length() )
|
|
fontfilename = "arial.ttf";
|
|
osg::ref_ptr< osgText::Font > font = osgText::readFontFile(fontfilename);
|
|
|
|
_fonts[itr->first] = font;
|
|
|
|
const trpgMatTable* matTable = GetMaterialTable();
|
|
if (matTable)
|
|
{
|
|
int matId = textStyle->GetMaterial();
|
|
const trpgMaterial* mat = matTable->GetMaterialRef(0,matId);
|
|
if (mat)
|
|
{
|
|
trpgColor faceColor;
|
|
mat->GetColor(faceColor);
|
|
|
|
float64 alpha;
|
|
mat->GetAlpha(alpha);
|
|
|
|
_fcolors[itr->first] = osg::Vec4(faceColor.red, faceColor.green, faceColor.blue, alpha );
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TXPArchive::addLightAttribute(osgSim::LightPointNode* lpn, osg::StateSet* fallback, const osg::Vec3& att,int handle)
|
|
{
|
|
DeferredLightAttribute la;
|
|
la.lightPoint = lpn;
|
|
la.fallback = fallback;
|
|
la.attitude = att;
|
|
_lights[handle] = la;
|
|
}
|
|
|
|
bool TXPArchive::getTileInfo(const TileLocationInfo& loc, TileInfo& info)
|
|
{
|
|
info.minRange = 0.0;
|
|
info.maxRange = 0.0;
|
|
info.radius = 0.f;
|
|
info.center.set(0.f,0.f,0.f);
|
|
info.bbox.set(0.f,0.f,0.f,0.f,0.f,0.f);
|
|
|
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
|
|
|
header.GetLodRange(loc.lod,info.maxRange);
|
|
header.GetLodRange(loc.lod+1,info.minRange);
|
|
header.GetLodRange(0,info.lod0Range);
|
|
|
|
trpg2dPoint sw,ne;
|
|
header.GetExtents(sw,ne);
|
|
|
|
trpg2dPoint size;
|
|
header.GetTileSize(loc.lod,size);
|
|
|
|
info.size.x() = size.x;
|
|
info.size.y() = size.y;
|
|
info.size.z() = 0.f;
|
|
|
|
info.center.set(
|
|
sw.x+(loc.x*size.x)+(size.x/2.f),
|
|
sw.y+(loc.y*size.y)+(size.y/2.f),
|
|
(loc.zmin + loc.zmax)/2.f
|
|
);
|
|
info.bbox.set(
|
|
osg::Vec3(
|
|
info.center.x()-(size.x/2.f),
|
|
info.center.y()-(size.y/2.f),
|
|
loc.zmin
|
|
),
|
|
osg::Vec3(
|
|
info.center.x()+(size.x/2.f),
|
|
info.center.y()+(size.y/2.f),
|
|
loc.zmax
|
|
)
|
|
);
|
|
info.radius = osg::Vec3(size.x/2.f, size.y/2.f,0.f).length() * 1.3;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool TXPArchive::getTileInfo(int x, int y, int lod, TileInfo& info)
|
|
{
|
|
|
|
if(_majorVersion == 2 && _minorVersion >=1)
|
|
{
|
|
// Version 2.1
|
|
// Tile table contain only lod 0
|
|
if(lod > 0)
|
|
return false;
|
|
}
|
|
|
|
trpgwAppAddress addr;
|
|
float minz = 0.f;
|
|
float maxz = 0.f;
|
|
tileTable.GetTile(x, y, lod, addr, minz, maxz);
|
|
|
|
TileLocationInfo loc(x, y, lod, addr, minz, maxz);
|
|
|
|
return getTileInfo(loc, info);
|
|
}
|
|
|
|
osg::Group* TXPArchive::getTileContent(
|
|
int x, int y, int lod,
|
|
double realMinRange,
|
|
double realMaxRange,
|
|
double usedMaxRange,
|
|
osg::Vec3& tileCenter,
|
|
std::vector<TileLocationInfo>& childInfoList)
|
|
{
|
|
if(_majorVersion == 2 && _minorVersion >= 1)
|
|
{
|
|
// Version 2.1
|
|
// This call is valid only for lod = 0
|
|
if(lod != 0)
|
|
return new osg::Group;
|
|
}
|
|
|
|
trpgwAppAddress addr;
|
|
float minz = 0.f;
|
|
float maxz = 0.f;
|
|
tileTable.GetTile(x, y, lod, addr, minz, maxz);
|
|
TileLocationInfo loc(x,y,lod,addr, minz,maxz);
|
|
|
|
return getTileContent(loc, realMinRange, realMaxRange, usedMaxRange, tileCenter, childInfoList);
|
|
|
|
}
|
|
|
|
class ModelVisitor : public osg::NodeVisitor
|
|
{
|
|
TXPArchive* _archive;
|
|
TXPArchive::TileLocationInfo _tileInfo;
|
|
// int _x;
|
|
// int _y;
|
|
// int _lod;
|
|
|
|
public:
|
|
ModelVisitor(TXPArchive* archive, const TXPArchive::TileLocationInfo& loc):
|
|
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
|
|
_archive(archive), _tileInfo(loc)
|
|
{
|
|
}
|
|
|
|
virtual void apply(osg::MatrixTransform& xform)
|
|
{
|
|
const trpgHeader* header = _archive->GetHeader();
|
|
trpgHeader::trpgTileType tileType;
|
|
header->GetTileOriginType(tileType);
|
|
const osg::Referenced* ref = xform.getUserData();
|
|
const TileIdentifier* tileID = dynamic_cast<const txp::TileIdentifier*>(ref);
|
|
|
|
if(!tileID) return; // bail early - this isn't a loaded model
|
|
|
|
if(tileType == trpgHeader::TileLocal && tileID->lod == 9999)
|
|
{
|
|
trpg2dPoint tileExtents;
|
|
header->GetTileSize(0, tileExtents);
|
|
osg::BoundingBox bbox;
|
|
_archive->getExtents(bbox);
|
|
osg::Vec3 offset(xform.getMatrix().getTrans());
|
|
offset[0] -= bbox._min[0];
|
|
offset[1] -= bbox._min[1];
|
|
|
|
trpg2dPoint offsetXY, tileID(_tileInfo.x,_tileInfo.y);
|
|
int divider = (0x01 << _tileInfo.lod);
|
|
// calculate which tile model is located in
|
|
tileExtents.x /= divider;
|
|
tileExtents.y /= divider;
|
|
offset[0] -= tileID.x*tileExtents.x;
|
|
offset[1] -= tileID.y*tileExtents.y;
|
|
|
|
osg::Matrix mat(xform.getMatrix());
|
|
mat.setTrans(offset);
|
|
xform.setMatrix(mat);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
osg::Group* TXPArchive::getTileContent(
|
|
const TileLocationInfo& loc,
|
|
double realMinRange,
|
|
double realMaxRange,
|
|
double usedMaxRange,
|
|
osg::Vec3& tileCenter,
|
|
std::vector<TileLocationInfo>& childInfoList)
|
|
{
|
|
|
|
if (_parser.get() == 0)
|
|
{
|
|
_parser = new TXPParser();
|
|
_parser->setArchive(this);
|
|
}
|
|
|
|
trpgMemReadBuffer buf(GetEndian());
|
|
bool readStatus;
|
|
trpgTileTable::TileMode tileMode;
|
|
tileTable.GetMode(tileMode);
|
|
if(tileMode == trpgTileTable::External)
|
|
readStatus = ReadExternalTile(loc.x, loc.y, loc.lod, buf);
|
|
else
|
|
readStatus = ReadTile(loc.addr, buf);
|
|
|
|
if(!readStatus)
|
|
return new osg::Group;
|
|
trpgTileHeader *tilehdr = _parser->getTileHeaderRef();
|
|
|
|
int majVersion,minVersion;
|
|
GetVersion(majVersion,minVersion);
|
|
// only compute block # if we are a master archive.
|
|
if((majVersion >= TRPG_NOMERGE_VERSION_MAJOR) && (minVersion >= TRPG_NOMERGE_VERSION_MINOR) && (_isMaster))
|
|
{
|
|
if(tilehdr)
|
|
{
|
|
int x,y;
|
|
unsigned int denom = (1 << loc.lod); // this should work up to lod 31
|
|
x = loc.x/denom;
|
|
y = loc.y/denom;
|
|
tilehdr->SetBlockNo(y,x);
|
|
}
|
|
}
|
|
|
|
osg::Group *tileGroup = _parser->parseScene(buf,_statesMap,_models,realMinRange,realMaxRange,usedMaxRange);
|
|
tileCenter = _parser->getTileCenter();
|
|
|
|
int nbChild = _parser->GetNbChildrenRef();
|
|
|
|
childInfoList.clear();
|
|
for(int idx = 0; idx < nbChild; idx++)
|
|
{
|
|
const trpgChildRef *childRef = _parser->GetChildRef(idx);
|
|
|
|
if(childRef)
|
|
{
|
|
TileLocationInfo loc;
|
|
childRef->GetTileLoc(loc.x, loc.y, loc.lod);
|
|
childRef->GetTileZValue(loc.zmin, loc.zmax);
|
|
childRef->GetTileAddress(loc.addr);
|
|
childInfoList.push_back(loc);
|
|
|
|
}
|
|
}
|
|
|
|
// Fix up model MatrixTransform
|
|
ModelVisitor mv(this, loc);
|
|
tileGroup->accept(mv);
|
|
|
|
// Prune
|
|
OSGStatesMapType::iterator itr = _statesMap.begin();
|
|
while( itr != _statesMap.end( ) )
|
|
{
|
|
if(itr->second.valid() &&
|
|
(itr->second->referenceCount()==1))
|
|
{
|
|
// unreference it.
|
|
itr->second = NULL;
|
|
|
|
OSGStatesMapType::iterator toRemove = itr;
|
|
++itr;
|
|
|
|
// remove it from the map
|
|
_statesMap.erase( toRemove );
|
|
}
|
|
else
|
|
{
|
|
++itr;
|
|
}
|
|
}
|
|
|
|
OSGTexMapType::iterator mitr = _texmap.begin();
|
|
while( mitr != _texmap.end( ) )
|
|
{
|
|
if(mitr->second.valid() &&
|
|
(mitr->second->referenceCount()==1))
|
|
{
|
|
// unreference it.
|
|
mitr->second = NULL;
|
|
|
|
OSGTexMapType::iterator toRemove = mitr;
|
|
++mitr;
|
|
|
|
// remove it from the map
|
|
_texmap.erase( toRemove );
|
|
}
|
|
else
|
|
{
|
|
++mitr;
|
|
}
|
|
}
|
|
|
|
return tileGroup;
|
|
}
|
|
|
|
bool TXPArchive::getLODSize(int lod, int& x, int& y)
|
|
{
|
|
x = y = 0;
|
|
|
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
|
|
|
trpg2iPoint size;
|
|
if (header.GetLodSize(lod,size))
|
|
{
|
|
x = size.x;
|
|
y = size.y;
|
|
}
|
|
|
|
return true;
|
|
}
|