/* ************************ Copyright Terrain Experts Inc. Terrain Experts Inc (TERREX) reserves all rights to this source code unless otherwise specified in writing by the President of TERREX. This copyright may be updated in the future, in which case that version supercedes this one. ------------------- Terrex Experts Inc. 4400 East Broadway #314 Tucson, AZ 85711 info@terrex.com Tel: (520) 323-7990 ************************ */ #include #include #include /* trpage_warchive.cpp This source file contains the implementations of trpgwArchive and trpgwGeomHelper. The Write Archive is used to write TerraPage archives. All its important methods are virtual, so you shouldn't need to modify any of this code. Simply subclass and override. The Geometry Helper is a class that's used to sort out polygons and build trpgGeometry objects, containing triangle strips and fans out of them. The one contained here is fairly simple, but all its important methods are virtual. So again, subclass and override if you need to change them. */ #include #include #include #include // Constructor trpgwArchive::trpgwArchive(trpgEndian inNess,trpgwArchive::TileMode inTileMode,int majorVer, int minorVer) { Init(inNess,inTileMode,majorVer, minorVer); } void trpgwArchive::Init(trpgEndian inNess, trpgwArchive::TileMode inTileMode,int majorVer, int minorVer) { minorVersion = minorVer; majorVersion = majorVer; if (majorVersion < 1 || majorVersion > TRPG_VERSION_MAJOR) throw 1; if(majorVersion == TRPG_VERSION_MAJOR) { if(minorVersion < 0 || minorVersion > TRPG_VERSION_MINOR) throw 1; } fp = NULL; strcpy(dir,"."); ness = inNess; tileMode = inTileMode; cpuNess = trpg_cpu_byte_order(); tileFile = NULL; tileFileCount = 0; isRegenerate = false; maxTileFileLen = -1; firstHeaderWrite = true; } // Constructor for regenerate trpgwArchive::trpgwArchive(char *inDir,char *inFile,trpg2dPoint &sw, trpg2dPoint &ne, int majorVer, int minorVer) { Init(inDir,inFile,sw,ne, majorVer, minorVer); } void trpgwArchive::Init(char *inDir,char *inFile,trpg2dPoint &sw, trpg2dPoint &ne, int majorVer, int minorVer) { maxTileFileLen = -1; majorVersion = majorVer; minorVersion = minorVer; fp = NULL; strcpy(dir,inDir); cpuNess = trpg_cpu_byte_order(); tileFile = NULL; tileFileCount = 0; isRegenerate = true; errMess[0] = '\0'; // TODO: have a "setup from file" method for trpgwArchive // Open a Read Archive to get the rest of the info we need trpgr_Archive *inArch = this->GetArchiveReader(); inArch->SetDirectory(inDir); if (!inArch->OpenFile(inFile)) { delete inArch; throw 1; } // Get the header (this is what we need) if (!inArch->ReadHeader()) { delete inArch; throw 1; } ness = inArch->GetEndian(); const trpgHeader *inHeader = inArch->GetHeader(); // use the version in the archive instead. inHeader->GetVersion(majorVersion,minorVersion); // Expand the coverage trpg2dPoint newSW,newNE; trpg2dPoint oldSW,oldNE; // Or not. Have to add in something to avoid recalculation // when merging geocentric databases. We don't really support // them, and everything goes to hell. So: hack is: // if sw=ne, don't change anything. // This will also help a little with MMB TXP merge speed. bool extentsUnchanged=false; inHeader->GetExtents(oldSW,oldNE); // just checking for equality right now. Bad? if ((sw==ne) || ((oldSW==sw) && (oldNE==ne))) { extentsUnchanged = true; // set up passed-in SW and NE as well. sw=newSW=oldSW; ne=newNE=oldNE; } else { newSW.x = MIN(sw.x,oldSW.x); newSW.y = MIN(sw.y,oldSW.y); newNE.x = MAX(ne.x,oldNE.x); newNE.y = MAX(ne.y,oldNE.y); } // Decide what the offset should be for new tiles if (!extentsUnchanged) { trpg2dPoint blockSize; inHeader->GetTileSize(0,blockSize); double dx = (oldSW.x - newSW.x)/blockSize.x + 10e-10; double dy = (oldSW.y - newSW.y)/blockSize.y + 10e-10; addOffset.x = (int)dx; addOffset.y = (int)dy; if (dx - addOffset.x > 10e-4 || dy - addOffset.y > 10e-4) { delete inArch; throw 1; } } // Header can mostly stay the same header = *inHeader; header.SetExtents(newSW,newNE); header.GetNumLods(numLod); // Update to the new MBR and tile grid sizes if (!extentsUnchanged) { for (int i=0;iGetTileSize(i,tileSize); trpg2iPoint newTileExt; newTileExt.x = int((newNE.x - newSW.x)/tileSize.x + 10e-5); newTileExt.y = int((newNE.y - newSW.y)/tileSize.y + 10e-15); header.SetLodSize(i,newTileExt); } } // These tables we can copy straight over matTable = *inArch->GetMaterialTable(); texTable = *inArch->GetTexTable(); modelTable = *inArch->GetModelTable(); lightTable = *inArch->GetLightTable(); rangeTable = *inArch->GetRangeTable(); textStyleTable = *inArch->GetTextStyleTable(); supportStyleTable = *inArch->GetSupportStyleTable(); labelPropertyTable = *inArch->GetLabelPropertyTable(); // Need to resize the tile table (maybe) // NOTE: Starting with version 2.1, the tile tables will contain only // the lod 0 tiles trpgTileTable::TileMode tileTableMode; if (!extentsUnchanged) { const trpgTileTable *oldTiles = inArch->GetTileTable(); oldTiles->GetMode(tileTableMode); tileTable.SetMode(tileTableMode); if(majorVersion == 2 && minorVersion >=1) { // Version 2.1. we store only lod 0, all other lod tiles are // stored in the parent tile tileTable.SetNumLod(0); // Size the output tile table trpg2iPoint tileSize; header.GetLodSize(0,tileSize); tileTable.SetNumTiles(tileSize.x, tileSize.y, 0); // Copy over individual tiles trpg2iPoint levelOffset; levelOffset.x = addOffset.x; levelOffset.y = addOffset.y; trpg2iPoint oldTileSize; inHeader->GetLodSize(0, oldTileSize); for (int ix=0;ixGetTile(ix, iy, 0,addr,zmin,zmax); tileTable.SetTile(ix+addOffset.x, iy+addOffset.y ,0, addr, zmin, zmax); } } } else { tileTable.SetNumLod(numLod); for (int lod=0;lodGetLodSize(lod,oldTileSize); for (int ix=0;ixGetTile(ix,iy,lod,addr,zmin,zmax); tileTable.SetTile(ix+addOffset.x,iy+addOffset.y,lod,addr,zmin,zmax); } } } } } else { tileTable = *inArch->GetTileTable(); tileTable.GetMode(tileTableMode); } // Continue to work in the mode the original database is in switch(tileTableMode) { case trpgTileTable::Local: tileMode = TileLocal; break; case trpgTileTable::External: tileMode = TileExternal; break; case trpgTileTable::ExternalSaved: tileMode = TileExternalSaved; break; } // That's it for the read archive delete inArch; } // Destructor trpgwArchive::~trpgwArchive() { if (fp) fclose(fp); if (tileFile) { delete tileFile; tileFile = NULL; } } // WriteHeaderData int32 trpgwArchive::WriteHeaderData(const char *dataPtr, int length, FILE* /*filehandle*/) { return fwrite(dataPtr,1,length,fp); } // IsValid() // Verifies that our file is open bool trpgwArchive::isValid() const { if (!fp) { strcpy(errMess, "File object do not exist"); return false; } return true; } const char *trpgwArchive::getErrMess() const { if(errMess[0] == '\0') return 0; else return &errMess[0]; } // Set the maximum advised size for a tile file void trpgwArchive::SetMaxTileFileLength(int max) { maxTileFileLen = max; } /* Set Functions These just copy tables and the header from the input. If these aren't set, then empty ones are written. */ bool trpgwArchive::SetHeader(const trpgHeader &head) { header = head; return true; } bool trpgwArchive::SetMaterialTable(const trpgMatTable &mat) { matTable = mat; return true; } bool trpgwArchive::SetTextureTable(const trpgTexTable &tex) { texTable = tex; return true; } bool trpgwArchive::SetModelTable(const trpgModelTable &models) { modelTable = models; return true; } bool trpgwArchive::SetLightTable(const trpgLightTable &lights) { lightTable = lights; return true; } bool trpgwArchive::SetRangeTable(const trpgRangeTable &ranges) { rangeTable = ranges; return true; } bool trpgwArchive::SetTextStyleTable(const trpgTextStyleTable &styles) { textStyleTable = styles; return true; } bool trpgwArchive::SetLabelPropertyTable(const trpgLabelPropertyTable &properties) { labelPropertyTable = properties; return true; } bool trpgwArchive::SetSupportStyleTable(const trpgSupportStyleTable &styles) { supportStyleTable = styles; return true; } /* Get Methods Used in regenerate. */ trpgHeader *trpgwArchive::GetHeader() { return &header; } trpgMatTable *trpgwArchive::GetMatTable() { return &matTable; } trpgTexTable *trpgwArchive::GetTextureTable() { return &texTable; } trpgModelTable *trpgwArchive::GetModelTable() { return &modelTable; } trpgLightTable *trpgwArchive::GetLightTable() { return &lightTable; } trpgRangeTable *trpgwArchive::GetRangeTable() { return &rangeTable; } trpgTextStyleTable *trpgwArchive::GetTextStyleTable() { return &textStyleTable; } trpgSupportStyleTable *trpgwArchive::GetSupportStyleTable() { return &supportStyleTable; } trpgLabelPropertyTable *trpgwArchive::GetLabelPropertyTable() { return &labelPropertyTable; } // OpenFile // Same as above, only gets a basename as well bool trpgwArchive::OpenFile(const char *in_dir,const char *name) { char filename[1024]; strncpy(dir,in_dir,1023); sprintf(filename,"%s" PATHSEPERATOR "%s",dir,name); if (!(fp = fopen(filename,"wb"))) return false; return true; } // CloseFile // Close the open file void trpgwArchive::CloseFile() { if (fp) fclose(fp); fp = NULL; } /* Write Header Flush out the header (checkpoint) and return. */ bool trpgwArchive::WriteHeader() { bool ret = CheckpointHeader(); if (tileFile) { delete tileFile; tileFile=NULL; } return ret; } /* CheckpointHeader The header lives in its own file, so we can write it at any point we have a valid archive. This includes all the tables, as well as basic header info. */ bool trpgwArchive::CheckpointHeader() { trpgMemWriteBuffer buf(ness); if (!isValid()) return false; if (!header.isValid()) { if(header.getErrMess()) strcpy(errMess, header.getErrMess()); return false; } // This will close the appendable files if (tileFile) { tileFile->Flush(); } /* Build a Tile Table We need to build one from scratch here. However, we have all the relevant information collected during the WriteTile calls. */ if(header.GetIsLocal()) { int row = 0; int col = 0; header.GetBlocks(row,col); tileTable.SetCurrentBlock(row,col,true); } if (tileMode == TileExternal) { // External tiles are easy tileTable.SetMode(trpgTileTable::External); } else if( tileMode == TileExternalSaved) { if(!isRegenerate && firstHeaderWrite) { // Set up the sizes tileTable.SetMode(trpgTileTable::ExternalSaved); tileTable.SetNumLod(1); trpg2iPoint lodSize; header.GetLodSize(0,lodSize); tileTable.SetNumTiles(lodSize.x, lodSize.y, 0); firstHeaderWrite = false; } // Now set the individual tile locations for (unsigned int i=0;i= 1) { // Version 2.1, we store only lod 0 in the tile table // Set up the sizes tileTable.SetNumLod(1); trpg2iPoint lodSize; header.GetLodSize(0,lodSize); tileTable.SetNumTiles(lodSize.x, lodSize.y, 0); } else { // Set up the sizes int32 numLod; header.GetNumLods(numLod); tileTable.SetNumLod(numLod); for (int i=0;iGetVersion(majorVer,minorVer); if((majorVer >= TRPG_NOMERGE_VERSION_MAJOR) && (minorVer>=TRPG_NOMERGE_VERSION_MINOR)) { separateGeo = true; } return new trpgwImageHelper(ness,dir,inTexTable,separateGeo); } /* Increment Tile File. Close the current tile file (if any) and open the next one. Also update the records we're keeping of which tiles went in which files. */ bool trpgwArchive::IncrementTileFile() { if (tileMode != TileLocal) return false; // Closes the current tile file if (tileFile) { delete tileFile; tileFile=NULL; } // Open the next one char filename[1024]; sprintf(filename,"%s" PATHSEPERATOR "tileFile_%d.tpf",dir,tileFileCount++); tileFile = GetNewWAppFile(ness,filename,true); if (!tileFile->isValid()) return false; // Add another TileFiles entry tileFiles.resize(tileFiles.size()+1); tileFiles[tileFiles.size()-1].id = tileFiles.size()-1; return true; } /* Designate Tile File Close the current tile file (if any) and open one with the given base name. This is used for regenerate. */ bool trpgwArchive::DesignateTileFile(int id) { if (tileMode != TileLocal) return false; // Close the current tile file if (tileFile) { delete tileFile; tileFile=NULL; } // Open a named on char filename[1024]; sprintf(filename,"%s" PATHSEPERATOR "tileFile_%d.tpf",dir,id); tileFile = GetNewWAppFile(ness,filename); if (!tileFile->isValid()) return false; // Add another TileFiles entry tileFiles.resize(tileFiles.size()+1); tileFiles[tileFiles.size()-1].id = id; return true; } /* WriteTile. Write the given tile (x,y,lod) in the appropriate mode (Local or External). The tile header is given separately from the rest of the tile, but they are appended together to the file. */ bool trpgwArchive::WriteTile(unsigned int x,unsigned int y,unsigned int lod, float zmin, float zmax, const trpgMemWriteBuffer *head,const trpgMemWriteBuffer *buf, int32& fileId, int32& fileOffset) { FILE *tfp=NULL; if (!isValid()) return false; fileId = -1; fileOffset = -1; // External tiles get their own individual files if (tileMode == TileExternal || tileMode == TileExternalSaved) { // Make a new filename char filename[1024]; // Note: Windows specific sprintf(filename,"%s" PATHSEPERATOR "tile_%d_%d_%d.tpt",dir,x,y,lod); if (!(tfp = fopen(filename,"wb"))) return false; // Write the header first unsigned int len; const char *data; if (head) { data = head->getData(); len = head->length(); if (fwrite(data,sizeof(char),len,tfp) != len) { fclose(tfp); return false; } } // Write the buffer out data = buf->getData(); len = buf->length(); if (fwrite(data,sizeof(char),len,tfp) != len) { fclose(tfp); return false; } fclose(tfp); // In version 2.1 and over we still add an entry to the tile table // to save the zmin and zmax value. if(tileMode == TileExternalSaved && lod == 0) { externalTiles.push_back(TileFileEntry()); TileFileEntry& tf = externalTiles.back(); tf.x = x; tf.y = y; tf.lod = lod; tf.offset = -1; tf.zmax = zmax; tf.zmin = zmin; } } else { // Local tiles get appended to a tile file if (!tileFile) { if (!IncrementTileFile()) return false; } // See if we've exceeded the maximum advised size for a tile file while (maxTileFileLen > 0 && tileFile->GetLengthWritten() > maxTileFileLen) { if (!IncrementTileFile()) return false; } int32 pos = static_cast(tileFile->Pos()); if (!tileFile->Append(head,buf)) return false; // Keep track of the fact that this went here TileFile &tf = tileFiles[tileFiles.size()-1]; TileFileEntry te; te.x = x; te.y = y; te.lod = lod; te.zmin = zmin; te.zmax = zmax; te.offset = pos; if(majorVersion == 2 && minorVersion >=1) { // Version 2.1, we need to keep track of lod 0 only if(lod == 0) tf.tiles.push_back(te); } else tf.tiles.push_back(te); fileOffset = pos; fileId = tileFiles[tileFiles.size()-1].id; } return true; } /* **************** Geometry Stats Used by the Geometry Helper **************** */ trpgwGeomStats::trpgwGeomStats() { totalTri = totalStripTri = totalFanTri = totalBagTri = 0; for (int i=0;i<15;i++) { stripStat[i] = fanStat[i] = 0; } stripGeom = fanGeom = bagGeom = 0; stateChanges = 0; numStrip = numFan = 0; totalQuad = 0; } trpgwGeomStats::~trpgwGeomStats() { } /* **************** Geometry Helper Here, since it's used with a write archive. **************** */ trpgwGeomHelper::trpgwGeomHelper() { buf = NULL; mode = trpgGeometry::Triangles; } trpgwGeomHelper::~trpgwGeomHelper() { } void trpgwGeomHelper::SetMode(int m) { if (m == trpgGeometry::Triangles || m == trpgGeometry::Quads) mode = m; } trpgwGeomHelper::trpgwGeomHelper(trpgWriteBuffer *ibuf, int dtype) { init(ibuf,dtype); } void trpgwGeomHelper::init(trpgWriteBuffer *ibuf,int dtype) { buf = ibuf; dataType = dtype; zmin = 1e12; zmax = -1e12; } // Reset back to a clean state (except for the buffer) void trpgwGeomHelper::Reset() { ResetTri(); ResetPolygon(); zmin = 1e12; zmax = -1e12; } // Reset triangle arrays (usually after a flush) void trpgwGeomHelper::ResetTri() { strips.Reset(); fans.Reset(); bags.Reset(); tex.resize(0); norm.resize(0); vert.resize(0); } // Start a polygon definition void trpgwGeomHelper::StartPolygon() { ResetPolygon(); } // Finish a polygon definition void trpgwGeomHelper::EndPolygon() { // See if we can add it to the current triangle arrays if (vert.size() && (matTri != matPoly)) { // Couldn't flush geometry and move on FlushGeom(); } // Turn the polygon into triangles // Note: Only dealing with convex here matTri = matPoly; unsigned int numMats=matTri.size(); switch (mode) { case trpgGeometry::Triangles: { int num = polyVert.size() - 2; int id1,id2; for (int i=0;i 1) { id1 = i+2; id2 = i+1; } #else id1 = i+1; id2 = i+2; #endif // Define the triangle vert.push_back(polyVert[0]); vert.push_back(polyVert[id1]); vert.push_back(polyVert[id2]); norm.push_back(polyNorm[0]); norm.push_back(polyNorm[id1]); norm.push_back(polyNorm[id2]); // multiple textures unsigned int loop; for (loop=0;loopGetEndian() != trpg_cpu_byte_order()) { trpg3dPoint tmpVert; tmpVert.x = trpg_byteswap_8bytes_to_double ((char *)&pt.x); tmpVert.y = trpg_byteswap_8bytes_to_double ((char *)&pt.y); tmpVert.z = trpg_byteswap_8bytes_to_double ((char *)&pt.z); polyVert.push_back(tmpVert); } else #endif polyVert.push_back(pt); // Update min/max zmin = MIN(pt.z,zmin); zmax = MAX(pt.z,zmax); } // Flush the current set of geometry and move on void trpgwGeomHelper::FlushGeom() { bool hadGeom = false; switch (mode) { case trpgGeometry::Triangles: { Optimize(); // Write only if we've got something int numPrim; if (strips.GetNumPrims(numPrim) && numPrim) { strips.Write(*buf); stats.stripGeom++; hadGeom = true; } if (fans.GetNumPrims(numPrim) && numPrim) { fans.Write(*buf); stats.fanGeom++; hadGeom = true; } if (bags.GetNumPrims(numPrim) && numPrim) { bags.Write(*buf); stats.bagGeom++; hadGeom = true; } } break; case trpgGeometry::Quads: { unsigned int numVert = vert.size(); unsigned int numMat = matTri.size(); unsigned int loop; // Make sure we've got quads if (numVert % 4 == 0) { int dtype = (dataType == UseDouble ? trpgGeometry::DoubleData : trpgGeometry::FloatData); // Just dump the quads into single geometry node trpgGeometry quads; quads.SetPrimType(trpgGeometry::Quads); for (loop=0;loop &itex) { v = iv; n = in; tex=itex; valid = true;} optVert(int numMat, int vid, std::vector &iv, std::vector &in, std::vector &itex); trpg3dPoint v; trpg3dPoint n; std::vector tex; bool valid; int operator == (const optVert &in) const { return (v == in.v && n == in.n && tex == in.tex); } }; optVert::optVert(int numMat, int vid, std::vector &iv, std::vector &in, std::vector &itex) { v=iv[vid]; n=in[vid]; tex.resize(0); for (unsigned int loop=0; loop < (unsigned int)numMat; loop++) tex.push_back(itex[vid*numMat+loop]); } void trpgwGeomHelper::Optimize() { int dtype = (dataType == UseDouble ? trpgGeometry::DoubleData : trpgGeometry::FloatData); // Potentially writing to all of these strips.SetPrimType(trpgGeometry::TriStrips); fans.SetPrimType(trpgGeometry::TriFans); bags.SetPrimType(trpgGeometry::Triangles); unsigned int numMat = matTri.size(); for (unsigned int loop =0; loop < numMat; loop++ ) { strips.AddMaterial(matTri[loop]); strips.AddTexCoords(trpgGeometry::PerVertex); fans.AddMaterial(matTri[loop]); fans.AddTexCoords(trpgGeometry::PerVertex); bags.AddMaterial(matTri[loop]); bags.AddTexCoords(trpgGeometry::PerVertex); } int numTri = vert.size()/3; if (numTri == 0) return; // Iterate through the triangles enum {Strip,Fan,Bag}; int type,triId; optVert a[3],b[3],c[3]; for (triId = 0; triIdseparateGeoTypical = separateGeoTypical; maxTexFileLen = -1; } trpgwImageHelper::~trpgwImageHelper() { if (texFile) delete texFile; if (geotypFile) delete geotypFile; } bool trpgwImageHelper::AddExternal(char *name,int &texID,bool lookForExisting) { trpgTexture tex; tex.SetImageMode(trpgTexture::External); tex.SetName(name); if (lookForExisting) texID = texTable->FindAddTexture(tex); else texID = texTable->AddTexture(tex); return (texID != -1); } void trpgwImageHelper::SetMaxTexFileLength(int len) { maxTexFileLen = len; } bool trpgwImageHelper::AddLocal(char *name,trpgTexture::ImageType type,int sizeX,int sizeY, bool isMipmap,char *data,int &texID,bool deferWrite) { // Set up the basic texture trpgTexture tex; if(texID!=-1) tex.SetHandle(texID); tex.SetName(name); tex.SetImageMode(trpgTexture::Local); tex.SetImageType(type); int depth; tex.GetImageDepth(depth); tex.SetNumLayer(depth); tex.SetImageSize(trpg2iPoint(sizeX,sizeY)); tex.SetIsMipmap(isMipmap); // Write the image out to disk trpgwAppAddress addr; if(!deferWrite) if (!WriteToArchive(tex,data,addr,true)) return false; // Now add the specifics to the texture table tex.SetImageAddr(addr); texID = texTable->AddTexture(tex); return true; } bool trpgwImageHelper::ReplaceLocal(char *data,int &texID) { const trpgTexture *texRef=texTable->GetTextureRef(texID); if (!texRef) return false; // Write the image out to disk trpgwAppAddress addr; if (!WriteToArchive(*texRef,data,addr,true)) return false; // Now add the specifics to the texture table const_cast(texRef)->SetImageAddr(addr); return true; } bool trpgwImageHelper::AddTileLocal(char *name,trpgTexture::ImageType type, int sizeX, int sizeY, bool isMipmap,char *data,int &texID,trpgwAppAddress &addr) { // Set up the texture template and add to the table trpgTexture tex; if(texID!=-1) tex.SetHandle(texID); tex.SetName(name); tex.SetImageMode(trpgTexture::Template); tex.SetImageType(type); int depth; tex.GetImageDepth(depth); tex.SetNumLayer(depth); tex.SetImageSize(trpg2iPoint(sizeX,sizeY)); tex.SetIsMipmap(isMipmap); texID = texTable->FindAddTexture(tex); // Write the specific data out to an archive (return the address) if (!WriteToArchive(tex,data,addr,false)) return false; return true; } /* Get new appendable file. */ trpgwAppFile* trpgwImageHelper::GetNewWAppFile(trpgEndian inNess,const char *fileName,bool reuse) { return new trpgwAppFile(inNess,fileName,reuse); } /* Increment Texture File. Close the current texture file (if any) and open the next one. */ trpgwAppFile * trpgwImageHelper::IncrementTextureFile(bool geotyp) { char filename[1024]; trpgwAppFile *thefile = texFile; if(geotyp && separateGeoTypical) { thefile = geotypFile; sprintf(filename,"%s" PATHSEPERATOR "geotypFile_%d.txf",dir,static_cast(geotypFileIDs.size())); } else { sprintf(filename,"%s" PATHSEPERATOR "texFile_%d.txf",dir,static_cast(texFileIDs.size())); } // Closes the current texture file if (thefile) delete thefile; thefile = NULL; // Open the next one thefile = GetNewWAppFile(ness,filename,true); if (!thefile->isValid()) return NULL; if(geotyp && separateGeoTypical) { geotypFileIDs.push_back(geotypFileIDs.size()); geotypFile = thefile; } else { texFileIDs.push_back(texFileIDs.size()); texFile = thefile; } return thefile; } // Flush current texture file (if any) bool trpgwImageHelper::Flush() { if (texFile) texFile->Flush(); if (geotypFile) geotypFile->Flush(); return true; } /* Designate Texture File Close the curren texture file (if any) and open one with the given base name. */ bool trpgwImageHelper::DesignateTextureFile(int id) { // Close the current texture file if (texFile) delete texFile; texFile = NULL; // Open one with the given base name char filename[1024]; sprintf(filename,"%s" PATHSEPERATOR "texFile_%d.txf",dir,id); texFile = GetNewWAppFile(ness,filename); if (!texFile->isValid()) return false; texFileIDs.push_back(id); sprintf(filename,"%s" PATHSEPERATOR "geotypFile_%d.txf",dir,id); geotypFile = GetNewWAppFile(ness,filename); if (!geotypFile->isValid()) return false; geotypFileIDs.push_back(id); return true; } /* Write To Archive. Write the given image data out to an appropriate archive and return the address. This is used for Local and Tile Local textures. */ bool trpgwImageHelper::WriteToArchive(const trpgTexture &tex,char *data,trpgwAppAddress &addr,bool geotyp) { trpg2iPoint size; tex.GetImageSize(size); int32 depth; tex.GetImageDepth(depth); trpgwAppFile *thefile = texFile; if(geotyp && separateGeoTypical) { thefile = geotypFile; } // Get a usable texture archive file if (!thefile) { if (! (thefile=IncrementTextureFile(geotyp && separateGeoTypical))) return false; } while (maxTexFileLen > 0 && thefile->GetLengthWritten() > maxTexFileLen) { if (!(thefile=IncrementTextureFile(geotyp && separateGeoTypical))) return false; } // Get the current address if(geotyp && separateGeoTypical) { addr.file = geotypFileIDs[geotypFileIDs.size()-1]; } else { addr.file = texFileIDs[texFileIDs.size()-1]; } addr.offset = (int32)thefile->Pos(); // Write the data out to the archive. int totSize = tex.CalcTotalSize(); if (!thefile->Append(data,totSize)) return false; return true; }