这几天一直在研究怎样取得由静态模型(.x)所建立的地形中取得对应坐标的高度值。期间研究了很多的问题,.x文件的格式,从mesh的buffer中读取得到其顶点信息,射线跟面的相交检测等等。最后终于做了个实验模型出来了,现在在这里总结一下经验,以及期间发现的一些已解决,未解决的问题。希望各位如果发现文章中有什么需要改善或者不对的地方,指出来一起研究。
首先要从.x格式讲起。
由于.x格式真要讲的话,就这一点也能写一篇很长的文章了,而且在Gameres就已经有高手写了一篇很详尽的了,在这里我就只是说一些简单的,我知道的一些,如果想详细研究的,这里给出地址:
http://www.gameres.com/document.asp?TopicID=47493
用3dmax建好模型后,用熊猫插件输出.x文件~记得选择输出为text文件的,如果选择输出为二进制(Binary)的,那么你用记事本打开的时候就只会看到很多乱码,当然,文件体积小了,但不方便我们研究。
用记事本打开.x文件后,你会看到很多含template关键字的模板。
例如:
template Mesh
{
<3d82ab44-62da-11cf-ab39-0020af71e433>
DWORD nVertices; //顶点数量
array Vector vertices[nVertices]; //顶点数组
DWORD nFaces; //面片个数
array MeshFace faces[nFaces]; //面片数组
[...]
}
这个就告诉你对应模板名称的模板里面是怎么定义的
这里的模板名称为Mesh,
我们再找到对应的~
Mesh {
5; //对应模板中的DWORD nVertices(顶点数量)
-1068.06445;0.000000;-80.981415;, //对应array Vector vertices[nVertices](顶点数组)
1057.55237;6.156775;-80.981415;,
-1068.06445;0.000000;-26.824957;,
-537.72321;-2.920163;-25.445328;,
1057.55249;6.156775;-25.445328;,
4; //对应DWORD nFaces; //面片个数
3;2,8,0;, //对应array MeshFace faces[nFaces]; //面片数组
3;2,5,10;,
3;3,6,16;,
3;9,8,11;,
}
由上面我们可以知道这个.x文件时由5个点,4个面组成。并且第一个面由2,8,0三个点组成。(因为太多点,篇幅太长,所以我删了一些点,改了一下点的总数那些数据,所以可能你会有疑问,怎么总共就5个点,第一个面却由2,8,0,这第八个点是怎么来的。其实只是被我删了改了一下数据,大家理解就好。)
ps:这里虽然不相干,但是因为发现身边以及网上都有很多人问道关于贴图的问题,附加说一下.x文件内部的贴图。
由direct读取.x的知识我们知道,.x内部是不储存纹理文件的,只储存一个纹理的名字(准确点讲是路径,如果你输出的.x文件使
用绝对路径的话)。
而纹理名称是储存在.x的材质中的。
通过在.x文件中搜索纹理的文件名我们可以找到这个纹理所在的材质模板。
如“头副本”:
Material PDX23_-_Default {
1.000000;1.000000;1.000000;1.000000;;
3.200000;
0.000000;0.000000;0.000000;;
0.000000;0.000000;0.000000;;
TextureFilename {
"/315/头3/270/副/261/本.jpg";
}
“PDX23”是这个材质的名字,后面会用到。
下面的"/315/头3/270/副/261/本.jpg"就是我们“PDX23”材质中的纹理的名称了,要注意的是!很多朋友说用3dmax导出.x文
件后无法贴图,这是因为你的纹理名称使用了中文命名的缘故,可能因为一些字符转换的原因,DIRECTX在读取纹理名称的时候不能
正确读取以中文命名的纹理文件,所以就会导致了无法贴图。以我现在的技术水平,我能想到的解决方法只有:
1.让你的美工人员在更改贴图名称为英文名称再重新生成文件。
2.。。。。自己手工更改,自己动手,风衣足食。
这里说一下手工更改的方法。
这里就将"/315/头3/270/副/261/本.jpg";本去掉,然后改为"head.jpg"然后你的纹理文件名称也改为"head.jpg"......其实
就系这么简单。
然后我不知道大家有没有遇到过。生成的.x文件就算名称都是用英文的,但是有些地方会出现纹理错误的状况,就是这个地方本来
应该贴草地的贴图,但是现在却变成了水的纹理。
这个。。。我想原因是因为输出.x的时候一些设置不正确而导致的。至于应该怎么设置才正确,这个我也还未知道,但是我们可以
在.x文件内部去更改。
在用于表示模型中每一个物体的Frame模板内部,都可以找到一个“MeshMaterialList”
例如上面的例子中我们通过搜索该材质的名称“PDX23_-_Default”可以找到,该纹理是位于文件中“Frame pPlane08”物体模
板里面的其中,“pPlane08”对应你在3dmax中该物体使用的名字,你可以通过打开3dmax中的层管理器展开查看对应的是什么物
体。
MeshMaterialList {
1; //材质数量
12; //该材质对应的面数
0, //面索引数组
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0;
{ PDX23_-_Default } //对应的材质
}
通过更改这里的“对应材质”以及上面的纹理名称就可以达到更改纹理或者更改材质的效果了。
现在开始说读取高度坐标。
我的思路是,先去取得.x导进来的mesh文件中的buffer然后再将buffer内的顶点信息放到一个顶点数组中,然后找到指定点位于mesh网格中的哪一个面片,然后由该面片所组成的三个顶点用线性插值求得高度y的值。
首先将顶点输出到数组。
这里要
1.先构造一个顶点灵活格式的数组。
2.再用自定的顶点灵活格式将mesh复制一个出来。
3.再用新复制的mesh去去得他的顶点缓存。
这里要说一下lock跟顶点的赋值,用上面的方法锁定缓存并赋值后,我调试的时候发现,虽然成功赋值到顶点数组中了~但是里面的顶点信息跟.x文本打开查看的点位置不一样~在网上看到一些人说是导入的时候经过d3d优化了~
到这里都不是问题然后当我用相同的方式(声明一个DWORD * index,然后用去得的索引缓存去lock然后赋值)但是我在调试的时候发现得到的索引值为4862,我那个模型只是一个很简单的面片,不可能有那么多的索引值~所以当我尝试将索引值放到顶点数组中使用的时候就出错了~
这里给出我出错的代码
想了很久找不到原因,望各位高手指出其中的问题。
于是我原本的设想的实现在这一步受到很大的阻碍,但是后来我发现了一个在d3d中很有用的函数,D3DXIntersect。
这个函数的原型是
HRESULT D3DXIntersect(
LPD3DXBASEMESH pMesh, //模型文件
CONST D3DXVECTOR3 * pRayPos, //一个射线的发出点
CONST D3DXVECTOR3 * pRayDir, //射线的方向向量
BOOL * pHit, //这条射线是否跟这个MESH相交?
DWORD * pFaceIndex, //返回跟射线相交的面的最近射线点。
FLOAT * pU, // 射线跟相交面的U分量
FLOAT * pV, //射线跟相交面的V分量
FLOAT * pDist, 射线起始点到相交点的距离
LPD3DXBUFFER * ppAllHits, //如果相交的面不止一个,返回所有相交点所组成的缓存的指针。
DWORD * pCountOfHits //如果相交的面不止一个,相交点的数目。
);
有了这个函数碰撞检测,地形高度这些都不是问题了。
先定义一个高度的最大值。x,z坐标我这里是我摄像机的坐标,取得了y的高度后,就可以做到画面跟着地形起伏而起伏的效果。
一直想要求得的高度坐标就是你定义的最大高度值,减去相交检测返回的距离pDist。
其中使用上面的D3DXIntersect函数要注意的是,定义向量的时候,向量的长度即为返回的pDist的一个单位的距离。
我的GETHEIGHT函数
由于开始学习不久,还有很多不会的地方,就总结的目的写了这篇文章,希望各位能指出文中不好的地方,教导我更好的方法,一起交流,一起学习。