#include #include #include #include "TrPageArchive.h" #include "TrPageParser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trpage_geom.h" #include "trpage_read.h" #include "trpage_write.h" #include "trpage_scene.h" #include "trpage_managers.h" using namespace txp; using namespace osg; // disable 'this' used in base pointer initilialization.. #if defined(WIN32) && !defined(__GNUC__) #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 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 = osgDB::readImageFile(theFile); if (image.valid()) { osg_texture->setImage(image.get()); } m_textures[i] = osg_texture; } else if( mode == trpgTexture::Local ) { m_textures[i] = GetLocalTexture(image_helper,0, tex); } else if( mode == trpgTexture::Template ) { m_textures[i] = GetLocalTexture(image_helper,0, tex); } 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>=GL_NEVER && alphaFunc<=GL_ALWAYS) { 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 ); // ----------- // Min filter // ----------- int32 minFilter; texEnv.GetMinFilter(minFilter); switch (minFilter) { case trpgTextureEnv::Point: case trpgTextureEnv::Nearest: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, Texture2D::NEAREST); break; case trpgTextureEnv::Linear: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, Texture2D::LINEAR); break; case trpgTextureEnv::MipmapPoint: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, Texture2D::NEAREST_MIPMAP_NEAREST); break; case trpgTextureEnv::MipmapLinear: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, Texture2D::NEAREST_MIPMAP_LINEAR); break; case trpgTextureEnv::MipmapBilinear: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, Texture2D::LINEAR_MIPMAP_NEAREST); break; case trpgTextureEnv::MipmapTrilinear: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, Texture2D::LINEAR_MIPMAP_LINEAR); break; default: osg_texture->setFilter(osg::Texture2D::MIN_FILTER, 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,Texture2D::NEAREST); break; case trpgTextureEnv::Linear: default: osg_texture->setFilter(osg::Texture2D::MAG_FILTER, Texture2D::LINEAR); break; } // pass on to the stateset. osg_state_set->setTextureAttributeAndModes(ntex,osg_texture, StateAttribute::ON); if(osg_texture->getImage() && osg_texture->getImage()->isImageTranslucent()) { 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 **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 *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 groupList; std::vector *groupList = parse->GetGroupList(); for (int nl=0;nlGetLodSize(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; }