class DMaterial | .h |
constructor | DMaterial() : flags(0) |
constructor | DMaterial(DMaterial *master) Clone the material and submaterials Textures are REFERENCED, but submaterials are COPIED |
destructor | ~DMaterial() |
SetDiffuseColor | void SetDiffuseColor(float r,float g,float b,float a) |
Enable | void Enable(int newFlags) |
Disable | void Disable(int _flags) |
AddBitMap | bool AddBitMap(cstring fname,int where) Adds the bitmap to the material |
GetTexture | DTexture *GetTexture(int n) |
SetTexture | void SetTexture(int n,DTexture *_tex) |
PrepareToPaint | void PrepareToPaint() Use this material for painting OpenGL polygons |
ExportDOF | bool ExportDOF(QFile *f) Write the material into a file ASSUMES: dfloat==float (otherwise PC ordering will fail on MIPS) |
ImportDOF | bool ImportDOF(QFile *f,DGeode *geode) Read the material from a file |
/*
* DMaterial - materials
* 15-03-00: Created!
* 16-11-00: Import/export for DOF files
* 27-12-00: MAT0 format changed considerably; split into chunks.
* 06-01-01: Optimization flags to reduce OpenGL state changes.
* BUGS:
* - Upon dtor, doesn't destroy tex's (mind the pool!)
* (C) MarketGraph/RVG
*/
#include <d3/d3.h>
#pragma hdrstop
#ifdef WIN32
#include <winsock.h>
#else
#include <netinet/in.h>
#endif
#include <d3/material.h>
#include <d3/global.h>
#include <d3/geode.h>
#include <qlib/app.h>
#include <qlib/debug.h>
DEBUG_ENABLE
#undef DBG_CLASS
#define DBG_CLASS "DMaterial"
DMaterial::DMaterial()
: flags(0)
{
DBG_C("ctor")
int i;
// Defaults (matches OpenGL defaults)
for(i=0;i<4;i++)
{
ambient[i]=0.2;
diffuse[i]=0.8;
specular[i]=0;
emission[i]=0;
}
diffuse[3]=1;
emission[3]=1;
specular[3]=1;
shininess=0;
transparency=0;
blendMode=BLEND_OFF;
textures=0;
uvwUoffset=uvwVoffset=0.0;
uvwUtiling=uvwVtiling=1.0;
uvwBlur=uvwBlurOffset=0;
uvwAngle=0;
submaterials=0;
}
DMaterial::DMaterial(DMaterial *master)
// Clone the material and submaterials
// Textures are REFERENCED, but submaterials are COPIED
{
DBG_C("copy-ctor")
int i;
name=master->name;
className=master->className;
flags=master->flags;
for(i=0;i<4;i++)
{
ambient[i]=master->ambient[i];
diffuse[i]=master->diffuse[i];
specular[i]=master->specular[i];
emission[i]=master->emission[i];
}
shininess=master->shininess;
transparency=master->transparency;
blendMode=master->blendMode;
// Take over textures by reference
textures=master->textures;
for(i=0;i<textures;i++)
tex[i]=master->tex[i];
// Take over materials by copy
submaterials=master->submaterials;
for(i=0;i<submaterials;i++)
submaterial[i]=new DMaterial(master->submaterial[i]);
}
DMaterial::~DMaterial()
{
DBG_C("dtor")
int i;
for(i=0;i<submaterials;i++)
if(submaterial[i])
delete submaterial[i];
// Delete textures NOT from the pool
//...
}
/*************
* Attributes *
*************/
void DMaterial::SetDiffuseColor(float r,float g,float b,float a)
{
diffuse[0]=r;
diffuse[1]=g;
diffuse[2]=b;
diffuse[3]=a;
}
void DMaterial::Enable(int newFlags)
{
flags|=newFlags;
}
void DMaterial::Disable(int _flags)
{
flags&=~_flags;
}
/******************
* Bitmap textures *
******************/
bool DMaterial::AddBitMap(cstring fname,int where)
// Adds the bitmap to the material
{
DBG_C("AddBitMap")
DTexture *t;
// Max reached?
if(textures==MAX_BITMAP_TEXTURE)
return FALSE;
if(dglobal.flags&DGlobal::USE_TEXTURE_POOL)
{
// Check pool first, to save bitmap loading (for Racer tracks
// for example)
t=dglobal.texturePool->Find(QFileBase(fname));
//qdbg("Check pool: '%s' = tex(%p)\n",QFileBase(fname),t);
if(t)
{
tex[textures]=t;
// Default is to repeat textures
tex[textures]->SetWrap(DTexture::REPEAT,DTexture::REPEAT);
textures++;
return TRUE;
}
}
// Load the image
QImage *img=0;
QBitMap *bm=0;
if(!QFileExists(fname))
{
qwarn("Can't load material texture image '%s'\n",fname);
// Supply our own
supply_own:
QColor c(255,0,0);
bm=new QBitMap(32,1,1);
bm->SetColorAt(0,0,&c);
} else
{
// Try and load image
img=new QImage(fname);
if(!img->IsRead())
{
// Supply easy to recognize image
goto supply_own;
}
// For .BMP images, key out purple (Racer)
img->KeyPurple();
}
// Create texture from image
if(bm)tex[textures]=new DBitMapTexture(bm);
else tex[textures]=new DBitMapTexture(img);
// Record filename to allow for saving DMaterials
// only store file part, NOT the path
tex[textures]->SetName(QFileBase(fname));
// Default is to repeat textures
tex[textures]->SetWrap(DTexture::REPEAT,DTexture::REPEAT);
if(img)delete img;
if(bm)delete bm;
if(dglobal.flags&DGlobal::USE_TEXTURE_POOL)
{
// Add image to the pool
dglobal.texturePool->Add(QFileBase(fname),tex[textures]);
}
// Another texture
textures++;
return TRUE;
}
DTexture *DMaterial::GetTexture(int n)
{
return tex[n];
}
void DMaterial::SetTexture(int n,DTexture *_tex)
{
DBG_C("SetTexture")
DBG_ARG_I(n)
DBG_ARG_P(_tex)
tex[n]=_tex;
}
void DMaterial::PrepareToPaint()
// Use this material for painting OpenGL polygons
{
DBG_C("PrepareToPaint")
// Texturing
if(textures)
{
// Diffuse image texture map
//qdbg("DMat:PTP; tex %p select (this=%p)\n",tex[0],this);
if(tex[0])tex[0]->Select();
if(!(flags&NO_TEXTURE_ENABLING))
glEnable(GL_TEXTURE_2D);
//glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
//glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
} else
{
if(!(flags&NO_TEXTURE_ENABLING))
glDisable(GL_TEXTURE_2D);
}
// Set lighting/material properties
if(!(flags&NO_MATERIAL_PROPERTIES))
{
#ifdef OBS
qdbg("PTP; ambient=%f,%f,%f\n",ambient[0],ambient[1],ambient[2]);
qdbg("PTP; diffuse=%f,%f,%f\n",diffuse[0],diffuse[1],diffuse[2]);
qdbg("PTP; specular=%f,%f,%f\n",specular[0],specular[1],specular[2]);
qdbg("PTP; emission=%f,%f,%f\n",emission[0],emission[1],emission[2]);
qdbg("PTP; shininess=%f\n",shininess);
#endif
glMaterialfv(GL_FRONT,GL_AMBIENT,ambient);
glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuse);
glMaterialfv(GL_FRONT,GL_SPECULAR,specular);
#ifdef OBS
{float v[4]={ 1,1,1,1 };
glMaterialfv(GL_FRONT,GL_SPECULAR,v);
}
#endif
glMaterialfv(GL_FRONT,GL_EMISSION,emission);
glMaterialf(GL_FRONT,GL_SHININESS,shininess);
//glMaterialf(GL_FRONT,GL_SHININESS,128);
//glDisable(GL_TEXTURE_2D);
//glDisable(GL_BLEND);
}
// Blending
if(!(flags&NO_BLEND_PROPERTIES))
{
if(blendMode==BLEND_OFF)
{ glDisable(GL_BLEND);
} else if(blendMode==BLEND_SRC_ALPHA)
{ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
} else
{ qwarn("DMaterial:PrepareToPaint; unsupported blend mode %d",blendMode);
glDisable(GL_BLEND);
}
}
//glDisable(GL_BLEND);
}
/******
* I/O *
******/
bool DMaterial::ExportDOF(QFile *f)
// Write the material into a file
// ASSUMES: dfloat==float (otherwise PC ordering will fail on MIPS)
{
DBG_C("ExportDOF")
int i,len,off,off2,n;
f->Write("MAT0",4);
off=f->Tell();
len=0; n=QHost2PC_L(len);
f->Write(&n,sizeof(n));
// Header
{
int pos,posNext;
f->Write("MHDR",4);
pos=f->Tell();
len=QHost2PC_L(0); f->Write(&len,sizeof(len));
f->Write(name);
f->Write(className);
posNext=f->Tell();
f->Seek(pos); len=QHost2PC_L(posNext-pos-4); f->Write(&len,sizeof(len));
f->Seek(posNext);
}
// Colors
f->Write("MCOL",4);
len=(4*4+1)*sizeof(dfloat); n=QHost2PC_L(len); f->Write(&n,sizeof(n));
QHost2PC_FA(ambient,4);
QHost2PC_FA(diffuse,4);
QHost2PC_FA(specular,4);
QHost2PC_FA(emission,4);
QHost2PC_FA(&shininess,1);
f->Write(ambient,sizeof(ambient));
f->Write(diffuse,sizeof(diffuse));
f->Write(specular,sizeof(specular));
f->Write(emission,sizeof(emission));
f->Write(&shininess,sizeof(shininess));
QPC2Host_FA(ambient,4);
QPC2Host_FA(diffuse,4);
QPC2Host_FA(specular,4);
QPC2Host_FA(emission,4);
QPC2Host_FA(&shininess,1);
// UVW mapping
f->Write("MUVW",4);
len=7*sizeof(dfloat); n=QHost2PC_L(len); f->Write(&n,sizeof(n));
QHost2PC_FA(&uvwUoffset,1);
QHost2PC_FA(&uvwVoffset,1);
QHost2PC_FA(&uvwUtiling,1);
QHost2PC_FA(&uvwVtiling,1);
QHost2PC_FA(&uvwAngle,1);
QHost2PC_FA(&uvwBlur,1);
QHost2PC_FA(&uvwBlurOffset,1);
f->Write(&uvwUoffset,sizeof(uvwUoffset));
f->Write(&uvwVoffset,sizeof(uvwVoffset));
f->Write(&uvwUtiling,sizeof(uvwUtiling));
f->Write(&uvwVtiling,sizeof(uvwVtiling));
f->Write(&uvwAngle,sizeof(uvwAngle));
f->Write(&uvwBlur,sizeof(uvwBlur));
f->Write(&uvwBlurOffset,sizeof(uvwBlurOffset));
QPC2Host_FA(&uvwUoffset,1);
QPC2Host_FA(&uvwVoffset,1);
QPC2Host_FA(&uvwUtiling,1);
QPC2Host_FA(&uvwVtiling,1);
QPC2Host_FA(&uvwAngle,1);
QPC2Host_FA(&uvwBlur,1);
QPC2Host_FA(&uvwBlurOffset,1);
// Misc
f->Write("MTRA",4);
len=QHost2PC_L(1*sizeof(dfloat)+1*sizeof(long));
f->Write(&len,sizeof(len));
QHost2PC_FA(&transparency,1);
QHost2PC_LA((unsigned long*)&blendMode,1);
f->Write(&transparency,sizeof(transparency));
f->Write(&blendMode,sizeof(blendMode));
QPC2Host_FA(&transparency,1);
QPC2Host_LA((unsigned long*)&blendMode,1);
// Textures
{
int pos,posNext;
f->Write("MTEX",4);
pos=f->Tell();
len=QHost2PC_L(0); f->Write(&len,sizeof(len));
n=QHost2PC_L(textures); f->Write(&n,sizeof(n));
// Names of textures
for(i=0;i<textures;i++)
{
f->Write(*tex[i]->GetNameQ());
}
posNext=f->Tell();
f->Seek(pos); len=QHost2PC_L(posNext-pos-4); f->Write(&len,sizeof(len));
f->Seek(posNext);
}
// Submaterials
{
int pos,posNext;
f->Write("MSUB",4);
pos=f->Tell();
len=QHost2PC_L(0); f->Write(&len,sizeof(len));
n=QHost2PC_L(submaterials); f->Write(&n,sizeof(n));
for(i=0;i<submaterials;i++)
{
submaterial[i]->ExportDOF(f);
}
posNext=f->Tell();
f->Seek(pos); len=QHost2PC_L(posNext-pos-4); f->Write(&len,sizeof(len));
f->Seek(posNext);
}
f->Write("MEND",4);
// Record size of material chunk
off2=f->Tell();
f->Seek(off);
len=QHost2PC_L(off2-off-4); f->Write(&len,sizeof(len));
f->Seek(off2);
return TRUE;
}
bool DMaterial::ImportDOF(QFile *f,DGeode *geode)
// Read the material from a file
{
DBG_C("ImportDOF")
int i,len,n;
char buf[8];
f->Read(buf,4); buf[4]=0; // MAT0
//qdbg("DMat:ImportDOF(); MAT0='%s'?\n",buf);
f->Read(&len,sizeof(len)); len=QPC2Host_L(len);
// Read chunks
while(1)
{
f->Read(buf,4); buf[4]=0;
if(f->IsEOF())break;
if(!strcmp(buf,"MEND"))break;
// All non-MEND chunks have a length
f->Read(&n,sizeof(n)); len=QPC2Host_L(n);
if(!strcmp(buf,"MCOL"))
{ // Color
f->Read(ambient,sizeof(ambient));
f->Read(diffuse,sizeof(diffuse));
f->Read(specular,sizeof(specular));
f->Read(emission,sizeof(emission));
f->Read(&shininess,sizeof(shininess));
QPC2Host_FA(ambient,4);
QPC2Host_FA(diffuse,4);
QPC2Host_FA(specular,4);
QPC2Host_FA(emission,4);
QPC2Host_FA(&shininess,1);
} else if(!strcmp(buf,"MHDR"))
{
f->Read(name);
f->Read(className);
//qdbg(" name='%s', className='%s'\n",(cstring)name,(cstring)className);
} else if(!strcmp(buf,"MTRA"))
{
f->Read(&transparency,sizeof(transparency));
f->Read(&blendMode,sizeof(blendMode));
QPC2Host_FA(&transparency,1);
QPC2Host_LA((unsigned long*)&blendMode,1);
} else if(!strcmp(buf,"MUVW"))
{
f->Read(&uvwUoffset,sizeof(uvwUoffset));
f->Read(&uvwVoffset,sizeof(uvwVoffset));
f->Read(&uvwUtiling,sizeof(uvwUtiling));
f->Read(&uvwVtiling,sizeof(uvwVtiling));
f->Read(&uvwAngle,sizeof(uvwAngle));
f->Read(&uvwBlur,sizeof(uvwBlur));
f->Read(&uvwBlurOffset,sizeof(uvwBlurOffset));
QPC2Host_FA(&uvwUoffset,1);
QPC2Host_FA(&uvwVoffset,1);
QPC2Host_FA(&uvwUtiling,1);
QPC2Host_FA(&uvwVtiling,1);
QPC2Host_FA(&uvwAngle,1);
QPC2Host_FA(&uvwBlur,1);
QPC2Host_FA(&uvwBlurOffset,1);
} else if(!strcmp(buf,"MTEX"))
{
// Textures
f->Read(&n,sizeof(n));
n=QPC2Host_L(n);
// Names of textures
textures=0;
for(i=0;i<n;i++)
{
qstring s;
f->Read(s);
//qdbg("DMat:ImportDOF; texture '%s'\n",(cstring)s);
if(geode)
{
// Use the geode's path
char fname[256];
geode->FindMapFile(s,fname);
#ifdef OBS
cstring path;
path=(cstring)geode->GetMapPath();
sprintf(fname,"%s/%s",path[0]?path:"images",(cstring)s);
#endif
AddBitMap(fname);
} else
{ // Relative to images/ (default)
char fname[256];
sprintf(fname,"images/%s",(cstring)s);
AddBitMap(fname);
}
}
} else if(!strcmp(buf,"MSUB"))
{
// Submaterials
f->Read(&submaterials,sizeof(submaterials));
submaterials=QPC2Host_L(submaterials);
//qdbg(" submats=%d\n",submaterials);
for(i=0;i<submaterials;i++)
{
submaterial[i]=new DMaterial();
submaterial[i]->ImportDOF(f,geode);
}
} else
{
qdbg("Unknown chunk '%s'\n",buf);
f->Seek(len,QFile::S_CUR);
}
}
return TRUE;
}