⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 3dsloader.cpp

📁 三维模型数据格式3DS数据的读取。详细分解3DS数据格式
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//----------------------------------------------------
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 + -