www.pudn.com > Direct3D-3ds_loader-render.rar > 3ds.cpp
// 3ds.cpp: implementation of the C3ds class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "3ds.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
C3ds::C3ds()
{
error=1;
textures=NULL;
}
// filter : 0 - nearest | 1 - linear | 2 - bilinear, linear_mipmap_nearest | 3 - trilinear, linear_mipmap_linear
C3ds::C3ds(char *file_name, LPDIRECT3DDEVICE9 pDevice, int filter, int faceNormal, int shadowObject)
{
z3ds_Chunk ch;
char strMessage[255]={0};
textures = NULL;
isShadowObject = shadowObject;
isFaceNormal = faceNormal;
file = fopen( file_name, "rb");
if(!file)
{
sprintf(strMessage, "Unable to find the file: %s!", file_name);
MessageBox(NULL, strMessage, "Error", MB_OK);
error = 1;
return;
}
ReadChunk( &ch);
// Make sure this is a 3DS file
if (ch.ID != MAIN3DS)
{
sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", file_name);
MessageBox(NULL, strMessage, "Error", MB_OK);
error = 1;
return;
}
ReadNextChunk( &ch);
fclose( file);
if(isShadowObject)
{
for(int i=0; i0)textures = new texture[materials.size()];
// Go through all the materials
for(int i = 0; i < materials.size(); i++)
{
// Check to see if there is a file name to load in this material
if(strlen(materials[i].strFile) > 0)
{
// Use the name of the texture file to load the bitmap, with a texture ID (i).
char text[300];
char* pdest = strrchr( file_name, '/' ); // find '/'
if( pdest==NULL)strncpy(text, materials[i].strFile, 300);
else
{
strncpy( text, file_name, pdest-file_name+1);
text[pdest-file_name+1]=NULL;
strcat(text, materials[i].strFile);
}
if( !textures[i].load( text, pDevice, 1, filter))error = 1;
}
// Set the texture ID for this material
materials[i].texureId = i;
}
RotateX90(); //转换左右手坐标系
// Rescale(.2,.2,.2); //缩放模型
if (!CreateDXObjects(pDevice))
{
MessageBox(NULL,"Failed to create Direct3D objects form 3ds file", file_name, MB_OK|MB_ICONERROR);
}
}
C3ds::~C3ds()
{
}
void C3ds::ReadChunk(z3ds_Chunk *pChunk)
{
// This reads the chunk ID which is 2 bytes.
// The chunk ID is like OBJECT or MATERIAL. It tells what data is
// able to be read in within the chunks section.
pChunk->bytesRead = fread(&pChunk->ID, 1, 2, file);
// Then, we read the length of the chunk which is 4 bytes.
// This is how we know how much to read in, or read past.
pChunk->bytesRead += fread(&pChunk->length, 1, 4, file);
}
void C3ds::ReadNextChunk(z3ds_Chunk *old)
{
z3ds_Chunk ch;
while( old->bytesRead < old->length )
{
ReadChunk( &ch);
switch( ch.ID)
{
case VERSION: // This holds the version of the file
{
unsigned short version = 0; // This will hold the file version
ch.bytesRead += fread(&version, 1, ch.length - ch.bytesRead, file);
// If the file version is over 3, give a warning that there could be a problem
if (version > 0x03)
MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
}
break;
case EDIT3DS: // 0x3D3D
ReadEdit( &ch);
break;
case KEYF3DS: // 0xB000
// Read_KeyF( &ch);
fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
break;
default:
fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
}
old->bytesRead += ch.bytesRead;
}
}
void C3ds::ReadEdit(z3ds_Chunk *old)
{
z3ds_Chunk ch;
while( old->bytesRead < old->length )
{
ReadChunk( &ch);
switch( ch.ID)
{
case EDIT_MATERIAL: // 0xAFFF
ReadEditMaterial( &ch);
break;
case EDIT_OBJECT: // 0x4000
ReadEditObject( &ch);
break;
default:
fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
}
old->bytesRead += ch.bytesRead;
}
}
void C3ds::ReadEditMaterial(z3ds_Chunk *old)
{
unsigned char buf[100];
unsigned char color[3];
z3ds_Chunk ch, temp;;
z3ds_MaterialInfo mat;
memset( &mat, 0, sizeof(z3ds_MaterialInfo));
while( old->bytesRead < old->length )
{
ReadChunk( &ch);
switch( ch.ID)
{
case 0xA000: // This holds the material name
ch.bytesRead += fread( mat.strName, 1, ch.length-ch.bytesRead, file);
break;
case 0xA010: // This holds the ambient color of the object/material
{
ReadChunk( &temp);
fread( color, 1, temp.length-temp.bytesRead, file);
mat.ambient.set( (float)color[0]/255.f, (float)color[1]/255.f, (float)color[2]/255.f );
ch.bytesRead += temp.length;
}
break;
case 0xA020: // This holds the diffuse color of the object/material
{
ReadChunk( &temp);
fread( color, 1, temp.length-temp.bytesRead, file);
mat.diffuse.set( (float)color[0]/255.f, (float)color[1]/255.f, (float)color[2]/255.f );
ch.bytesRead += temp.length;
}
break;
case 0xA030: // This holds the specular color of the object/material
{
ReadChunk( &temp);
fread( color, 1, temp.length-temp.bytesRead, file);
mat.specular.set( (float)color[0]/255.f, (float)color[1]/255.f, (float)color[2]/255.f );
ch.bytesRead += temp.length;
}
break;
case 0xA040: // This holds the specular shininess of the object/material
{
ReadChunk( &temp);
fread( color, 1, temp.length-temp.bytesRead, file);
mat.shininess = (float)color[0];
ch.bytesRead += temp.length;
}
break;
case 0xA041: // This holds the specular level of the object/material
{
ReadChunk( &temp);
fread( color, 1, temp.length-temp.bytesRead, file);
mat.specular = mat.specular*0.01f*(256.f*color[1]+color[0]);
ch.bytesRead += temp.length;
}
break;
case 0xA084: // This holds the specular level of the object/material
{
ReadChunk( &temp);
fread( color, 1, temp.length-temp.bytesRead, file);
mat.emission = mat.diffuse*0.01f*color[0];
ch.bytesRead += temp.length;
}
break;
case 0xA200: // This is a header for a new material
ReadEditMaterialTexture( &ch, mat);
break;
case 0xA300: // This holds the file name of the texture
ch.bytesRead += fread( mat.strFile, 1, ch.length-ch.bytesRead, file);
break;
default:
fread( buf, 1, ch.length-ch.bytesRead, file);
// fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
}
old->bytesRead += ch.bytesRead;
}
materials.push_back(mat);
}
void C3ds::ReadEditMaterialTexture(z3ds_Chunk *old, z3ds_MaterialInfo &mat)
{
z3ds_Chunk ch;
char buf[100];
while( old->bytesRead < old->length )
{
ReadChunk( &ch);
switch( ch.ID)
{
case 0xA300: // This holds the file name of the texture
ch.bytesRead += fread( mat.strFile, 1, ch.length-ch.bytesRead, file);
break;
default:
fread( buf, 1, ch.length-ch.bytesRead, file);
// fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
}
old->bytesRead += ch.bytesRead;
}
}
void C3ds::ReadEditObject(z3ds_Chunk *old)
{
z3ds_Chunk ch;
z3ds_Object obj;// = {0};
memset( &obj, 0, sizeof(z3ds_Object));
old->bytesRead += GetString(obj.strName);
while( old->bytesRead < old->length )
{
ReadChunk( &ch);
switch( ch.ID)
{
case OBJ_TRIMESH: // 0x4100
ReadEditObjectTriMesh( &ch, obj);
break;
// case OBJ_LIGHT 0x4600
// case OBJ_CAMERA 0x4700
// case OBJ_UNKNWN01 0x4010
// case OBJ_UNKNWN02 0x4012 //---- Could be shadow
default:
fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
}
old->bytesRead += ch.bytesRead;
}
objects.push_back( obj);
}
void C3ds::ReadEditObjectTriMesh(z3ds_Chunk *old, z3ds_Object &obj)
{
z3ds_Chunk ch;
while( old->bytesRead < old->length )
{
ReadChunk( &ch);
switch( ch.ID)
{
case TRI_VERTEXL: // 0x4110
ReadEditObjectTriMeshVertex( &ch, obj);
break;
// case TRI_FACEL2: // 0x4111
case TRI_FACEL1: // 0x4120
ReadEditObjectTriMeshFace( &ch, obj);
break;
case 0x4130: // Face Material Chunk
ReadEditObjectTriMeshMaterial( &ch, obj);
break;
case 0x4140: // Mapping coordinates
ReadEditObjectTriMeshUV( &ch, obj);
break;
case TRI_SMOOTH: // 0x4150
case TRI_LOCAL: // 0x4160
case TRI_VISIBLE: // 0x4165
default:
fseek( file, ch.length-ch.bytesRead, SEEK_CUR);
ch.bytesRead = ch.length;
}
old->bytesRead += ch.bytesRead;
}
}
void C3ds::ReadEditObjectTriMeshVertex(z3ds_Chunk *old, z3ds_Object &obj)
{
old->bytesRead += fread( &obj.numOfVerts, 1, 2, file);
obj.pVerts = new vec[obj.numOfVerts];
if(obj.pVerts==NULL){ error=1; return;}
old->bytesRead += fread( obj.pVerts, 1, sizeof(vec)*obj.numOfVerts, file);
}
void C3ds::ReadEditObjectTriMeshFace(z3ds_Chunk *old, z3ds_Object &obj)
{
old->bytesRead += fread( &obj.numOfFaces, 1, 2, file);
obj.pFaces = new z3ds_Face[obj.numOfFaces];
if(obj.pFaces==NULL){ error=1; return;}
unsigned short *temp;
temp = new unsigned short[8*obj.numOfFaces];
if(temp==NULL){ error=1; return;}
old->bytesRead += fread( temp, 1, 2*4*obj.numOfFaces, file);
for(unsigned short i=0; ibytesRead += GetString( strMaterial );
for(int i=0; i 0) obj.bHasTexture = true;
break;
}
else
obj.materialID = -1; // Set the ID to -1 to show there is no material for this object
}
fseek( file, old->length-old->bytesRead, SEEK_CUR);
old->bytesRead = old->length;
}
void C3ds::ReadEditObjectTriMeshUV(z3ds_Chunk *old, z3ds_Object &obj)
{
old->bytesRead += fread( &obj.numTexVertex, 1, 2, file);
obj.pTexVerts = new vec2[ obj.numTexVertex];
old->bytesRead += fread( obj.pTexVerts, 1, old->length-old->bytesRead, file);
}
void C3ds::ComputeNormals()
{
for(int i=0; iobjects.size(); i++)
{
vec* faceN;
z3ds_Object *obj;
obj = &objects[i];
faceN = new vec[obj->numOfFaces];
if(faceN==NULL)return;
obj->pNormals = new vec[obj->numOfVerts];
if(obj->pNormals==NULL)return;
for(int j=0; jnumOfFaces; j++)
{
vec a,b;
a = obj->pVerts[obj->pFaces[j].vertIndex[1]] - obj->pVerts[obj->pFaces[j].vertIndex[0]];
b = obj->pVerts[obj->pFaces[j].vertIndex[2]] - obj->pVerts[obj->pFaces[j].vertIndex[0]];
faceN[j] = CROSS( a, b);
if(isShadowObject)
{
obj->pFacesShadow[j].normal=faceN[j];
obj->pFacesShadow[j].normal.Normalize();
obj->pFacesShadow[j].delta = -DOT3( obj->pFacesShadow[j].normal, obj->pVerts[obj->pFaces[j].vertIndex[0]]);
}
// if(isFaceNormal)
// faceN[j].Normalize();
}
if(isFaceNormal)
{
obj->pNormals = faceN;
faceN = NULL;
continue;
}
int count;
for(j=0; jnumOfVerts; j++) // Go through all of the vertices
{
count=0;
obj->pNormals[j].clear();
for(int k=0; knumOfFaces; k++) // Go through all of the triangles
{ // Check if the vertex is shared by another face
if( obj->pFaces[k].vertIndex[0]==j ||
obj->pFaces[k].vertIndex[1]==j ||
obj->pFaces[k].vertIndex[2]==j )
{
obj->pNormals[j] += faceN[k];
count++;
}
}
obj->pNormals[j] *= 1.f/(float)count;
obj->pNormals[j].Normalize();
}
if(faceN!=NULL)delete [] faceN;
}
}
void C3ds::SetConnectivity()
{
for( int iobj=0; iobjnumOfFaces)-1; ifaceA++)
{
for(int edgeA=0; edgeA<3; edgeA++)
{
if(obj->pFacesShadow[ifaceA].neighbourIndices[edgeA]>-1)continue;
for(int ifaceB=ifaceA+1; ifaceBnumOfFaces; ifaceB++)
{
for(int edgeB=0; edgeB<3; edgeB++)
{
unsigned short a1,a2,b1,b2;
a1 = obj->pFaces[ifaceA].vertIndex[edgeA];
a2 = obj->pFaces[ifaceA].vertIndex[(edgeA+1)%3];
b1 = obj->pFaces[ifaceB].vertIndex[edgeB];
b2 = obj->pFaces[ifaceB].vertIndex[(edgeB+1)%3];
if( (a1==b1 && a2==b2) || (a1==b2 && a2==b1) )
{
obj->pFacesShadow[ifaceA].neighbourIndices[edgeA]=ifaceB;
obj->pFacesShadow[ifaceB].neighbourIndices[edgeB]=ifaceA;
}
}
}
}
}
}
}
int C3ds::GetString(char *pBuffer)
{
int index = 0;
fread(pBuffer, 1, 1, file);
// Loop until we get NULL
while (*(pBuffer + index++) != 0)
fread(pBuffer + index, 1, 1, file); // Read in a character at a time until we hit NULL.
return strlen(pBuffer) + 1; // Return the string length, which is how many bytes we read in (including the NULL)
}
//////////////////////////////////////////////////////////////////////////
//左手、右手坐标系转换函数
//void C3ds::RotateX90()
//////////////////////////////////////////////////////////////////////////
void C3ds::RotateX90()
{
for(int i=0; ipVerts!=NULL)
{
for(int j=0; jnumOfVerts; j++)
{
float z = -obj->pVerts[j].y;
obj->pVerts[j].y = obj->pVerts[j].z;
obj->pVerts[j].z = z;
}
}
if(obj->pNormals!=NULL)
{
for(int j=0; jnumOfVerts; j++)
{
float z = -obj->pNormals[j].y;
obj->pNormals[j].y = obj->pNormals[j].z;
obj->pNormals[j].z = z;
}
}
if(obj->pFacesShadow!=NULL)
{
for(int j=0; jnumOfFaces; j++)
{
float z = -obj->pFacesShadow[j].normal.y;
obj->pFacesShadow[j].normal.y = obj->pFacesShadow[j].normal.z;
obj->pFacesShadow[j].normal.z = z;
}
}
}
}
void C3ds::Rescale(float scaleX,float scaleY,float scaleZ)
{
for(int i=0; ipVerts!=NULL)
{
for(int j=0; jnumOfVerts; j++)
{
obj->pVerts[j].x*=scaleX;
obj->pVerts[j].y*=scaleY;
obj->pVerts[j].z*=scaleZ;
}
}
}
}
int C3ds::CreateDXObjects(LPDIRECT3DDEVICE9 pDevice)
{
z3ds_DXobj dxobj;
// Since we know how many objects our model has, go through each of them.
for(int i = 0; i < objects.size(); i++)
{
z3ds_Object *obj = &objects[i];
// zistime ci mame nacitane texturove koordinacie
if(obj->bHasTexture && obj->pTexVerts!=NULL)dxobj.have_texture_coord_0 = 1;
else dxobj.have_texture_coord_0 = 0;
dxobj.materialID = obj->materialID;
dxobj.count = obj->numOfFaces;
dxobj.pvb = NULL;
if( FAILED(pDevice->CreateVertexBuffer( dxobj.count*3*sizeof(z3ds_CUSTOMVERTEX),D3DUSAGE_WRITEONLY,
D3DFVF_CUSTOMVERTEX_3ds,D3DPOOL_DEFAULT, &dxobj.pvb, NULL) ) )return 0;
void* p;
if( FAILED( (dxobj.pvb)->Lock( 0, dxobj.count*3*sizeof(z3ds_CUSTOMVERTEX), &p, 0 ) ) )return 0;
int i_vec=0;
// Go through all of the faces (polygons) of the object and draw them
for(int j = 0; j < obj->numOfFaces; j++)
{
// Go through each corner of the triangle and draw it.
for(int whichVertex = 0; whichVertex < 3; whichVertex++)
{
// Get the index for each point of the face
int index = obj->pFaces[j].vertIndex[whichVertex];
// normal
((z3ds_CUSTOMVERTEX*)p)[i_vec].n = obj->pNormals[ index ];
// texture coordinates
if(dxobj.have_texture_coord_0)((z3ds_CUSTOMVERTEX*)p)[i_vec].t0 = obj->pTexVerts[ index ];
((z3ds_CUSTOMVERTEX*)p)[i_vec].t0.y = 1.f -((z3ds_CUSTOMVERTEX*)p)[i_vec].t0.y;
// vertex
((z3ds_CUSTOMVERTEX*)p)[i_vec++].v = obj->pVerts[ index ];
}
}
dxobj.pvb->Unlock();
dxobjs.push_back(dxobj);
dxobj.pvb = NULL;
}
DeleteObjects();
return 1;
}
void C3ds::DeleteObjects()
{
for(int i=0; iobjects.size(); i++)
{
if(objects[i].pFaces!=NULL) delete [] objects[i].pFaces;
if(objects[i].pFacesShadow!=NULL) delete [] objects[i].pFacesShadow;
if(objects[i].pNormals!=NULL) delete [] objects[i].pNormals;
if(objects[i].pTexVerts!=NULL) delete [] objects[i].pTexVerts;
if(objects[i].pVerts!=NULL) delete [] objects[i].pVerts;
}
objects.clear();
}
// texturing:
// 0-vypnuty // nenastavuje sa akt. textura, glDisable(GL_TEXTURE_2D);, nezadavaju sa Tex Coord
// 1-zapnuty // nastavuje sa akt. textura, glEnable(GL_TEXTURE_2D);, zadavaju sa Tex Coord
// 2-environmental mapping // nenastavuje sa akt. textura, nezadavaju sa Tex Coord
// 3-bez zmeny // nenastavuje sa akt. textura, zadavaju sa Tex Coord
// material
// 0-ziadne zmeny ohladne nastavenia farieb
// 1-farby su nastavene podla materialu, ak je zapnute osvetlenie nastavuje sa material
void C3ds::Render(LPDIRECT3DDEVICE9 pd, int texturing, int material, DWORD hVertexShader)
{
for(int i=0; i=0 && dxobjs[i].materialIDdiffuse.x;
mtrl.Diffuse.g = mtrl.Ambient.g = mat->diffuse.y;
mtrl.Diffuse.b = mtrl.Ambient.b = mat->diffuse.z;
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
pd->SetMaterial( &mtrl );
}
if(dxobjs[i].have_texture_coord_0 && texturing)
{
textures[mat->texureId].SetTexture(pd);
pd->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
pd->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_CURRENT );
pd->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
}
else
pd->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
}
pd->SetStreamSource( 0, dxobjs[i].pvb, 0, sizeof(z3ds_CUSTOMVERTEX) );
// pd->SetVertexShader( hVertexShader );
for(int j=0; jDrawPrimitive( D3DPT_TRIANGLELIST, 3*j, 21000 );
pd->DrawPrimitive( D3DPT_TRIANGLELIST, 3*j, dxobjs[i].count-j );
pd->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
}
// if(material)
// {
// D3DMATERIAL9 mtrl;
// ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
// mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
// mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
// mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
// mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
// pd->SetMaterial( &mtrl );
// }
}