Direct3D学习手记十一:网格二【从.x文件中加载网格】

上篇我们介绍了自己创建网格的方法,此次我们介绍从.x文件中加载3D模型的方法

.x文件:

.x文件是一种存储3D模型数据的一种文本文件,其存储格式有两种:文本方式和二进制方式,

其中文本方式易于查看,.x文件有其特定的格式

.x文件中存储了顶点数据,材质信息和纹理贴图信息等待,可以通过特定的方式读取其中的内容。

从.x文件中加载网格:

HRESULT D3DXLoadMeshFromX(  LPCTSTR pFilename,  DWORD Options,
  LPDIRECT3DDEVICE9 pD3DDevice,  LPD3DXBUFFER * ppAdjacency,
  LPD3DXBUFFER * ppMaterials,  LPD3DXBUFFER * ppEffectInstances,
  DWORD * pNumMaterials,  LPD3DXMESH * ppMesh);

pFilename为.x文件的名称(注意目录结构)

Options为网格创建标志,有D3DXMESH_MANAGED、D3DXMESH_WRITEONLY、D3DXMESH_32BIT等待

pD3DDevice为设备指针

ppAdjacency保存网格的邻接信息

ppMaterials保存网格的材质信息

ppEffectInstances保存网格的特效信息,暂时不使用置为NULL即可

pNumMaterials保存网格所使用的材质的数目

ppMesh保存创建的网格

例如:

	//从.x文件中加载网格
	HRESULT hr=E_FAIL;
	LPD3DXBUFFER pAdjBuffer=NULL,pMtrlBuffer=NULL;
	hr=D3DXLoadMeshFromX(TEXT("Demo_11_Media\\baodao.x"),
							D3DXMESH_MANAGED,
							g_pd3dDevice,
							&pAdjBuffer,
							&pMtrlBuffer,
							NULL,
							&g_dwMtrlNum,
							&g_pMesh);

其中LPD3DXBUFFER为ID3DXBuffer接口指针,该类型是一种泛型数据,类型为void,可以通过函数:

LPVOID GetBufferPointer();
得到内存地址,并通过强制类型转换为想要的数据格式,

如存储邻接信息(ppAdjacency)的是DWORD类型的数组可使用如下方式转换:

(DWORD *)pAdjBuffer->GetBufferPointer()
另外,保存材质信息的是D3DXMATERIAL结构类型的数组,该结构如下:

typedef struct D3DXMATERIAL {
    D3DMATERIAL9 MatD3D;
    LPSTR pTextureFilename;
} D3DXMATERIAL, *LPD3DXMATERIAL;
其中MatD3D是之前介绍过的材质,pTextureFilename指向该材质使用的纹理贴图文件名称字符串(使用时注意目录结构)

可以使用如下方式获得其首地址:

LPD3DXMATERIAL	pMtrl=(LPD3DXMATERIAL)pMtrlBuffer->GetBufferPointer();

获取材质和纹理信息:

从.x文件中加载并创建完网格后,就可以读取其中的材质和纹理信息,并保存下来

全局变量用于保存:

std::vector<D3DMATERIAL9>	g_vecMaterials(0);//材质
DWORD						g_dwMtrlNum=0;//材质数量
std::vector<LPDIRECT3DTEXTURE9>	g_vecPTextures(0);//纹理
读取信息:

	//获取材质和纹理
	CHAR szTextureFile[MAX_PATH];
	if(NULL!=pMtrlBuffer && 0!=g_dwMtrlNum)
	{
		LPD3DXMATERIAL	pMtrl=(LPD3DXMATERIAL)pMtrlBuffer->GetBufferPointer();
		for(DWORD i=0;i<g_dwMtrlNum;i++)
		{
			pMtrl[i].MatD3D.Ambient=pMtrl[i].MatD3D.Diffuse;//设置材质对环境光的反射
			g_vecMaterials.push_back(pMtrl[i].MatD3D);//添加材质

			if(NULL!=pMtrl[i].pTextureFilename)//纹理文件名不为空
			{
				ZeroMemory(szTextureFile,sizeof(szTextureFile));
				sprintf(szTextureFile,"Demo_11_Media\\%s",pMtrl[i].pTextureFilename);
				LPDIRECT3DTEXTURE9 pTexture=NULL;
				D3DXCreateTextureFromFileA(g_pd3dDevice,szTextureFile,&pTexture);//创建新纹理
				g_vecPTextures.push_back(pTexture);//添加纹理
			}
			else
			{
				//贴图文件为空,则设置当前纹理为NULL
				g_vecPTextures.push_back(NULL);
			}
		}
	}
	SAFE_RELEASE(pMtrlBuffer);//释放材质缓存ID3DXBuffer


网格优化:

完成上面的步骤就可以绘制网格了,但是在绘制之前,我们可以对其进行优化来提高绘制效率:

	//对网格进行优化,可有可无
	g_pMesh->OptimizeInplace(D3DXMESHOPT_COMPACT//优化标志,移除无用的顶点和索引
							|D3DXMESHOPT_ATTRSORT//根据属性ID对三角形图元进行排序
							|D3DXMESHOPT_VERTEXCACHE,//提高顶点高速缓存的命中率
							(DWORD *)pAdjBuffer->GetBufferPointer(),//指向尚未优化的网格的邻接数组的指针
							NULL,//存储优化后的网格的邻接信息
							NULL,
							NULL);
	SAFE_RELEASE(pAdjBuffer);//释放存储邻接信息的缓存


克隆网格:

另外,由于.x文件存储的模型可能顶点结构中不包含顶点法向量,此时要想使用光照效果就必须为其添加顶点法向量,

为此我们可以克隆一分网格,使克隆的网格具有顶点法向量,然后通过计算得到其顶点法向量,可使用如下方式完成:

	//判断顶点是否具有法向量,可有可无
	if(!(g_pMesh->GetFVF() & D3DFVF_NORMAL))
	{
		//没有法向量,则克隆一分
		LPD3DXMESH	pTempMesh=NULL;
		g_pMesh->CloneMeshFVF(D3DXMESH_MANAGED,
							g_pMesh->GetFVF()|D3DFVF_NORMAL,
							g_pd3dDevice,
							&pTempMesh);//克隆网格,使其具有法向量
		if(NULL!=pTempMesh)
		{
			D3DXComputeNormals(pTempMesh,NULL);//计算顶点法向量
			SAFE_RELEASE(g_pMesh);
			g_pMesh=pTempMesh;
		}
	}

绘制网格:

		//绘制网格
		for(DWORD i=0;i<g_dwMtrlNum;i++)
		{
			g_pd3dDevice->SetMaterial(&g_vecMaterials[i]);//设置材质
			g_pd3dDevice->SetTexture(0,g_vecPTextures[i]);//设置纹理
			g_pMesh->DrawSubset(i);//绘制子集
		}

释放纹理:

在程序退出时使用Cleanup函数释放资源,注意要释放所有的纹理:

	for(DWORD i=0;i<g_dwMtrlNum;i++)
	{
		SAFE_RELEASE(g_vecPTextures[i]);//释放纹理
	}
	g_vecPTextures.clear();
	g_vecMaterials.clear();

Setup函数:

Setup函数实现了从.x文件创建网格和信息读取的关键步骤

/****************************************************************
*函数名	:	Setup
*功能		:	创建与初始化资源、缓存、变换等
*输入		:	hWnd:窗口句柄
*输出		:	无
*返回值	:	成功:TRUE		失败:FALSE
****************************************************************/
BOOL			Setup(HWND hWnd)
{
	if(NULL==hWnd)
		return FALSE;

	//创建字体
	//方式一
	D3DXCreateFont(g_pd3dDevice,20,14,600,D3DX_DEFAULT,FALSE,DEFAULT_CHARSET,0,0,0,TEXT("微软雅黑"),&g_pHelpFont);

	//方式二
	D3DXFONT_DESC fd;
	ZeroMemory(&fd,sizeof(D3DXFONT_DESC));
	fd.Height=20;
	fd.Width=14;
	fd.Weight=600;
	fd.MipLevels=D3DX_DEFAULT;
	fd.Italic=TRUE;
	fd.CharSet=DEFAULT_CHARSET;
	fd.OutputPrecision=0;
	fd.PitchAndFamily=0;
	fd.Quality=0;
	_tcscpy_s(fd.FaceName,TEXT("Times New Roman"));
	D3DXCreateFontIndirect(g_pd3dDevice,&fd,&g_pTipFont);
	
	//从.x文件中加载网格
	HRESULT hr=E_FAIL;
	LPD3DXBUFFER pAdjBuffer=NULL,pMtrlBuffer=NULL;
	hr=D3DXLoadMeshFromX(TEXT("Demo_11_Media\\baodao.x"),
							D3DXMESH_MANAGED,
							g_pd3dDevice,
							&pAdjBuffer,
							&pMtrlBuffer,
							NULL,
							&g_dwMtrlNum,
							&g_pMesh);
	if(FAILED(hr))
		return FALSE;

	//获取材质和纹理
	CHAR szTextureFile[MAX_PATH];
	if(NULL!=pMtrlBuffer && 0!=g_dwMtrlNum)
	{
		LPD3DXMATERIAL	pMtrl=(LPD3DXMATERIAL)pMtrlBuffer->GetBufferPointer();
		for(DWORD i=0;i<g_dwMtrlNum;i++)
		{
			pMtrl[i].MatD3D.Ambient=pMtrl[i].MatD3D.Diffuse;//设置材质对环境光的反射
			g_vecMaterials.push_back(pMtrl[i].MatD3D);//添加材质

			if(NULL!=pMtrl[i].pTextureFilename)//纹理文件名不为空
			{
				ZeroMemory(szTextureFile,sizeof(szTextureFile));
				sprintf(szTextureFile,"Demo_11_Media\\%s",pMtrl[i].pTextureFilename);
				LPDIRECT3DTEXTURE9 pTexture=NULL;
				D3DXCreateTextureFromFileA(g_pd3dDevice,szTextureFile,&pTexture);//创建新纹理
				g_vecPTextures.push_back(pTexture);//添加纹理
			}
			else
			{
				//贴图文件为空,则设置当前纹理为NULL
				g_vecPTextures.push_back(NULL);
			}
		}
	}
	SAFE_RELEASE(pMtrlBuffer);//释放材质缓存ID3DXBuffer
	
	//对网格进行优化,可有可无
	g_pMesh->OptimizeInplace(D3DXMESHOPT_COMPACT//优化标志,移除无用的顶点和索引
							|D3DXMESHOPT_ATTRSORT//根据属性ID对三角形图元进行排序
							|D3DXMESHOPT_VERTEXCACHE,//提高顶点高速缓存的命中率
							(DWORD *)pAdjBuffer->GetBufferPointer(),//指向尚未优化的网格的邻接数组的指针
							NULL,//存储优化后的网格的邻接信息
							NULL,
							NULL);
	SAFE_RELEASE(pAdjBuffer);//释放存储邻接信息的缓存

	//判断顶点是否具有法向量,可有可无
	if(!(g_pMesh->GetFVF() & D3DFVF_NORMAL))
	{
		//没有法向量,则克隆一分
		LPD3DXMESH	pTempMesh=NULL;
		g_pMesh->CloneMeshFVF(D3DXMESH_MANAGED,
							g_pMesh->GetFVF()|D3DFVF_NORMAL,
							g_pd3dDevice,
							&pTempMesh);//克隆网格,使其具有法向量
		if(NULL!=pTempMesh)
		{
			D3DXComputeNormals(pTempMesh,NULL);//计算顶点法向量
			SAFE_RELEASE(g_pMesh);
			g_pMesh=pTempMesh;
		}
	}

	//设置取景变换矩阵
	D3DXMATRIX matView;
	D3DXVECTOR3 vEye(0.0f,0.0f,-300.0f);
	D3DXVECTOR3 vAt(0.0f,0.0f,0.0f);
	D3DXVECTOR3 vUp(0.0f,1.0f,0.0f);
	D3DXMatrixLookAtLH(&matView,&vEye,&vAt,&vUp);
	g_pd3dDevice->SetTransform(D3DTS_VIEW,&matView);

	//设置投影变换矩阵
	D3DXMATRIX matProjection;
	::D3DXMatrixPerspectiveFovLH(&matProjection,D3DX_PI/4.0F,(FLOAT)g_nWidth/(FLOAT)g_nHeight,1.0F,1000.0F);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION,&matProjection);

	//设置光照
	SetLight(2,g_pd3dDevice);

	//设置纹理过滤方式
	g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
	g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
	g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

	//显示窗口
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
	return TRUE;
}

程序运行结果:



源代码与工程文件下载地址:

百度网盘


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值