Files
OpenSceneGraph/src/osgPlugins/txp/TXPArchive.cpp
2005-05-01 10:34:48 +00:00

719 lines
21 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 <osgDB/FileUtils>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgSim/Sector>
#include <osgSim/LightPoint>
#include <osgSim/LightPointNode>
#include <osgSim/BlinkSequence>
#include <iostream>
#include <fstream>
#include "TXPArchive.h"
#include "TXPParser.h"
#include <OpenThreads/ScopedLock>
using namespace txp;
#define TXPArchiveERROR(s) osg::notify(osg::NOTICE) << "txp::TXPArchive::" << (s) << " error: "
TXPArchive::TXPArchive():
trpgr_Archive(),
_id(-1),
_numLODs(0),
_swExtents(0.0,0.0),
_neExtents(0.0,0.0)
{
}
TXPArchive::~TXPArchive()
{
CloseFile();
}
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())
{
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);
}
int numTextures;
texTable.GetNumTextures(numTextures);
_textures.resize(numTextures);
int numModel;
modelTable.GetNumModels(numModel);
_models.resize(numModel);
int numMaterials;
materialTable.GetNumMaterial(numMaterials);
_gstates.resize(numMaterials);
return true;
}
bool TXPArchive::loadMaterial(int ix)
{
int i = ix;
if (_gstates[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);
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 = _textures[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);
}
}
_gstates[i] = osg_state_set;
return true;
}
bool TXPArchive::loadMaterials()
{
return true;
}
bool TXPArchive::loadTexture(int i)
{
if (_textures[i].get()) return true;
trpgrImageHelper image_helper(this->GetEndian(),getDir(),materialTable,texTable);
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::Image* image = osgDB::readImageFile(theFile);
if (image)
{
osg_texture->setImage(image);
}
else
{
osg::notify(osg::WARN) << "TrPageArchive::LoadMaterials() error: "
<< "couldn't open image: " << filename << std::endl;
}
_textures[i] = osg_texture;
}
else if( mode == trpgTexture::Local )
{
_textures[i] = getLocalTexture(image_helper,tex);
}
else if( mode == trpgTexture::Template )
{
_textures[i] = 0L; //GetTemplateTexture(image_helper,0, tex);
}
else
{
_textures[i] = 0;
}
return (_textures[i].get() != 0);
}
bool TXPArchive::loadModel(int ix)
{
trpgModel *mod = modelTable.GetModelRef(ix);
int type;
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::NOTICE) << "txp:: Loading models ..." << std::endl;
int numModel;
modelTable.GetNumModels(numModel);
_models.resize(numModel);
// Iterate over the models
for (int i=0; i< numModel; i++)
{
loadModel(i);
}
osg::notify(osg::NOTICE) << "txp:: ... done." << std::endl;
return true;
}
bool TXPArchive::loadLightAttributes()
{
osg::notify(osg::NOTICE) << "txp:: Loading light attributes ..." << std::endl;
int num;
lightTable.GetNumLightAttrs(num);
for ( int attr_num = 0; attr_num < num; attr_num++ )
{
trpgLightAttr* ref = const_cast<trpgLightAttr*>(lightTable.GetLightAttrRef(attr_num));
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));
}
osg::notify(osg::NOTICE) << "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";
std::ifstream fmapfile;
fmapfile.open(fmapfname.c_str(),std::ios::in);
if (fmapfile.is_open())
{
osg::notify(osg::NOTICE) << "txp:: Font map file found: " << fmapfname << std::endl;
std::string line;
while (std::getline(fmapfile,line))
{
unsigned int 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;
}
_fonts.resize(textStyleTable->GetNumStyle());
_fcolors.resize(textStyleTable->GetNumStyle());
for (int i = 0; i < textStyleTable->GetNumStyle(); i++)
{
const trpgTextStyle *textStyle = textStyleTable->GetStyleRef(i);
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[i] = 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[i] = osg::Vec4(faceColor.red, faceColor.green, faceColor.blue, alpha );
}
}
}
return true;
}
void TXPArchive::addLightAttribute(osgSim::LightPointNode* lpn, osg::StateSet* fallback, const osg::Vec3& att)
{
DeferredLightAttribute la;
la.lightPoint = lpn;
la.fallback = fallback;
la.attitude = att;
_lights.push_back(la);
}
bool TXPArchive::getTileInfo(int x, int y, int lod, 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(lod,info.maxRange);
header.GetLodRange(lod+1,info.minRange);
header.GetLodRange(0,info.lod0Range);
trpg2dPoint sw,ne;
header.GetExtents(sw,ne);
trpg2dPoint size;
header.GetTileSize(lod,size);
info.size.x() = size.x;
info.size.y() = size.y;
info.size.z() = 0.f;
trpgwAppAddress addr;
float minz = 0.f;
float maxz = 0.f;
tileTable.GetTile(x,y,lod,addr,minz,maxz);
info.center.set(
sw.x+(x*size.x)+(size.x/2.f),
sw.y+(y*size.y)+(size.y/2.f),
(minz+maxz)/2.f
);
info.bbox.set(
osg::Vec3(
info.center.x()-(size.x/2.f),
info.center.y()-(size.y/2.f),
minz
),
osg::Vec3(
info.center.x()+(size.x/2.f),
info.center.y()+(size.y/2.f),
maxz
)
);
info.radius = osg::Vec3(size.x/2.f, size.y/2.f,0.f).length() * 1.3;
return true;
}
osg::Group* TXPArchive::getTileContent(
int x, int y, int lod,
double realMinRange,
double realMaxRange,
double usedMaxRange,
osg::Vec3& tileCenter)
{
if (_parser.get() == 0)
{
_parser = new TXPParser();
_parser->setArchive(this);
}
trpgMemReadBuffer buf(GetEndian());
if (!ReadTile(x,y,lod,buf))
{
return new osg::Group;
}
osg::Group *tileGroup = _parser->parseScene(buf,_gstates,_models,realMinRange,realMaxRange,usedMaxRange);
tileCenter = _parser->getTileCenter();
// Prune
unsigned int i = 0;
for (i = 0; i < _gstates.size(); i++)
{
if (_gstates[i].valid() && (_gstates[i]->referenceCount()==1)) _gstates[i] = 0;
}
for (i = 0; i < _textures.size(); i++)
{
if (_textures[i].valid() && (_textures[i]->referenceCount()==1)) _textures[i] = 0;
}
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;
}