3ds文件是3ds Max建模软件的标准输入输出格式,它的应用十分的广泛。各种虚拟现实项目都可使用它作为模型格式,甚至可以在游戏中使用,但是它的文件格式比较复杂而且 没有相关的官方文档,所以对它的读取显示一直是一个问题。笔者通过多年的项目经验总结了一套操作3ds文件的方法,简单的说就是使用lib3ds库对 3ds文件进行解析读取,然后利用OpenGL来显示。
一、 模型的读取
要绘制模型必须首先将其读入内存。 在我们读取3ds文件以前我们有必要了解一下3ds文件都包含那些组成部分。
1. 物体(Object)
这 可能是我们最关心的一部分了,一个3ds文件可以包含多个物体,一个物体可以是一座房屋、一头狮子、一个简单的长方体等等。物体又包含顶点数据、三角形索 引数据、纹理坐标数据、材质列表数据等。每种数据由一个特定的ID标识,ID说明了跟在其后的是什么数据。
2. 材质
材 质包含颜色、透明度、贴图文件名等数据。3ds文件里可以有多个材质,也可以没有。
3. 相机
相机数据中主 要包含相机的位置、朝向等信息。
4. 灯光
灯光数据中主要包含灯光的位置、类型等信息。灯光可以是泛光灯、 聚光灯等。
5. 关键帧
关键帧是用来描述动画的,其中包含了每个物体在每一关键帧处的变换矩阵。这样只要在 绘制每一帧动画前给物体乘上相应的变换矩阵即可实现动画。需要注意一下3ds文件只能描述刚体动画,不能描述柔体动画。
读取的工作 由lib3ds中的lib3ds_file_load(char *filename)函数来实现,lib3ds库将不同类型数据组织成以file为根节点的树状结构,而同类数据以链表的形式存放,可以像这样遍历一种数据:
Lib3dsMesh *p;
for(p=file->meshes;p!=0;p=p->next)
{
//在这里您可以用p做一些事情
}
以下为模型加载的关键代码:
// 加载模型
file=lib3ds_file_load(投篮.3DS);
// 加载出错或找不到文件时显示错误信息并退出
if (!file) {
puts(没有找到文件\n);
exit(1);
}
以下代码获取模型文件的包围盒
lib3ds_file_bounding_box_of_nodes(file, LIB3DS_TRUE, LIB3DS_FALSE, LIB3DS_FALSE, bmin, bmax); sx = bmax[0] - bmin[0]; sy = bmax[1] - bmin[1]; sz = bmax[2] - bmin[2]; size = MAX(sx, sy); size = MAX(size, sz); cx = (bmin[0] + bmax[0])/2; cy = (bmin[1] + bmax[1])/2; cz = (bmin[2] + bmax[2])/2;
因为3ds Max制作的场景中可能没有灯光、相机所以人为的在模型的左右上下各加入一个,代码如下:
if( !file->cameras ) { Lib3dsCamera *camera = lib3ds_camera_new(Camera_X); camera->target[0] = cx; camera->target[1] = cy; camera->target[2] = cz; memcpy(camera->position, camera->target, sizeof(camera->position)); camera->position[0] = bmax[0] + 1.5 * MAX(sy,sz); camera->near_range = ( camera->position[0] - bmax[0] ) * .5; camera->far_range = ( camera->position[0] - bmin[0] ) * 2; lib3ds_file_insert_camera(file, camera); camera = lib3ds_camera_new(Camera_Y); camera->target[0] = cx; camera->target[1] = cy; camera->target[2] = cz; memcpy(camera->position, camera->target, sizeof(camera->position)); camera->position[1] = bmin[1] - 1.5 * MAX(sx,sz); camera->near_range = ( bmin[1] - camera->position[1] ) * .5; camera->far_range = ( bmax[1] - camera->position[1] ) * 2; lib3ds_file_insert_camera(file, camera); camera = lib3ds_camera_new(Camera_Z); camera->target[0] = cx; camera->target[1] = cy; camera->target[2] = cz; memcpy(camera->position, camera->target, sizeof(camera->position)); camera->position[2] = bmax[2] + 1.5 * MAX(sx,sy); camera->near_range = ( camera->position[2] - bmax[2] ) * .5; camera->far_range = ( camera->position[2] - bmin[2] ) * 2; lib3ds_file_insert_camera(file, camera); camera = lib3ds_camera_new(Camera_ISO); camera->target[0] = cx; camera->target[1] = cy; camera->target[2] = cz; memcpy(camera->position, camera->target, sizeof(camera->position)); camera->position[0] = bmax[0] + .75 * size; camera->position[1] = bmin[1] - .75 * size; camera->position[2] = bmax[2] + .75 * size; camera->near_range = ( camera->position[0] - bmax[0] ) * .5; camera->far_range = ( camera->position[0] - bmin[0] ) * 3; lib3ds_file_insert_camera(file, camera); }