📄 3dsloader.cpp
字号:
//----------------------------------------------------
CGL3DSLoader::MESHDATA* CGL3DSLoader::Read3DSObjectChunk( fstream& file, DWORD dwLength )
{
int iCount;
WORD wID;
DWORD dwBlockLength;
DWORD dwReadCount = 0;
DWORD dwNextChunk = (DWORD)(file.tellg()) + dwLength;
CGL3DSLoader::MESHDATA*
lpRet = NULL;
iCount = dwLength;
//物体块以Null terminate string开始
char name[200];
int i=0;
memset( name, 1,sizeof(char)*200 );
file.read( name, 1 );
iCount --;
for( ; name[i]!=0; i++ )
{
file.read( name+i+1, 1 );
iCount --;
}
//接着读取块名
file.read( (char*)&wID, sizeof(wID) );
iCount -= sizeof(wID);
file.read( (char*)&dwBlockLength, sizeof( dwBlockLength ) );
iCount -= sizeof(dwBlockLength);
if( file.eof() || iCount <=0 )
{
file.seekg( dwNextChunk - dwLength, ios::beg );
return NULL;
}
//如果是网格块
if( wID == CHUNK_TRIMESH )
{
lpRet = new MESHDATA;
while( iCount >0 )
{
//读取块头
file.read( (char*)&wID, sizeof( wID ) );
file.read( (char*)&dwBlockLength, sizeof( dwBlockLength ) );
switch( wID )
{
case CHUNK_VERTLIST: //顶点列表
{
WORD wNum;
file.read( (char*)&wNum, sizeof(wNum) );
lpRet->dwVertexNum = wNum;
lpRet->lpVertex = new GLVERTEX3[lpRet->dwVertexNum];
float* pfVertex = new float[(lpRet->dwVertexNum)*3];
//读取所有顶点
file.read( (char*)pfVertex, sizeof(float)*3*(lpRet->dwVertexNum) );
for( int i=0; i<(int)(lpRet->dwVertexNum); i++ )
//转换3DS视图坐标系到OpenGL坐标系,可以视情况调整。
lpRet->lpVertex[i] = GLVECTOR3( -pfVertex[i*3], pfVertex[i*3+2], pfVertex[i*3+1] );
//释放中间变量
delete []pfVertex;
}
break;
case CHUNK_MAPLIST:
{
//纹理坐标
WORD wNum;
//数目(==顶点数目)
file.read( (char*)&wNum, sizeof(wNum) );
lpRet->lpTexcoord = new GLTEXCOORD[wNum];
//读取所有顶点的纹理坐标
file.read( (char*)(lpRet->lpTexcoord), sizeof(float)*2*(lpRet->dwVertexNum) );
lpRet->dwFlags |= MESH_USINGTEXCOORD;
}
break;
case CHUNK_FACELIST:
//读取面信息
Read3DSFaceListChunk( file, dwBlockLength - sizeof(wID) - sizeof(dwBlockLength), lpRet );
break;
case CHUNK_TRMATRIX:
//取得物体的中心坐标(目前没有用处)
//3(3*float)个表示X,Y,Z轴的向量
file.seekg( sizeof(float)*3*3, ios::cur );
float a[3];
file.read( (char*)a, sizeof(float)*3 );
lpRet->vtxCenter = GLVERTEX3( -a[0], a[2], a[1] );
break;
default:
file.seekg( dwBlockLength - sizeof(wID) - sizeof( dwBlockLength ), ios::cur );
break;
}
iCount -= dwBlockLength;
}
}
file.seekg( dwNextChunk, ios::beg );
return lpRet;
}
//----------------------------------------------------
//Function Name : Read3DSColorChunk
//Desc : 从当前位置开始读取颜色块
//Input :file : 用于定位的fstream文件对象的引用
// : wID :
//Output : 成功返回true,否则返回false
// : r : 红色分量(float)
// : g : 绿………
// : b : 蓝………
//Notice : 当前文件指针必须指向颜色的数据部分(不包括块头)
// file的完备性由使用者保证
//----------------------------------------------------
bool CGL3DSLoader::Read3DSColorChunk( fstream& file, WORD wID, float& r, float& g, float& b )
{
if( wID == CHUNK_RGB1 ) //3 floats of rgb
{
file.read( (char*)&r, sizeof(r) );
file.read( (char*)&g, sizeof(r) );
file.read( (char*)&b, sizeof(r) );
}
else if( wID == CHUNK_RGB2 ) //3 bytes of rgb
{
BYTE rb, gb, bb;
file.read( (char*)&rb, sizeof(rb) );
file.read( (char*)&gb, sizeof(gb) );
file.read( (char*)&bb, sizeof(bb) );
r = (float)rb/255.0f;
g = (float)gb/255.0f;
b = (float)bb/255.0f;
}
else
return false;
return true;
}
//----------------------------------------------------
//Function Name : Read3DSTextureChunk
//Desc : 从当前位置开始读取纹理块
//Input :file : 用于定位的fstream文件对象的引用
// : dwLength : 块大小
// : lpData : 用于填充数据的对象
//Output : 成功返回true,否则返回false
//Notice : 当前文件指针必须指向纹理块的数据部分(不包括块头)
// file,lpData的完备性由使用者保证
//----------------------------------------------------
bool CGL3DSLoader::Read3DSTextureChunk( fstream& file, DWORD dwLength, CGL3DSLoader::MATERIALDATA* lpData )
{
int iCount = dwLength;
WORD wID;
DWORD dwBlockLength;
DWORD dwNextBlock = (DWORD)(file.tellg()) + dwLength;
while( iCount > 0 )
{
//块头
file.read( (char*)&wID, sizeof(wID) );
file.read( (char*)&dwBlockLength, sizeof(dwBlockLength) );
switch( wID )
{
case CHUNK_MAPFILENAME: //纹理文件名
{
char name[200];
int i=0;
memset( name, 1,sizeof(char)*200 );
file.read( name, 1 );
for( ; name[i]!=0; i++ )
file.read( name+i+1, 1 );
lpData->strTexture = name;
}
break;
case CHUNK_OFFSETU: //以下为局部纹理映射参数
file.read( (char*)&(lpData->fOffsetU), sizeof(float) );
break;
case CHUNK_OFFSETV:
file.read( (char*)&(lpData->fOffsetV), sizeof(float) );
lpData->fOffsetV = -(lpData->fOffsetV);
break;
case CHUNK_TILINGU:
file.read( (char*)&(lpData->fTilingU), sizeof(float) );
break;
case CHUNK_TILINGV:
file.read( (char*)&(lpData->fTilingV), sizeof(float) );
break;
case CHUNK_ROTATEW:
file.read( (char*)&(lpData->fRotationW), sizeof(float) );
default:
file.seekg( dwBlockLength - sizeof(wID) - sizeof(dwBlockLength), ios::cur );
}
iCount -= dwBlockLength;
}
file.seekg( dwNextBlock, ios::beg );
return true;
}
//----------------------------------------------------
//Function Name : Read3DSMaterialChunk
//Desc : 从当前位置开始读取材质
//Input :file : 用于定位的fstream文件对象的引用
// : dwLength : 块大小
//Output : 成功返回描述材质的MATERIALDATA结构指针,否则返回NULL
//Notice : 当前文件指针必须指向材质块的数据部分(不包括块头)
// file,lpData的完备性由使用者保证,返回指针由使用者释放
//----------------------------------------------------
CGL3DSLoader::MATERIALDATA* CGL3DSLoader::Read3DSMaterialChunk( fstream& file, DWORD dwLength )
{
int iCount;
WORD wID;
DWORD dwBlockLength;
DWORD dwNextBlock = (DWORD)(file.tellg()) + dwLength;
MATERIALDATA*
lpRet = NULL;
iCount = dwLength;
lpRet = new MATERIALDATA;
while( iCount > 0 )
{
//块头
file.read( (char*)&wID, sizeof(wID) );
file.read( (char*)&dwBlockLength, sizeof(dwBlockLength) );
switch( wID )
{
case CHUNK_MATNAME: //材质名
{
//Null terminate string
char name[200];
int i=0;
memset( name, 1,sizeof(char)*200 );
file.read( name, 1 );
for( ; name[i]!=0; i++ )
file.read( (char*)name+i+1, 1 );
lpRet->strName = name;
}
break;
case CHUNK_AMBIENT: //材质参数,下同
{
WORD wID1;
DWORD dwBlockLength1;
float r,g,b;
file.read( (char*)&wID1, sizeof( wID1 ) );
file.read( (char*)&dwBlockLength1, sizeof(dwBlockLength1) );
//每个参量都是由RGB构成的
Read3DSColorChunk( file, wID1, r, g, b );
lpRet->pAmbient[0] = r;
lpRet->pAmbient[1] = g;
lpRet->pAmbient[2] = b;
}
break;
case CHUNK_DIFFUSE:
{
WORD wID1;
DWORD dwBlockLength1;
float r,g,b;
file.read( (char*)&wID1, sizeof( wID1 ) );
file.read( (char*)&dwBlockLength1, sizeof(dwBlockLength1) );
Read3DSColorChunk( file, wID1, r, g, b );
lpRet->pDiffuse[0] = r;
lpRet->pDiffuse[1] = g;
lpRet->pDiffuse[2] = b;
}
break;
case CHUNK_SPECULAR:
{
WORD wID1;
DWORD dwBlockLength1;
float r,g,b;
file.read( (char*)&wID1, sizeof( wID1 ) );
file.read( (char*)&dwBlockLength1, sizeof(dwBlockLength1) );
Read3DSColorChunk( file, wID1, r, g, b );
lpRet->pSpecular[0] = r;
lpRet->pSpecular[1] = g;
lpRet->pSpecular[2] = b;
}
break;
case CHUNK_TEXTURE:
//纹理块
Read3DSTextureChunk( file, dwBlockLength, lpRet );
break;
default:
file.seekg( dwBlockLength - sizeof(wID) - sizeof(dwBlockLength), ios::cur );
break;
}//end switch
iCount -= dwBlockLength;
}//end while
//跳到下一块
file.seekg( dwNextBlock, ios::beg );
return lpRet;
}
//----------------------------------------------------
//Function Name : LoadData
//Desc : 从指定文件中导入数据
//Input :file : 用于定位的fstream文件对象的引用
//Output : 成功返回GL3DSLOADER_OK,否则返回错误代码
//Notice : 文件不存在则调用失败,文件不是3ds文件调用也失败
//----------------------------------------------------
HRESULT CGL3DSLoader::LoadData( string filename,
CGL3DSLoader::MATERIALDATA**& lplpMaterial,
CGL3DSLoader::MESHDATA**& lplpMesh,
DWORD& dwMtrlNum,
DWORD& dwObjNum )
{
using namespace std;
fstream file;
lplpMaterial = NULL;
lplpMesh = NULL;
dwMtrlNum = 0;
dwObjNum = 0;
file.open( filename.data(), ios::in | ios::binary );
//若无法打开文件
if( !file.is_open() )
return GL3DSLOADERERR_CANNOTOPENFILE;
DWORD dwLength;
DWORD dwEditBlockLength;
if( ! ( dwLength = Seek3DSChunk( file, CHUNK_MAIN, 0 ) ) ) //找到主块
{
//不是3DS文件
file.close();
return GL3DSLOADERERR_NOT3DSFILE;
}
if( !( dwEditBlockLength = Seek3DSChunk( file, CHUNK_OBJMESH, dwLength ) ) ) //主编辑块
{
//可能的文件I/O错误
file.close();
return GL3DSLOADERERR_IOERROR;
}
list<MESHDATA*> listMesh;
list<MATERIALDATA*> listMaterial;
listMesh.clear( );
listMaterial.clear( );
DWORD dwObjectNum = 0;
DWORD dwMaterialNum = 0;
//在Edit块内找
int iCount = dwEditBlockLength;
while( iCount >0 )
{
WORD wID = 0;
DWORD dwBlockLength = 0;
DWORD dwNextBlock = file.tellg();
//读取块头
file.read( (char*)&wID, sizeof(wID) );
file.read( (char*)&dwBlockLength, sizeof(dwBlockLength) );
dwNextBlock += dwBlockLength;
MESHDATA* lpMesh = NULL;
MATERIALDATA* lpMaterial = NULL;
DWORD dwDataLength = dwBlockLength - sizeof(wID) - sizeof(dwBlockLength);
switch( wID )
{
case CHUNK_MATERIAL: //材质块
//创建一个新的材质对象放入链表尾部
lpMaterial = Read3DSMaterialChunk( file, dwDataLength );
if( lpMaterial )
{
listMaterial.push_back( lpMaterial );
dwMaterialNum ++;
}
break;
case CHUNK_OBJBLOCK: //物体块
lpMesh = Read3DSObjectChunk( file, dwDataLength );
//若是网格对象
if( lpMesh )
{
//创建一个网格对象放入链表尾部
listMesh.push_back( lpMesh );
dwObjectNum ++;
}
break;
default:
break;
}
file.seekg( dwNextBlock, ios::beg );
iCount -= dwBlockLength;
}
file.close();
lplpMesh = new MESHDATA*[dwObjectNum];
lplpMaterial = new MATERIALDATA*[dwMaterialNum];
//将对象指针拷贝到数组里
for( DWORD i=0; i<dwObjectNum; i++ )
{
lplpMesh[i] = listMesh.front();
listMesh.pop_front( );
}
for( i=0; i<dwMaterialNum; i++ )
{
lplpMaterial[i] = listMaterial.front();
listMaterial.pop_front( );
}
//重整Mesh对象的Material属性,用ID表示
for( i=0; i<dwObjectNum; i++ )
{
lplpMesh[i]->lpMaterialID = new DWORD[lplpMesh[i]->dwMaterialNum];
for( DWORD j=0; j<lplpMesh[i]->dwMaterialNum; j++ )
{
for( DWORD k=0; k<dwMaterialNum; k++ )
{
if( lplpMesh[i]->lpMaterialName[j] == lplpMaterial[k]->strName )
lplpMesh[i]->lpMaterialID[j] = k;
}
}
}
//参数返回
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -