zoukankan      html  css  js  c++  java
  • 我的3ds Loader !

    早就想自己做个3dmax的读取器了,前几天找了点资料学习了一下,经过三天的努力,今晚终于告罄!简是简单了点,而且没有设置材质,但有纹理信息,而且效果还挺好。特地抓了一张图贴来晒晒。


    我的M60主战坦克!

    a. 3ds文件存储的基本单元叫“chunk”,我们就是读这样一块一块的信息。目录树如下,加粗体的是chunk的标识符,而缩进风格体现了块的父子关系:

    MAIN CHUNK 0x4D4D
       3D EDITOR CHUNK 0x3D3D
          OBJECT BLOCK 0x4000
             TRIANGULAR MESH 0x4100
                VERTICES LIST 0x4110
                FACES DESCRIPTION 0x4120
                   FACES MATERIAL 0x4130
                MAPPING COORDINATES LIST 0x4140
                   SMOOTHING GROUP LIST 0x4150
                LOCAL COORDINATES SYSTEM 0x4160
             LIGHT 0x4600
                SPOTLIGHT 0x4610
             CAMERA 0x4700
          MATERIAL BLOCK 0xAFFF
             MATERIAL NAME 0xA000
             AMBIENT COLOR 0xA010
             DIFFUSE COLOR 0xA020
             SPECULAR COLOR 0xA030
             TEXTURE MAP 1 0xA200
             BUMP MAP 0xA230
             REFLECTION MAP 0xA220
             [SUB CHUNKS FOR EACH MAP]
                MAPPING FILENAME 0xA300
                MAPPING PARAMETERS 0xA351
          KEYFRAMER CHUNK 0xB000
             MESH INFORMATION BLOCK 0xB002
             SPOT LIGHT INFORMATION BLOCK 0xB007
             FRAMES (START AND END) 0xB008
                OBJECT NAME 0xB010
                OBJECT PIVOT POINT 0xB013
                POSITION TRACK 0xB020
                ROTATION TRACK 0xB021
                SCALE TRACK 0xB022
                HIERARCHY POSITION 0xB030


    b. 每一个“chunk”的结构如下所示:

    偏移量 长度  
    0 2 块标识符
    2 4 块长: 块数据 + 子块内容
    6 n 块数据
    6+n m S子块

    c. 读取的思路是:首先根据偏移量和长度找到一个块的标识符,然后据此来判断它是什么块,遇到我们需要的块,就进一步读取,如果不需要,直接跳过这一块,读取下面的块。

    d. 一个简单的Loader程序不需要什么都读取,只需要读基本的图元就行了。比如点数据,面数据,和纹理数据。我的Laoder主要包含的就是这三个信息。当然,如果你要读到这三者,起码要把先前的父块内容读入才行,否则就被跳过了。因此你一共要读个7块数据。这7者信息如下:

    MAIN CHUNK
    Identifier 0x4d4d 
    Length 0 + sub-chunks length
    Chunk father None
    Sub chunks 3D EDITOR CHUNK
    Data None
    3D EDITOR CHUNK
    Identifier 0x3D3D 
    Length 0 + sub-chunks length
    Chunk father MAIN CHUNK
    Sub chunks OBJECT BLOCK, MATERIAL BLOCK, KEYFRAMER CHUNK
    Data None
    OBJECT BLOCK
    Identifier 0x4000
    Length Object name length + sub-chunks length
    Chunk father 3D EDITOR CHUNK
    Sub chunks TRIANGULAR MESH, LIGHT, CAMERA
    Data Object name
    TRIANGULAR MESH
    Identifier 0x4100
    Length 0 + sub-chunks length
    Chunk father OBJECT BLOCK
    Sub chunks VERTICES LIST, FACES DESCRIPTION, MAPPING COORDINATES LIST
    Data None
    VERTICES LIST(点数据在这)
    Identifier 0x4110
    Length varying + sub-chunks length
    Chunk father TRIANGULAR MESH
    Sub chunks None
    Data Vertices number (unsigned short)
    Vertices list: x1,y1,z1,x2,y2,z2 etc. (for each vertex: 3*float)
    FACES DESCRIPTION(面数据在这)
    Identifier 0x4120
    Length varying + sub-chunks length
    Chunk father TRIANGULAR MESH
    Sub chunks FACES MATERIAL
    Data Polygons number (unsigned short)
    Polygons list: a1,b1,c1,a2,b2,c2 etc. (for each point: 3*unsigned short)
    Face flag: face options, sides visibility etc. (unsigned short)
    MAPPING COORDINATES LIST(贴图数据在这)
    Identifier 0x4140
    Length varying + sub-chunks length
    Chunk father TRIANGULAR MESH
    Sub chunks SMOOTHING GROUP LIST
    Data Vertices number (unsigned short)
    Mapping coordinates list: u1,v1,u2,v2 etc. (for each vertex: 2*float)

    e. 稍作解释:OBJECT BLOCK块的Data里有内容,是一个以“\0”结尾的字符串,存放了该对象的名字,实际上从目录树上可以看出,接下来的点、面、纹理数据等都是该对象的子块。所以说该对象就是描述一个独立完整的模型的对象。不过你可以不用读它,当然,读了也没什么坏处。不过注意的是,不读是不是说要跳过呢?不然,即便不读,也要让指针经过它才能接着取到后面的数据,因此我说的“不读”的意思,是指这个信息没什么用,可以把它读到一个专门盛放废品的容器里。

    再来看VERTICES LIST,它的Data里有两方面的数据,一是一共有多少个点,二是分别这些点的x、y、z坐标值是什么,这些信息正是你需要的。

    再来看FACES DESCRIPTION,它的Data里有三方面的数据,一是一共有多少面,二是分别这些面是由哪三个点构成的,它用到了上面读到的点的数据。比如有一个面的信息是1,2,3,这就表示,这个面是由第1个点、第2个点和第3个点构成的。注意3ds里所有的面都是三角形,只需三个点。第三方面的内容face flag是专门为3dmax的编辑器使用的,我们不需要,所以要把它读到废品站里。

    再来看MAPPING COORDINATES LIST,这是纹理坐标数据,由u、v值表示坐标。这些纹理点的次序和上面的实点的次序是一一对应的。每一个实点都对应一个纹理点。

    f. 我的Laoder代码贴下:

      1#include"3dsloader.h"
      2#include<fstream>
      3
      4using namespace std;
      5
      6bool tri_dsLoader(char* f_name,Object* obj)
      7{
      8    //定义接受容器
      9    unsigned short    chunk_id;
     10    unsigned int      chunk_length;
     11    unsigned char     chunk_char[1];
     12    unsigned short    chunk_qty;
     13    unsigned short      i;
     14    unsigned short    chunk_face_flag_chuck;
     15    
     16
     17
     18    
     19    //新建输入流对象
     20    std::ifstream f_in;
     21
     22    
     23
     24    //打开文件
     25    f_in.open(f_name,ios::binary,0x10) ;  //注意第三个参数的取值,只能是0x10,0x20,0x30,0x40中的一个。其他值会报“invalid sha”错误
     26    if(f_in.fail()) return false;
     27
     28    //length表示文件长度
     29    f_in.seekg(0,ios::end);
     30    streampos length=f_in.tellg();
     31    f_in.seekg(0,ios::beg);
     32    
     33    //读取文件
     34    while(f_in.tellg()<length)
     35    {
     36        f_in.read((char*)&chunk_id,sizeof(unsigned short));
     37        f_in.read((char*)&chunk_length,sizeof(unsigned int));
     38
     39        switch(chunk_id)
     40        {
     41        case 0x4d4d:
     42            break;
     43
     44        case 0x3d3d:
     45            break;
     46
     47        case 0x4000:
     48            unsigned char*      p_name;
     49            p_name=obj->getP_name();
     50            do
     51            {
     52                f_in.read((char*)chunk_char,1);
     53                *p_name=chunk_char[0];
     54                ++p_name;
     55            }
    while(chunk_char[0]!='\0');
     56            break;
     57
     58        case 0x4100:
     59            break;
     60
     61        case 0x4110:
     62            VertexCoord point;
     63            f_in.read((char*)&chunk_qty,2);
     64            obj->set_points_num(chunk_qty);
     65
     66            for(i=0;i<chunk_qty;++i)
     67            {
     68                f_in.read((char*)&point.x,4);
     69                f_in.read((char*)&point.y,4);
     70                f_in.read((char*)&point.z,4);
     71
     72                obj->set_points(i,point);
     73            }

     74            break;
     75
     76        case 0x4120:
     77            Triangle surface;
     78            f_in.read((char*)&chunk_qty,2);
     79            obj->set_surfaces_num(chunk_qty);
     80
     81            for(i=0;i<chunk_qty;++i)
     82            {
     83                f_in.read((char*)&surface.a,2);
     84                f_in.read((char*)&surface.b,2);
     85                f_in.read((char*)&surface.c,2);
     86
     87                obj->set_surfaces(i,surface);
     88
     89                f_in.read((char*)&chunk_face_flag_chuck,2);
     90            }

     91            break;
     92
     93        case 0x4140:
     94            MappingCoord texture;
     95            f_in.read((char*)&chunk_qty,2);
     96
     97            for(i=0;i<chunk_qty;++i)
     98            {
     99                f_in.read((char*)&texture.u,4);
    100                f_in.read((char*)&texture.v,4);
    101
    102
    103                obj->set_textures(i,texture);
    104            }

    105
    106            break;
    107
    108        default:        
    109            f_in.seekg(chunk_length-6,ios::cur);
    110        }

    111    }

    112    f_in.close();
    113    
    114    return true;
    115}

    g. 我认为把别人的成果拿来说成是自己的是不道德的,因此我一定要交待下我获取此知识的途径:http://www.spacesimulator.net/tut4_3dsloader.html。我要向作者表示感谢,虽然他永远也不知道,而且这小子从下一篇教程起就开始收费了。


  • 相关阅读:
    echarts + timeline 显示多个options
    微信如何获取unionid 并且打通微信公众号和小程序
    枚举
    十三、springboot集成定时任务(Scheduling Tasks)
    十二、springboot之web开发之静态资源处理
    十一、springboot之web开发之Filter
    十、springboot之web开发打包生产
    九、springboot整合redis二之缓冲配置
    RedisTemplate使用
    八、springboot整合redis
  • 原文地址:https://www.cnblogs.com/lookof/p/1423695.html
Copyright © 2011-2022 走看看