Module: dmaterial.cpp

class DMaterial
.h

constructorDMaterial()

: flags(0)

constructorDMaterial(DMaterial *master)

Clone the material and submaterials
Textures are REFERENCED, but submaterials are COPIED

destructor~DMaterial()
SetDiffuseColorvoid SetDiffuseColor(float r,float g,float b,float a)
Enablevoid Enable(int newFlags)
Disablevoid Disable(int _flags)
AddBitMapbool AddBitMap(cstring fname,int where)

Adds the bitmap to the material

GetTextureDTexture *GetTexture(int n)
SetTexturevoid SetTexture(int n,DTexture *_tex)
PrepareToPaintvoid PrepareToPaint()

Use this material for painting OpenGL polygons

ExportDOFbool ExportDOF(QFile *f)

Write the material into a file
ASSUMES: dfloat==float (otherwise PC ordering will fail on MIPS)

ImportDOFbool 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;
}