503 lines
16 KiB
C++
503 lines
16 KiB
C++
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <osgTXP/TrPageArchive.h>
|
|
#include <osgTXP/TrPageParser.h>
|
|
|
|
#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/Notify>
|
|
#include <osgDB/FileUtils>
|
|
|
|
#include <iostream>
|
|
|
|
#include <osgDB/ReadFile>
|
|
#include <osgDB/WriteFile>
|
|
#include <osgDB/FileNameUtils>
|
|
|
|
#include <osgTXP/trpage_geom.h>
|
|
#include <osgTXP/trpage_read.h>
|
|
#include <osgTXP/trpage_write.h>
|
|
#include <osgTXP/trpage_scene.h>
|
|
#include <osgTXP/trpage_managers.h>
|
|
|
|
|
|
using namespace txp;
|
|
using namespace osg;
|
|
|
|
// disable 'this' used in base pointer initilialization..
|
|
#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
|
|
#pragma warning( disable : 4355 )
|
|
#endif
|
|
|
|
|
|
TrPageArchive::TrPageArchive()
|
|
: trpgr_Archive()
|
|
, parse(new TrPageParser(this))
|
|
, buf(GetEndian())
|
|
{
|
|
}
|
|
|
|
TrPageArchive::~TrPageArchive()
|
|
{
|
|
}
|
|
|
|
bool TrPageArchive::OpenFile(const char* file)
|
|
{
|
|
m_alternate_path = osgDB::getFilePath(file);
|
|
std::string name = osgDB::getSimpleFileName(file);
|
|
|
|
if(m_alternate_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(m_alternate_path);
|
|
SetDirectory(m_alternate_path.c_str());
|
|
}
|
|
|
|
if (!trpgr_Archive::OpenFile(name.c_str()))
|
|
{
|
|
notify(WARN) << "TrPageArchive::OpenFile() error: "
|
|
<< "couldn't open archive: " << file << std::endl;
|
|
return false;
|
|
}
|
|
if (!ReadHeader())
|
|
{
|
|
notify(WARN) << "TrPageArchive::OpenFile() error: "
|
|
<< "couldn't read header for archive: " << file
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Set the max group ID here because it won't change
|
|
const trpgHeader *head = GetHeader();
|
|
int maxID;
|
|
head->GetMaxGroupID(maxID);
|
|
parse->SetMaxGroupID(maxID);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Calculate the center of the database (including Z)
|
|
*/
|
|
void TrPageArchive::GetCenter(Vec3 ¢er)
|
|
{
|
|
trpg2dPoint sw,ne;
|
|
const trpgHeader *head = GetHeader();
|
|
const trpgTileTable *tileTable = GetTileTable();
|
|
head->GetExtents(sw,ne);
|
|
trpg2dPoint tileSize;
|
|
head->GetTileSize(0,tileSize);
|
|
|
|
center[0] = (ne.x+sw.x)/2.0;
|
|
center[1] = (ne.y+sw.y)/2.0;
|
|
|
|
trpg2iPoint loc;
|
|
loc.x = int((center[0]-sw.x)/tileSize.x);
|
|
loc.y = int((center[1]-sw.y)/tileSize.y);
|
|
trpgwAppAddress foo;
|
|
float zmin,zmax;
|
|
tileTable->GetTile(loc.x, loc.y,0,foo,zmin,zmax);
|
|
center[2] = (zmin+zmax)/2.0;
|
|
}
|
|
|
|
// load textures and materials
|
|
// TODO : multitexturing
|
|
void TrPageArchive::LoadMaterials()
|
|
{
|
|
trpgrImageHelper image_helper(this->GetEndian(),getDir(),materialTable,texTable);
|
|
|
|
int n_textures;
|
|
|
|
texTable.GetNumTextures(n_textures);
|
|
|
|
m_textures.resize(n_textures);
|
|
|
|
// these extra braces are workaroud for annoying bug in MSVC
|
|
// for( int i = ....) and i is visible outside the loop
|
|
{
|
|
for (int i=0; i < n_textures ; i++)
|
|
{
|
|
const trpgTexture *tex;
|
|
tex = texTable.GetTextureRef(i);
|
|
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.
|
|
ref_ptr<Texture2D> osg_texture = new Texture2D();
|
|
|
|
// 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 ;
|
|
ref_ptr<Image> image = osgDB::readImageFile(theFile);
|
|
if (image.valid())
|
|
{
|
|
osg_texture->setImage(image.get());
|
|
}
|
|
m_textures[i] = osg_texture;
|
|
}
|
|
else if( mode == trpgTexture::Local )
|
|
{
|
|
ref_ptr<Texture2D> osg_texture = GetLocalTexture(image_helper,0, tex);
|
|
osg_texture->ref();
|
|
m_textures[i] = osg_texture;
|
|
// delete [] data;
|
|
}
|
|
else if( mode == trpgTexture::Template )
|
|
{
|
|
ref_ptr<Texture2D> osg_texture = GetLocalTexture(image_helper,0, tex);
|
|
if (osg_texture.valid()) osg_texture->ref();
|
|
m_textures[i] = osg_texture;
|
|
// delete [] data;
|
|
}
|
|
else
|
|
{
|
|
m_textures[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int n_materials;
|
|
materialTable.GetNumMaterial(n_materials);
|
|
{
|
|
m_gstates.resize(n_materials);
|
|
for (int i = 0; i < n_materials; i++)
|
|
{
|
|
StateSet* osg_state_set = new 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 )
|
|
{
|
|
Material *osg_material = new Material;
|
|
|
|
float64 alpha;
|
|
mat->GetAlpha(alpha);
|
|
|
|
trpgColor color;
|
|
mat->GetAmbient(color);
|
|
osg_material->setAmbient( Material::FRONT_AND_BACK ,
|
|
Vec4(color.red, color.green, color.blue, alpha));
|
|
mat->GetDiffuse(color);
|
|
osg_material->setDiffuse(Material::FRONT_AND_BACK ,
|
|
Vec4(color.red, color.green, color.blue, alpha));
|
|
|
|
mat->GetSpecular(color);
|
|
osg_material->setSpecular(Material::FRONT_AND_BACK ,
|
|
Vec4(color.red, color.green, color.blue, alpha));
|
|
mat->GetEmission(color);
|
|
osg_material->setEmission(Material::FRONT_AND_BACK ,
|
|
Vec4(color.red, color.green, color.blue, alpha));
|
|
|
|
float64 shinines;
|
|
mat->GetShininess(shinines);
|
|
osg_material->setShininess(Material::FRONT_AND_BACK , (float)shinines);
|
|
|
|
osg_material->setAlpha(Material::FRONT_AND_BACK ,(float)alpha);
|
|
osg_state_set->setAttributeAndModes(osg_material, StateAttribute::ON);
|
|
|
|
if( alpha < 1.0f )
|
|
{
|
|
osg_state_set->setMode(GL_BLEND,StateAttribute::ON);
|
|
osg_state_set->setRenderingHint(StateSet::TRANSPARENT_BIN);
|
|
}
|
|
|
|
int alphaFunc;
|
|
mat->GetAlphaFunc(alphaFunc);
|
|
if( alphaFunc != trpgMaterial::None)
|
|
{
|
|
float64 ref;
|
|
mat->GetAlphaRef(ref);
|
|
AlphaFunc *osg_alpha_func = new AlphaFunc;
|
|
osg_alpha_func->setFunction((AlphaFunc::ComparisonFunction)alphaFunc,(float)ref);
|
|
osg_state_set->setAttributeAndModes(osg_alpha_func, StateAttribute::ON);
|
|
}
|
|
|
|
for (int ntex = 0; ntex < numMatTex; ntex ++ )
|
|
{
|
|
int texId;
|
|
trpgTextureEnv texEnv;
|
|
mat->GetTexture(ntex,texId,texEnv);
|
|
|
|
// Set up texture environment
|
|
TexEnv *osg_texenv = new TexEnv();
|
|
int32 te_mode;
|
|
texEnv.GetEnvMode(te_mode);
|
|
switch( te_mode )
|
|
{
|
|
case trpgTextureEnv::Alpha :
|
|
osg_texenv->setMode(TexEnv::REPLACE);
|
|
break;
|
|
case trpgTextureEnv::Decal:
|
|
osg_texenv->setMode(TexEnv::DECAL);
|
|
break;
|
|
case trpgTextureEnv::Blend :
|
|
osg_texenv->setMode(TexEnv::BLEND);
|
|
break;
|
|
case trpgTextureEnv::Modulate :
|
|
osg_texenv->setMode(TexEnv::MODULATE);
|
|
break;
|
|
}
|
|
|
|
osg_state_set->setTextureAttribute(ntex,osg_texenv);
|
|
|
|
int wrap_s, wrap_t;
|
|
texEnv.GetWrap(wrap_s, wrap_t);
|
|
|
|
Texture2D* osg_texture = m_textures[texId].get();
|
|
if(osg_texture)
|
|
{
|
|
osg_texture->setWrap(Texture2D::WRAP_S, wrap_s == trpgTextureEnv::Repeat ? Texture2D::REPEAT: Texture2D::CLAMP );
|
|
osg_texture->setWrap(Texture2D::WRAP_T, wrap_t == trpgTextureEnv::Repeat ? Texture2D::REPEAT: Texture2D::CLAMP );
|
|
osg_state_set->setTextureAttributeAndModes(ntex,osg_texture, StateAttribute::ON);
|
|
|
|
if(osg_texture->getImage())
|
|
{
|
|
switch (osg_texture->getImage()->getPixelFormat())
|
|
{
|
|
case GL_LUMINANCE_ALPHA:
|
|
case GL_RGBA:
|
|
osg_state_set->setMode(GL_BLEND,StateAttribute::ON);
|
|
osg_state_set->setRenderingHint(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)
|
|
{
|
|
CullFace* cull_face = new CullFace;
|
|
switch (cullMode)
|
|
{
|
|
case trpgMaterial::Front:
|
|
cull_face->setMode(CullFace::BACK);
|
|
break;
|
|
case trpgMaterial::Back:
|
|
cull_face->setMode(CullFace::FRONT);
|
|
break;
|
|
}
|
|
osg_state_set->setAttributeAndModes(cull_face, StateAttribute::ON);
|
|
}
|
|
}
|
|
m_gstates[i] = osg_state_set;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TrPageArchive::LoadModels()
|
|
{
|
|
int numModel;
|
|
modelTable.GetNumModels(numModel);
|
|
|
|
// Iterate over the models
|
|
for (int i=0; i< numModel; i++)
|
|
{
|
|
trpgModel *mod = modelTable.GetModelRef(i);
|
|
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
|
|
Node *osg_model = osgDB::readNodeFile(name);
|
|
if (!osg_model)
|
|
{
|
|
notify(WARN) << "TrPageArchive::LoadModels() error: "
|
|
<< "failed to load model: "
|
|
<< name << std::endl;
|
|
}
|
|
// Do this even if it's NULL
|
|
m_models.push_back(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;
|
|
}
|
|
|
|
// Group* TrPageArchive::LoadTile(int x,int y,int lod,int &parentID,vector<GroupIDInfo> **groupList)
|
|
Group* TrPageArchive::LoadTile(int x,int y,int lod,int &parentID)
|
|
{
|
|
trpgMemReadBuffer buf(GetEndian());
|
|
|
|
// Read the tile data in, but don't parse it
|
|
if (!ReadTile(x,y,lod,buf))
|
|
return NULL;
|
|
|
|
Group *tile = parse->ParseScene(buf, m_gstates , m_models);
|
|
if (tile)
|
|
{
|
|
parentID = parse->GetParentID();
|
|
// This is where you would page in textures and models
|
|
}
|
|
|
|
// Also return the list of IDs and their groups we read in for this tile
|
|
// *groupList = parse->GetGroupList();
|
|
|
|
// That's it
|
|
return tile;
|
|
}
|
|
|
|
// Group* TrPageArchive::LoadTile(Group *rootNode,trpgPageManager *pageManage,trpgManagedTile *tile,vector<)
|
|
Group* TrPageArchive::LoadTile(Group *rootNode,
|
|
trpgPageManager * /*pageManage*/,
|
|
trpgManagedTile *tile,osg::Group **parentNode)
|
|
{
|
|
int x,y,lod;
|
|
tile->GetTileLoc(x,y,lod);
|
|
std::vector<osg::Group *> *groupList = parse->GetGroupList();
|
|
|
|
// Fetch the tile data (but don't parse it)
|
|
if (!ReadTile(x,y,lod,buf))
|
|
return NULL;
|
|
|
|
// Now parse it
|
|
Group *gTile = parse->ParseScene(buf, m_gstates, m_models);
|
|
if (gTile && rootNode) {
|
|
// Hook it into its parent
|
|
int parentID = parse->GetParentID();
|
|
if (parentID >= 0) {
|
|
(*groupList)[parentID]->addChild(gTile);
|
|
} else
|
|
rootNode->addChild(gTile);
|
|
|
|
// Note: Need to page in the textures
|
|
// They're being forced in during the parse right now
|
|
}
|
|
if (parentNode) {
|
|
int parentID = parse->GetParentID();
|
|
if (parentID >= 0)
|
|
*parentNode = (*groupList)[parentID];
|
|
else
|
|
*parentNode = rootNode;
|
|
}
|
|
|
|
// Add the tile to the local data in the managed tile
|
|
tile->SetLocalData(gTile);
|
|
|
|
return gTile;
|
|
}
|
|
|
|
bool TrPageArchive::UnLoadTile(trpgPageManager * /*pageManage*/,
|
|
trpgManagedTile *tile)
|
|
{
|
|
// The local tile data will have the group we need
|
|
Group *gTile = (Group *)tile->GetLocalData();
|
|
if (gTile) {
|
|
// Remove it from the parent list
|
|
// We're only expecting on parent
|
|
const Group::ParentList &pList = gTile->getParents();
|
|
if (pList.size() != 1)
|
|
return false;
|
|
pList[0]->removeChild(gTile);
|
|
} else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Group* TrPageArchive::LoadAllTiles()
|
|
{
|
|
// Size information comes out of the header
|
|
const trpgHeader *head = GetHeader();
|
|
// Create one group as the top
|
|
Group *topGroup = new Group;
|
|
|
|
int32 numLod;
|
|
head->GetNumLods(numLod);
|
|
// Iterate over the LODs. Lower res LODs must be loaded
|
|
// first, otherwise there's nothing to hook the higher res
|
|
// LODs into.
|
|
trpg2iPoint tileSize;
|
|
|
|
// The group list is used to map parent IDs to pfGroup nodes
|
|
//std::vector<Group *> groupList;
|
|
std::vector<Group *> *groupList = parse->GetGroupList();
|
|
|
|
for (int nl=0;nl<numLod;nl++)
|
|
{
|
|
// The number of tiles in X and Y changes per LOD
|
|
head->GetLodSize(nl,tileSize);
|
|
for (int x=0; x < tileSize.x; x++)
|
|
{
|
|
for (int y=0; y < tileSize.y; y++)
|
|
{
|
|
int parentID;
|
|
Group *tile = LoadTile(x,y,nl,parentID);
|
|
if (!tile)
|
|
{
|
|
notify(WARN)<< "TrPageArchive::LoadAllTiles error: "
|
|
<< "failed to load tile ("
|
|
<< x << "," << y << "," << nl << ")"
|
|
<< std::endl;
|
|
}
|
|
else
|
|
{
|
|
if (parentID == -1)
|
|
{
|
|
// Get added to the top level node
|
|
topGroup->addChild(tile);
|
|
}
|
|
else
|
|
{
|
|
// Added below some other node
|
|
(*groupList)[parentID]->addChild(tile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return topGroup;
|
|
}
|