本文主要是介绍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{//贴图文件为空,则设置当前纹理为NULLg_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{//贴图文件为空,则设置当前纹理为NULLg_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;
}
程序运行结果:
源代码与工程文件下载地址:
百度网盘
这篇关于Direct3D学习手记十一:网格二【从.x文件中加载网格】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!