/* ************************ 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_material.cpp This source file contains the methods for trpgMatTable, trpgTextureEnv, trpgMaterial, and trpgTexTable. You should only modify this code if you want to add data to these classes. */ #include "trpage_geom.h" #include "trpage_read.h" /* Write Material Table class Keeps track of the materials that have been added. */ // Constructor trpgMatTable::trpgMatTable() { numTable = numMat = 0; } trpgMatTable::~trpgMatTable() { } // Reset function void trpgMatTable::Reset() { numTable = 0; numMat = 0; matTables.resize(0); } // Validity check bool trpgMatTable::isValid() const { if (numTable <= 0 || numMat <= 0) return false; for (int i=0;i= numTable) return; if (nm < 0 || nm >= numMat) return; matTables[nt*numMat+nm] = mat; } void trpgMatTable::SetMaterial(int nm,const trpgMaterial &mat) { if (nm < 0 || nm >= numMat) return; for (int i=0;i= matTables.size()) { matTables.push_back(cmat); numMat++; } else // Found it. Just return this ID. return baseMat; return numMat-1; } // Write out material table bool trpgMatTable::Write(trpgWriteBuffer &buf) { int i; if (!isValid()) return false; buf.Begin(TRPGMATTABLE); // Total number of materials buf.Add((int32)numTable); buf.Add((int32)numMat); // Write the materials for (i=0;i= numTable || nm < 0 || nm >= numMat) return false; mat = matTables[nt*numMat+nm]; return true; } const trpgMaterial *trpgMatTable::GetMaterialRef(int nt,int nm) const { if (nt < 0 || nt >= numTable || nm < 0 || nm >= numMat) return false; return const_cast(&matTables[nt*numMat+nm]); } bool trpgMatTable::Read(trpgReadBuffer &buf) { trpgMaterial mat; trpgToken matTok; int32 len; bool status; int i,j; try { buf.Get(numTable); buf.Get(numMat); if (numTable <= 0 || numMat < 0) throw 1; // Read the materials matTables.resize(numTable*numMat); for (i=0;iSetEnvMode(envMode); break; case TRPGMAT_TXENV_FILTER: buf.Get(minFilter); buf.Get(magFilter); tenv->SetMinFilter(minFilter); tenv->SetMagFilter(magFilter); break; case TRPGMAT_TXENV_WRAP: buf.Get(wrapS); buf.Get(wrapT); tenv->SetWrap(wrapS,wrapT); break; case TRPGMAT_TXENV_BORDER: buf.Get(borderCol); tenv->SetBorderColor(borderCol); break; default: // Don't know this token. Skip break; } } catch (...) { return NULL; } return tenv; } bool trpgTextureEnv::Read(trpgReadBuffer &buf) { trpgr_Parser parse; textureEnvCB teCb; // Texture environment is a bunch of tokens in random order // Interface to it with a parser teCb.tenv = this; parse.AddCallback(TRPGMAT_TXENV_MODE,&teCb,false); parse.AddCallback(TRPGMAT_TXENV_FILTER,&teCb,false); parse.AddCallback(TRPGMAT_TXENV_WRAP,&teCb,false); parse.AddCallback(TRPGMAT_TXENV_BORDER,&teCb,false); parse.Parse(buf); return isValid(); } /* Write Material class Material representation. */ // Constructor trpgMaterial::trpgMaterial() { Reset(); } trpgMaterial::~trpgMaterial() { } // Reset function void trpgMaterial::Reset() { color = trpgColor(1,1,1); ambient = trpgColor(0,0,0); diffuse = trpgColor(1,1,1); specular = trpgColor(0,0,0); emission = trpgColor(0,0,0); shininess = 0; shadeModel = Smooth; pointSize = 1; lineWidth = 1; cullMode = Back; alphaFunc = GreaterThan; alphaRef = 0; alpha = 1.0; autoNormal = false; numTex = 0; texids.resize(0); texEnvs.resize(0); numTile = 0; isBump = false; } // Validity check bool trpgMaterial::isValid() const { // Only thing we really care about is texture if (numTex < 0) return false; for (int i=0;i= texids.size()) return; texids[no] = id; texEnvs[no] = env; } int trpgMaterial::AddTexture(int id,const trpgTextureEnv &env) { texids.push_back(id); texEnvs.push_back(env); numTex++; return numTex-1; } void trpgMaterial::SetNumTiles(int no) { numTile = no; } int trpgMaterial::AddTile() { return(++numTile); } void trpgMaterial::SetIsBumpMap(bool val) { isBump = val; } // Write to buffer bool trpgMaterial::Write(trpgWriteBuffer &buf) { if (!isValid()) return false; buf.Begin(TRPGMATERIAL); // Bundle the basic material parameters together buf.Begin(TRPGMAT_BASIC); buf.Add(color); buf.Add(ambient); buf.Add(diffuse); buf.Add(specular); buf.Add(emission); buf.Add(shininess); buf.Add(numTile); buf.End(); // Most everything else is a single token. // This is inefficient, but expandable buf.Begin(TRPGMAT_SHADE); buf.Add(shadeModel); buf.End(); buf.Begin(TRPGMAT_SIZES); buf.Add(pointSize); buf.Add(lineWidth); buf.End(); buf.Begin(TRPGMAT_CULL); buf.Add(cullMode); buf.End(); buf.Begin(TRPGMAT_ALPHA); buf.Add(alphaFunc); buf.Add(alphaRef); buf.Add(alpha); buf.End(); buf.Begin(TRPGMAT_NORMAL); buf.Add(autoNormal); buf.End(); buf.Begin(TRPGMAT_TEXTURE); buf.Add(numTex); for (int i=0;i= numTex) return false; id = texids[no]; te = texEnvs[no]; return true; } bool trpgMaterial::GetNumTile(int &no) const { if (!isValid()) return false; no = numTile; return true; } bool trpgMaterial::GetIsBumpMap(bool &ret) const { if (!isValid()) return false; ret = isBump; return true; } /* Material CB Used to parse tokens for a material. */ class materialCB : public trpgr_Callback { public: void * Parse(trpgToken,trpgReadBuffer &); trpgMaterial *mat; }; void * materialCB::Parse(trpgToken tok,trpgReadBuffer &buf) { trpgColor color; float64 shininess; int shadeModel; float64 size; int cullMode; int alphaFunc; float64 alphaRef,alpha; bool autoNormal; int numTex,texId; trpgToken envTok; trpgTextureEnv texEnv; int32 len,numtile; bool status; int i; try { switch (tok) { case TRPGMAT_BASIC: buf.Get(color); mat->SetColor(color); buf.Get(color); mat->SetAmbient(color); buf.Get(color); mat->SetDiffuse(color); buf.Get(color); mat->SetSpecular(color); buf.Get(color); mat->SetEmission(color); buf.Get(shininess); mat->SetShininess(shininess); buf.Get(numtile); mat->SetNumTiles(numtile); break; case TRPGMAT_SHADE: buf.Get(shadeModel); mat->SetShadeModel(shadeModel); break; case TRPGMAT_SIZES: buf.Get(size); mat->SetPointSize(size); buf.Get(size); mat->SetLineWidth(size); break; case TRPGMAT_CULL: buf.Get(cullMode); mat->SetCullMode(cullMode); break; case TRPGMAT_ALPHA: buf.Get(alphaFunc); buf.Get(alphaRef); buf.Get(alpha); mat->SetAlphaFunc(alphaFunc); mat->SetAlphaRef(alphaRef); mat->SetAlpha(alpha); break; case TRPGMAT_NORMAL: { int tmp; buf.Get(tmp); if (tmp) autoNormal = true; else autoNormal = false; mat->SetAutoNormal(autoNormal); } break; case TRPGMAT_TEXTURE: buf.Get(numTex); for (i=0;iAddTexture(texId,texEnv); } break; case TRPGMAT_BUMP: { int tmp; buf.Get(tmp); bool isBump = (tmp) ? true : false; mat->SetIsBumpMap(isBump); } default: break; } } catch (...) { return NULL; } return mat; } bool trpgMaterial::Read(trpgReadBuffer &buf) { trpgr_Parser parse; materialCB matCb; // Material is just a bunch of unordered tokens. // Interface to it with a generic parser matCb.mat = this; parse.AddCallback(TRPGMAT_BASIC,&matCb,false); parse.AddCallback(TRPGMAT_SHADE,&matCb,false); parse.AddCallback(TRPGMAT_SIZES,&matCb,false); parse.AddCallback(TRPGMAT_CULL,&matCb,false); parse.AddCallback(TRPGMAT_ALPHA,&matCb,false); parse.AddCallback(TRPGMAT_NORMAL,&matCb,false); parse.AddCallback(TRPGMAT_TEXTURE,&matCb,false); parse.AddCallback(TRPGMAT_BUMP,&matCb,false); parse.Parse(buf); return isValid(); } /* Texture Really just a container for a texture name and use count. The use count is used for paging. */ // Constructor trpgTexture::trpgTexture() { mode = External; type = trpg_Unknown; name = NULL; useCount = 0; sizeX = sizeY = -1; addr.file = 0; addr.offset = 0; isMipmap = false; } // Copy construction trpgTexture::trpgTexture(const trpgTexture &in) { mode = in.mode; type = in.type; name = NULL; SetName(in.name); useCount = in.useCount; sizeX = in.sizeX; sizeY = in.sizeY; addr.file = in.addr.file; addr.offset = in.addr.offset; isMipmap = in.isMipmap; } // Destruction trpgTexture::~trpgTexture() { Reset(); } // Reset void trpgTexture::Reset() { mode = External; type = trpg_Unknown; if (name) delete [] name; name = NULL; useCount = 0; sizeX = sizeY = -1; addr.file = 0; addr.offset = 0; isMipmap = false; storageSize.clear(); levelOffset.clear(); } // Valid if we've got a name bool trpgTexture::isValid() const { switch (mode) { case External: return (name != NULL); break; case Local: return (type != trpg_Unknown && sizeX != -1 && sizeY != -1); break; case Global: return (type != trpg_Unknown); break; case Template: return (type != trpg_Unknown && sizeX != -1 && sizeY != -1); break; default: return false; } return false; } // Set Name void trpgTexture::SetName(const char *inName) { if (name) delete [] name; name = NULL; if (!inName) return; name = new char[strlen(inName)+1]; strcpy(name,inName); } // Get Name bool trpgTexture::GetName(char *outName,int outLen) const { if (!isValid()) return false; int len = (name) ? strlen(name) : 0; strncpy(outName,name,MIN(len,outLen)+1); return true; } void trpgTexture::SetImageMode(ImageMode inMode) { mode = inMode; } bool trpgTexture::GetImageMode(ImageMode &outMode) const { outMode = mode; return true; } void trpgTexture::SetImageType(ImageType inType) { type = inType; } bool trpgTexture::GetImageType(ImageType &outType) const { outType = type; return true; } void trpgTexture::SetImageSize(const trpg2iPoint &inSize) { sizeX = inSize.x; sizeY = inSize.y; } bool trpgTexture::GetImageSize(trpg2iPoint &outSize) const { if (mode != Local && mode != Template) return false; outSize.x = sizeX; outSize.y = sizeY; return true; } void trpgTexture::SetIsMipmap(bool val) { isMipmap = val; } bool trpgTexture::GetIsMipmap(bool &ret) const { ret = isMipmap; return true; } bool trpgTexture::GetImageAddr(trpgwAppAddress &outAddr) const { if (mode != Local) return false; outAddr = addr; return true; } void trpgTexture::SetImageAddr(const trpgwAppAddress &inAddr) { addr = inAddr; } bool trpgTexture::GetImageDepth(int32 &depth) const { switch (type) { case trpg_RGB8: depth = 3; break; case trpg_RGBA8: depth = 4; break; case trpg_INT8: depth = 1; break; case trpg_INTA8: depth = 2; break; case trpg_FXT1: depth = 3; break; case trpg_RGBX: depth = numLayer; break; case trpg_DXT1: depth = 3; break; case trpg_DXT3: depth = 3; break; case trpg_DXT5: depth = 3; break; default: depth = -1; break; } return true; } // Use count management void trpgTexture::SetNumTile(int num) { useCount = num; } void trpgTexture::AddTile() { useCount++; } bool trpgTexture::GetNumTile(int &num) const { if (!isValid()) return false; num = useCount; return true; } // Copy operator trpgTexture &trpgTexture::operator = (const trpgTexture &in) { mode = in.mode; type = in.type; if (in.name) SetName(in.name); useCount = in.useCount; sizeX = in.sizeX; sizeY = in.sizeY; isMipmap = in.isMipmap; addr = in.addr; return *this; } // Equality operator int trpgTexture::operator == (const trpgTexture &in) const { if (mode != in.mode) return 0; switch (mode) { case External: if (!in.name && !name) return 1; if (!in.name || !name) return 0; return (!strcmp(in.name,name)); break; case Local: if (type == in.type && sizeX == in.sizeX && sizeY == in.sizeY && isMipmap == in.isMipmap && addr.file == in.addr.file && addr.offset == in.addr.offset) return 1; break; case Global: case Template: if (type == in.type && sizeX == in.sizeX && sizeY == in.sizeY && isMipmap == in.isMipmap) return 1; } return 0; } // Utility functions int32 trpgTexture::CalcNumMipmaps() const { // We're going to assume these are powers of two. // If not, then the writer's a moron. // :))) The comment line above made me really loughing, Steve. - Nick int bval = MAX(sizeX,sizeY); // Now look for the highest bit int p2; for (p2=0;p2<32;p2++) if ((1<> 3; if (x > 1) x /= 2; if (y > 1) y /= 2; } return totSize; } }; // Figure out the total data size, including mipmaps if necessary int32 depth; GetImageDepth(depth); totSize = sizeX * sizeY * depth; // Do mipmaps if they're there if (isMipmap) { trpg2iPoint size(sizeX,sizeY); int maxSize = MAX(size.x,size.y); while (maxSize > 1) { size.x = size.x >> 1; size.x = MAX(1,size.x); size.y = size.y >> 1; size.y = MAX(1,size.y); maxSize = maxSize >> 1; totSize += size.x*size.y*depth; } } return totSize; } // Calculate the size of a given mip level int32 trpgTexture::MipLevelSize(int miplevel) { if ( miplevel > 0 && miplevel < CalcNumMipmaps() ) { if ( !storageSize.size() ) CalcMipLevelSizes(); return storageSize[miplevel]; } return 0; } int32 trpgTexture::MipLevelOffset(int miplevel) { if ( miplevel > 0 && miplevel < CalcNumMipmaps() ) { if ( !levelOffset.size() ) CalcMipLevelSizes(); return levelOffset[miplevel]; } return 0; } // Write function bool trpgTexture::Write(trpgWriteBuffer &buf) { if (!isValid()) return false; buf.Begin(TRPGTEXTURE); buf.Add(name); buf.Add(useCount); // New in 2.0 from here down buf.Add((unsigned char)mode); buf.Add((unsigned char)type); buf.Add(sizeX); buf.Add(sizeY); buf.Add(addr.file); buf.Add(addr.offset); buf.Add(isMipmap); buf.End(); return true; } // Read function bool trpgTexture::Read(trpgReadBuffer &buf) { char texName[1024]; try { buf.Get(texName,1023); SetName(texName); buf.Get(useCount); mode = External; // New in 2.0 from here down unsigned char bval; buf.Get(bval); mode = (trpgTexture::ImageMode)bval; buf.Get(bval); type = (trpgTexture::ImageType)bval; buf.Get(sizeX); buf.Get(sizeY); buf.Get(addr.file); buf.Get(addr.offset); int ival; buf.Get(ival); isMipmap = (ival) ? true : false; } catch (...) { return false; } if (!isValid()) return false; // calculate the mip level sizes CalcMipLevelSizes(); return true; } void trpgTexture::CalcMipLevelSizes() { int num_miplevels = CalcNumMipmaps(); int level_size = 0; int level_offset = 0; int block_size = 0; int pixel_size = 0; switch (type) { case trpg_DXT1: block_size = 8; break; case trpg_DXT3: case trpg_DXT5: block_size = 16; break; case trpg_RGB8: pixel_size = 3; break; case trpg_RGBA8: pixel_size = 4; break; case trpg_INT8: pixel_size = 1; break; case trpg_INTA8: pixel_size = 2; break; } levelOffset.clear(); storageSize.clear(); levelOffset.push_back(level_offset); if ( block_size ) { // DXT compressed int num_x_blocks = ((sizeX/4)+(sizeX%4?1:0)); int num_y_blocks = ((sizeY/4)+(sizeY%4?1:0)); level_size = num_x_blocks * num_y_blocks * block_size; storageSize.push_back(level_size); for ( int i = 1; i < num_miplevels; i++ ) { level_offset += level_size; levelOffset.push_back(level_offset); num_x_blocks /= 2; num_y_blocks /= 2; num_x_blocks = MAX(1,num_x_blocks); num_y_blocks = MAX(1,num_y_blocks); level_size = num_x_blocks * num_y_blocks * block_size; storageSize.push_back(level_size); } } else { int x_size = sizeX; int y_size = sizeY; level_size = x_size * y_size * pixel_size; storageSize.push_back(level_size); for ( int i = 1; i < num_miplevels; i++ ) { level_offset += level_size; levelOffset.push_back(level_offset); x_size /= 2; y_size /= 2; x_size = MAX(1,x_size); y_size = MAX(1,y_size); level_size = x_size * y_size * pixel_size; storageSize.push_back(level_size); } } } /* Texture Table Just a list of texture names so we can index. */ // Constructor trpgTexTable::trpgTexTable() { } trpgTexTable::trpgTexTable(const trpgTexTable &in) { *this = in; } // Reset function void trpgTexTable::Reset() { texList.resize(0); } // Destructor trpgTexTable::~trpgTexTable() { Reset(); } // Validity check bool trpgTexTable::isValid() const { if (!texList.size()) return false; for (unsigned int i=0;i= texList.size()) return; texList[id] = inTex; } // Copy operator trpgTexTable &trpgTexTable::operator = (const trpgTexTable &in) { Reset(); for (int i=0;i= texList.size()) return false; ret = texList[id]; return true; } const trpgTexture *trpgTexTable::GetTextureRef(int id) const { if (id < 0 || id >= texList.size()) return NULL; return &texList[id]; } bool trpgTexTable::Read(trpgReadBuffer &buf) { int32 numTex; trpgToken texTok; int32 len; try { buf.Get(numTex); texList.resize(numTex); for (unsigned int i=0;i *inSizes) { storageSize.resize(inSizes->size()); for (int i=0;isize();i++) storageSize[i] = (*inSizes)[i]; return true; }*/ /*bool trpgLocalMaterial::GetStorageSizes(const vector *retSize) { if (!isValid()) return false; retSize = storageSize; return true; }*/ bool trpgLocalMaterial::isValid() const { if (baseMat < 0) return false; return true; } // Write method bool trpgLocalMaterial::Write(trpgWriteBuffer &buf) { if (!isValid()) return false; buf.Begin(TRPGLOCALMATERIAL); // Write the data buf.Add(baseMatTable); buf.Add(baseMat); buf.Add(sx); buf.Add(sy); buf.Add(ex); buf.Add(ey); buf.Add(destWidth); buf.Add(destHeight); buf.Add(addr.file); buf.Add(addr.offset); buf.End(); return true; } // Read method bool trpgLocalMaterial::Read(trpgReadBuffer &buf) { try { buf.Get(baseMatTable); buf.Get(baseMat); buf.Get(sx); buf.Get(sy); buf.Get(ex); buf.Get(ey); buf.Get(destWidth); buf.Get(destHeight); buf.Get(addr.file); buf.Get(addr.offset); } catch (...) { return false; } return isValid(); }