unigine mesh文件经历很多版本都没有变动过,测试版本2.7.2
unigine 相关的官方论坛:developer.unigine.com
比较详细的mesh描述可以参看文档的mesh file format 和mesh类及下面的一些结构上的说明, 以及mesh相关的类和案例
mesh中的一大缺陷是不描述曲线。
uigine 中mesh结构可以存放和修改动画、模型、骨骼及其他相关的东西。关于修改,操作一般是从一个mesh选择性的添加到另外一个mesh。
可以通过mesh来加载动画或制作动画。加载制作模型,以及两者的结合体。
原生的mesh ,objectstaticmesh,objectDynamicMesh 和objectSkinnedMesh有很全的接口去操作或保存mesh文件。
当然也可以麻烦点手动写代码去创建和修改mesh文件。
文件格式大致如下
MESH File Format
|
上面的描述中对于所有的size需要按如下代码来读取,存储方式类似utf8,是不定字节数的:
bool Mesh::readNum(FILE *f, int &value, int index )//num range (0, fff)
{
unsigned char c;
if (readByte(f, c) == false) return false;
int offset = index > 0 ? 7 : 6;
if (c & 0x80)//means have a byte followed
{
readNum(f, value, index + 1);
value = (value << offset) | (c & 0x7f);
}
else
{
value = c & 0x7f;
}
return true;
}
1、关于surface。
一个mesh中可以有多个surface, surface是一个比较上层的概念,内部包含各个target和uv以及triangle等信息。在继承它的staticmesh等中可对surface设置lod。一套UV在unigine中是以二维顶点信息(texcoord)和顶点下标来表示的。一套surface可以绑定最多两套uv。
2、 关于anim:
anim是与骨骼严格相关的。一个mesh中可以存放多套animation,默认只有能有一套动作,但是在代码中可以操作。通过setNumLayers(int num)来设置几套animation。通过setLayer ( int layer, bool enabled, float weight )来控制animation的使用以及插值。
如下案例为anim的转储和对anim重命名。(可解决动画串着影响的问题)
可以参考我另外的博文及相关工具代码。
3、关于骨骼与模型绑定(vertex weight)关系:
这种绑定用途就是蒙皮动画(skinned animation),在unigine中有objectskinnedmesh专用于进行蒙皮动画的类。原理就是对模型的每个顶点,设置影响其位置变化的骨骼和其对应的权重(unigine中目前最多是四个骨骼 )。
在第三方软件中绑定好后,unigine中存放的关系如下。
骨骼是mesh层面的,而骨骼与顶点的绑定关系是surface层面的。绑定关系的数量目前来看与顶点数量一致。
vertex weight 结构:
测试样例:
4、关于target 和 blend shape。
一个target表示一个完整的geometry ,一个geometry至少有两个buffer(详情看mesh),vertex coordinate(float3), triangle vertex{struct triangle vertex(结构在下面有描述),一个vertex只有一个vertex coordinate,但是可能有多个triangle vertex} ,而vertex indices(int)(CIndices),triangle indices(int)(需要注意在文件中存储时有点小差别)(TIndices)。操作时,先对所有的顶点进行addvertex,然后在addIndices 会对vertex indices 和triangle indices同时扩容,addTindices会对triangle indicies 扩容(可通过getnumIndices和getNumTIndices)。mesht->createIndices(0) ,能对vertex做优化,消除一些重复的点。至于为何会有两个indices 文档中有说明:
mesh中getnumvertex 与getnumCVertex描述的是一样的东西。
Each coordinate vertex contains coordinates ( float[3]) of the vertex. |
Each triangle vertex can store:
|
The number of vertices and coordinate vertices is equal. |
可以看出mesh的存储分为坐标点(实际存放顶点信息)和三角形点(坐标点下标),两者的数量可能不一致 。三角点用于绘制工作,坐标点用于顶点缓存。
一个surface 至少有1个target,多个target主要用于blend shape,通过设置target 之间的权重(在dynamicmesh中)来计算相应顶点的当前的位置。多个target时,每个target的顶点数是一样的,多个target共用三角形indices和uv,可以理解为三角形顶点信息(包括顶点位置,tangent,normal)是target层面的,而三角形indices和uv是surface层面的 。
默认添加顶点时都是往第0 个target上添加。
// create mesh //创建一个具有球形target的surface,并另外添加box和star的target。
Mesh mesh = new Mesh();
// sphere surface
int surface = mesh.addSphereSurface("surface",2.0f,32,64);//此时创建好了这个surface的indices和uv信息。以及第一个target的顶点信息,没有设置第一个target的名字默认为Target_0。返回的值表示surface的下标
// create targets
int target_0 = mesh.addSurfaceTarget(surface,"box");//在下标为surface的surface上添加名字为“box”的target,内部会分配好存储一个target顶点信息的缓存。并返回这个target的下标
int target_1 = mesh.addSurfaceTarget(surface,"star");//在下标为surface的surface上添加名字为“star”的target
forloop(int i = 0; mesh.getNumVertex(surface)) {
vec3 vertex = normalize(mesh.getVertex(i,surface));
vec3 scale = vec3_one - vertex.yzx * vertex.yzx * 0.5f - vertex.zxy * vertex.zxy * 0.5f;
mesh.setVertex(i,vertex * scale * 2.5f,surface,target_0);//设置target的顶点位置信息
mesh.setVertex(i,vertex / scale * 2.0f,surface,target_1);
}
// update tangent space
mesh.createTangents(surface,target_0);//更新target顶点的tangent信息和normal信息。
mesh.createTangents(surface,target_1);
mesh.save(fullPath("samples/objects/meshes/skinned_07.mesh"));
delete mesh;
void setTarget ( int target, bool enabled, int index, float weight, int surface )
Enables or disables a given morph target and sets all its parameters.
Arguments
int target - Morph target number.
bool enabled - Enable flag: true to enable the morph target; false to disable it.
int index - Target index.
float weight - Target weight.
int surface - Surface number.
init(){
.....
// mesh targets
mesh.setNumTargets(5,0);
.....
}
update(){
......
//unigine内部实现中,mesh的surface最终展现的效果是由target决定的。存放当前显示的顶点的buffer与target存放顶点的buffer不是同一个buffer。当target的enable值为true则会将顶点结果叠加到显示的buffer中,默认第0个target的enable值为true。
//比如下面,会将5个设置了权重(weight)的target顶点位置叠加最终存放到显示buffer,并显示出来。通过这种顶点位置叠加和设置权重的方式来达到blend shape(morph) 的效果。
//所以使用的时候必须注意surface顶点的坐标系位置以及模型的顶点位置情况来判断是否适合用这种方式做morph,以及是否这种方式做的morph为所设想需要的结果。
float k0 = sin(time + i * 3.0f) + 0.75f;
float k1 = cos(time + i * 3.0f) + 0.75f;
mesh.setTarget(0,1,0,1.0f,0);//指令意思是将target 0顶点位置乘上1.0放到target 0中,此处我猜测应该不会有另外一层buffer来存放乘以weight后的顶点信息,而只是在数组中存放关键的参数,到最后的结果的时候一起进行计算。
mesh.setTarget(1,1,0,-k0,0);//将enable参数设置成0,表示不参与显示的计算。
mesh.setTarget(2,1,0,-k1,0);
mesh.setTarget(3,1,1,k0,0);
mesh.setTarget(4,1,2,k1,0);
/*过程:将target0 和target 1的顶点位置进行weight计算后分别放到target 3和target 4中,并将3 和4 的顶点位置信息叠加(位置叠加发现和tangent也会分别进行插值计算)放到显示的buffer中。target 0,target 1和target 2不参与最终的显示的计算。
mesh.setTarget(0,0,0,1.0f,0);
mesh.setTarget(1,0,0,-k0/2,0);
mesh.setTarget(2,0,0,k0/2-1.0f,0);
mesh.setTarget(3,1,0,k0/2,0);
mesh.setTarget(4,1,1,1.0f-k0/2,0);
*/
.....
}