Files
OpenSceneGraph/src/osgPlugins/txp/trpage_material.cpp
2002-03-09 10:51:09 +00:00

1689 lines
35 KiB
C++

/* ************************
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
/* 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<matTables.size();i++)
if (!matTables[i].isValid())
return false;
return true;
}
// Set functions
void trpgMatTable::SetNumTable(int no)
{
if (no < 0)
return;
numTable = no;
matTables.resize(numTable*numMat);
}
void trpgMatTable::SetNumMaterial(int no)
{
if (no < 0)
return;
// Note: Doesn't preserve the order of what was there
numMat = no;
matTables.resize(numMat*numTable);
}
void trpgMatTable::SetMaterial(int nt,int nm,const trpgMaterial &mat)
{
if (nt < 0 || nt >= 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<numTable;i++)
matTables[i*numMat+nm] = mat;
}
#define CEQ(ca,cb) (ca.red == cb.red && ca.green == cb.green && ca.blue == cb.blue)
int trpgMatTable::AddMaterial(const trpgMaterial &mat)
{
// Doesn't work if there's more than one table
if (numTable != 1)
return -1;
// Look for a matching base material minus the textures
trpgMaterial cmat = mat;
int baseMat;
for (baseMat = 0;baseMat < matTables.size();baseMat++) {
trpgMaterial &bm = matTables[baseMat];
// Compare structures
if (CEQ(cmat.color,bm.color) && CEQ(cmat.ambient,bm.ambient) &&
CEQ(cmat.diffuse,bm.diffuse) && CEQ(cmat.specular,bm.specular) &&
CEQ(cmat.emission,bm.emission) && cmat.shininess == bm.shininess &&
cmat.shadeModel == bm.shadeModel && cmat.pointSize == bm.pointSize &&
cmat.lineWidth == bm.lineWidth && cmat.cullMode == bm.cullMode &&
cmat.alphaFunc == bm.alphaFunc && cmat.alphaRef == bm.alphaRef &&
cmat.autoNormal == bm.autoNormal && cmat.texEnvs.size() == bm.texEnvs.size()) {
// Test the texture envs
bool isSame=true;
int i;
for (i=0;i<cmat.texEnvs.size();i++) {
trpgTextureEnv &e1 = cmat.texEnvs[i];
trpgTextureEnv &e2 = bm.texEnvs[i];
if (e1.envMode != e2.envMode ||
e1.minFilter != e2.minFilter ||
e1.magFilter != e2.magFilter ||
e1.wrapS != e2.wrapS || e1.wrapT != e2.wrapT ||
!CEQ(e1.borderCol,e2.borderCol))
isSame = false;
}
// Test the texture IDs
for (i=0;i<cmat.texids.size();i++) {
if (cmat.texids[i] != bm.texids[i])
isSame = false;
}
if (isSame)
break;
}
}
// Didn't find it. Add it
if (baseMat >= 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<matTables.size();i++)
matTables[i].Write(buf);
buf.End();
return true;
}
/* ************
Material Table Read
***********
*/
// Get functions
bool trpgMatTable::GetNumTable(int &no) const
{
if (!isValid()) return false;
no = numTable;
return true;
}
bool trpgMatTable::GetNumMaterial(int &no) const
{
if (!isValid()) return false;
no = numMat;
return true;
}
bool trpgMatTable::GetMaterial(int nt,int nm,trpgMaterial &mat) const
{
if (!isValid()) return false;
if (nt < 0 || nt >= 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<trpgMaterial *>(&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;i<numTable;i++)
for (j=0;j<numMat;j++) {
buf.GetToken(matTok,len);
if (matTok != TRPGMATERIAL) throw 1;
buf.PushLimit(len);
mat.Reset();
status = mat.Read(buf);
buf.PopLimit();
if (!status) throw 1;
matTables[i*numMat+j] = mat;
}
}
catch (...) {
return false;
}
return isValid();
}
/* Write Texture Environment class
Used to specify how a texture is applied.
Associated with materials.
*/
// Constructor
trpgTextureEnv::trpgTextureEnv()
{
Reset();
}
trpgTextureEnv::~trpgTextureEnv()
{
}
// Reset function
void trpgTextureEnv::Reset()
{
envMode = Decal;
minFilter = Linear;
magFilter = MipmapBilinear;
wrapS = wrapT = Repeat;
borderCol = trpgColor(0,0,0);
}
bool trpgTextureEnv::isValid() const
{
return true;
}
// Set functions
void trpgTextureEnv::SetEnvMode(int mode)
{
envMode = mode;
}
void trpgTextureEnv::SetMinFilter(int fl)
{
minFilter = fl;
}
void trpgTextureEnv::SetMagFilter(int fl)
{
magFilter = fl;
}
void trpgTextureEnv::SetWrap(int s,int t)
{
wrapS = s;
wrapT = t;
}
void trpgTextureEnv::SetBorderColor(const trpgColor &col)
{
borderCol = col;
}
// Write function
bool trpgTextureEnv::Write(trpgWriteBuffer &buf)
{
if (!isValid())
return false;
buf.Begin(TRPGMAT_TEXENV);
buf.Begin(TRPGMAT_TXENV_MODE);
buf.Add(envMode);
buf.End();
buf.Begin(TRPGMAT_TXENV_FILTER);
buf.Add(minFilter);
buf.Add(magFilter);
buf.End();
buf.Begin(TRPGMAT_TXENV_WRAP);
buf.Add(wrapS);
buf.Add(wrapT);
buf.End();
buf.Begin(TRPGMAT_TXENV_BORDER);
buf.Add(borderCol);
buf.End();
buf.End();
return true;
}
/* **************
Texture Env Read support
**************
*/
// Get functions
bool trpgTextureEnv::GetEnvMode(int32 &ret) const
{
if (!isValid()) return false;
ret = envMode;
return true;
}
bool trpgTextureEnv::GetMinFilter(int32 &ret) const
{
if (!isValid()) return false;
ret = minFilter;
return true;
}
bool trpgTextureEnv::GetMagFilter(int32 &ret) const
{
if (!isValid()) return false;
ret = magFilter;
return true;
}
bool trpgTextureEnv::GetWrap(int &S,int &T) const
{
if (!isValid()) return false;
S = wrapS;
T = wrapT;
return true;
}
bool trpgTextureEnv::GetBorderColor(trpgColor &col) const
{
if (!isValid()) return false;
col = borderCol;
return true;
}
/* Texture Env CB
Used to parse tokens for a texture Env.
*/
class textureEnvCB : public trpgr_Callback {
public:
void *Parse(trpgToken,trpgReadBuffer &);
trpgTextureEnv *tenv;
};
void * textureEnvCB::Parse(trpgToken tok,trpgReadBuffer &buf)
{
int envMode;
int minFilter;
int magFilter;
int wrapS,wrapT;
trpgColor borderCol;
try {
switch (tok) {
case TRPGMAT_TXENV_MODE:
buf.Get(envMode);
tenv->SetEnvMode(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<numTex;i++)
if (!texEnvs[i].isValid())
return false;
return true;
}
// Set functions
void trpgMaterial::SetColor(const trpgColor &col)
{
color = col;
}
void trpgMaterial::SetAmbient(const trpgColor &col)
{
ambient = col;
}
void trpgMaterial::SetDiffuse(const trpgColor &col)
{
diffuse = col;
}
void trpgMaterial::SetSpecular(const trpgColor &col)
{
specular = col;
}
void trpgMaterial::SetEmission(const trpgColor &col)
{
emission = col;
}
void trpgMaterial::SetShininess(float64 val)
{
shininess = val;
}
void trpgMaterial::SetShadeModel(int val)
{
shadeModel = val;
}
void trpgMaterial::SetPointSize(float64 val)
{
pointSize = val;
}
void trpgMaterial::SetLineWidth(float64 val)
{
lineWidth = val;
}
void trpgMaterial::SetCullMode(int val)
{
cullMode = val;
}
void trpgMaterial::SetAlphaFunc(int val)
{
alphaFunc = val;
}
void trpgMaterial::SetAlphaRef(float64 val)
{
alphaRef = val;
}
void trpgMaterial::SetAlpha(float64 val)
{
alpha = val;
}
void trpgMaterial::SetAutoNormal(bool val)
{
autoNormal = val;
}
void trpgMaterial::SetNumTexture(int no)
{
if (no < 0) return;
numTex = no;
texids.resize(no);
texEnvs.resize(no);
}
void trpgMaterial::SetTexture(int no,int id,const trpgTextureEnv &env)
{
if (no < 0 || (unsigned int)no >= 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;i++) {
buf.Add(texids[i]);
texEnvs[i].Write(buf);
}
buf.End();
// Bump mapping properties
buf.Begin(TRPGMAT_BUMP);
buf.Add(isBump);
buf.End();
buf.End();
return true;
}
/* ***************
Material Read methods
***************
*/
// Get methods
bool trpgMaterial::GetColor(trpgColor &col) const
{
if (!isValid()) return false;
col = color;
return true;
}
bool trpgMaterial::GetAmbient(trpgColor &col) const
{
if (!isValid()) return false;
col = ambient;
return true;
}
bool trpgMaterial::GetDiffuse(trpgColor &col) const
{
if (!isValid()) return false;
col = diffuse;
return true;
}
bool trpgMaterial::GetSpecular(trpgColor &col) const
{
if (!isValid()) return false;
col = specular;
return true;
}
bool trpgMaterial::GetEmission(trpgColor &col) const
{
if (!isValid()) return false;
col = emission;
return true;
}
bool trpgMaterial::GetShininess(float64 &shin) const
{
if (!isValid()) return false;
shin = shininess;
return true;
}
bool trpgMaterial::GetShadeModel(int &sm) const
{
if (!isValid()) return false;
sm = shadeModel;
return true;
}
bool trpgMaterial::GetPointSize(float64 &ps) const
{
if (!isValid()) return false;
ps = pointSize;
return true;
}
bool trpgMaterial::GetLineWidth(float64 &lw) const
{
if (!isValid()) return false;
lw = lineWidth;
return true;
}
bool trpgMaterial::GetCullMode(int32 &cull) const
{
if (!isValid()) return false;
cull = cullMode;
return true;
}
bool trpgMaterial::GetAlphaFunc(int &af) const
{
if (!isValid()) return false;
af = alphaFunc;
return true;
}
bool trpgMaterial::GetAlphaRef(float64 &ar) const
{
if (!isValid()) return false;
ar = alphaRef;
return true;
}
bool trpgMaterial::GetAlpha(float64 &a) const
{
if (!isValid()) return false;
a = alpha;
return true;
}
bool trpgMaterial::GetAutoNormal(bool &an) const
{
if (!isValid()) return false;
an = autoNormal;
return true;
}
bool trpgMaterial::GetNumTexture(int &no) const
{
if (!isValid()) return false;
no = numTex;
return true;
}
bool trpgMaterial::GetTexture(int no,int &id,trpgTextureEnv &te) const
{
if (!isValid() || no < 0 || no >= 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;i<numTex;i++) {
buf.Get(texId);
// Parse the texture Env
buf.GetToken(envTok,len);
if (envTok != TRPGMAT_TEXENV) throw 1;
buf.PushLimit(len);
status = texEnv.Read(buf);
buf.PopLimit();
if (!status) throw 1;
mat->AddTexture(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<<p2) & bval)
break;
return p2+1;
}
// Note: replacing this with explicit sizes
int32 trpgTexture::CalcTotalSize() const
{
int totSize=0;
// DXT compressed textures need diferent algorithm for this
switch (type) {
case trpg_DXT1:
case trpg_DXT3:
case trpg_DXT5:{
int blocksize = ( type == trpg_DXT1 ? 8 : 16 );
int blocksX = sizeX / 4;
int blocksY = sizeY / 4;
totSize = blocksX * blocksY * blocksize;
if ( isMipmap ) {
int nummiplevels = CalcNumMipmaps();
for ( int i = 1; i < nummiplevels; i++ ) {
blocksX /= 2;
blocksY /= 2;
blocksX = MAX(1,blocksX);
blocksY = MAX(1,blocksY);
totSize += blocksX*blocksY*blocksize;
}
}
}
return totSize;
// FXT1 is also calculated differently
case trpg_FXT1:
{
// bits per pixel and size
int bpp = 4;
int x = sizeX;
int y = sizeY;
int nummiplevels = (isMipmap ? CalcNumMipmaps() : 1);
for (int i = 0; i < nummiplevels; i++) {
x = ( x + 0x7 ) & ~0x7;
y = ( y + 0x3 ) & ~0x3;
// Number of bytes
totSize += ( x * y * bpp ) >> 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();i++)
if (!texList[i].isValid())
return false;
return true;
}
// Set functions
void trpgTexTable::SetNumTextures(int no)
{
texList.resize(no);
}
int trpgTexTable::AddTexture(const trpgTexture &inTex)
{
texList.push_back(inTex);
return texList.size()-1;
}
int trpgTexTable::FindAddTexture(const trpgTexture &inTex)
{
for (int i=0;i<texList.size();i++)
if (texList[i] == inTex)
return i;
return AddTexture(inTex);
}
void trpgTexTable::SetTexture(int id,const trpgTexture &inTex)
{
if (id < 0 || (unsigned int)id >= texList.size())
return;
texList[id] = inTex;
}
// Copy operator
trpgTexTable &trpgTexTable::operator = (const trpgTexTable &in)
{
Reset();
for (int i=0;i<in.texList.size();i++)
AddTexture(in.texList[i]);
return *this;
}
// Write Texture table
bool trpgTexTable::Write(trpgWriteBuffer &buf)
{
int32 numTex;
if (!isValid())
return false;
buf.Begin(TRPGTEXTABLE2);
numTex = texList.size();
buf.Add(numTex);
for (unsigned int i=0;i<texList.size();i++)
texList[i].Write(buf);
buf.End();
return true;
}
/* ***********
Read Texture Table
***********
*/
// Get functions
bool trpgTexTable::GetNumTextures(int &no) const
{
no = texList.size();
if (!isValid()) return false;
return true;
}
bool trpgTexTable::GetTexture(int id,trpgTexture &ret) const
{
if (!isValid()) return false;
if (id < 0 || id >= 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<numTex;i++) {
buf.GetToken(texTok,len);
if (texTok != TRPGTEXTURE) throw 1;
buf.PushLimit(len);
trpgTexture &tex = texList[i];
bool status = tex.Read(buf);
buf.PopLimit();
if (!status) throw 1;
}
}
catch (...) {
return false;
}
return true;
}
/* **************
Local Material
**************
*/
trpgLocalMaterial::trpgLocalMaterial()
{
baseMat = -1;
sx = sy = ex = ey = destWidth = destHeight = 0;
addr.file = 0;
addr.offset = 0;
}
trpgLocalMaterial::~trpgLocalMaterial()
{
}
void trpgLocalMaterial::SetBaseMaterial(int32 subTable,int32 inMatID)
{
baseMatTable = subTable;
baseMat = inMatID;
}
void trpgLocalMaterial::Reset()
{
baseMat = -1;
sx = sy = ex = ey = destWidth = destHeight = 0;
// storageSize.resize(0);
addr.file = 0;
addr.offset = 0;
}
bool trpgLocalMaterial::GetBaseMaterial(int32 &subTable,int32 &matID) const
{
if (!isValid()) return false;
subTable = baseMatTable;
matID = baseMat;
return true;
}
void trpgLocalMaterial::SetSubImageInfo(const SubImageInfo &info)
{
sx = info.sx;
sy = info.sy;
ex = info.ex;
ey = info.ey;
destWidth = info.destWidth;
destHeight = info.destHeight;
}
bool trpgLocalMaterial::GetSubImageInfo(SubImageInfo &info) const
{
if (!isValid()) return false;
info.sx = sx;
info.sy = sy;
info.ex = ex;
info.ey = ey;
info.destWidth = destWidth;
info.destHeight = destHeight;
return true;
}
void trpgLocalMaterial::SetAddr(const trpgwAppAddress &inAddr)
{
addr = inAddr;
}
bool trpgLocalMaterial::GetAddr(trpgwAppAddress &inAddr) const
{
if (!isValid()) return false;
inAddr = addr;
return true;
}
/*bool trpgLocalMaterial::SetStorageSizes(int level,vector<int> *inSizes)
{
storageSize.resize(inSizes->size());
for (int i=0;i<inSizes->size();i++)
storageSize[i] = (*inSizes)[i];
return true;
}*/
/*bool trpgLocalMaterial::GetStorageSizes(const vector<int> *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();
}