三維模型obj文件的格式解析與讀取


請先看這兩個中文博客中對於obj的介紹:

讀取Obj格式的模型文件(Dx10)

C++讀入obj格式模型文件

更為詳細的英文資料(用google或者aol搜索 "obj format"即可得到):

http://en.wikipedia.org/wiki/Wavefront_.obj_file

Wavefront OBJ File Format Summary

最詳細的資料 obj spec: http://www.martinreddy.net/gfx/3d/OBJ.spec

http://people.cs.clemson.edu/~dhouse/courses/405/docs/brief-obj-file-format.html

http://www.scratchapixel.com/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/

 

看完以上隨便一個內容,obj的格式可以說了解了。下面實現對obj文件的讀取。

 

 .obj文件中,每一行都有表明該行意義的標志符。對obj的讀取中,處理以下標志符

"v"--點的坐標,三維模型為x, y, z的順序;程序中以

1 typedef struct ObjVector3
2 {
3     ObjFloat x;
4     ObjFloat y;
5     ObjFloat z;
6 } ObjVector3;

結構存儲。

"vt"--紋理坐標,程序中以

1 typedef struct ObjVector2
2 {
3     ObjFloat x;
4     ObjFloat y;
5 } ObjVector2;

結構存儲

"vn"--法向量坐標,程序中與“v”的存儲結構相同

"f"--面所用到的點坐標/紋理坐標/法向量坐標的索引,

"mtllib"--.obj文件用到的material庫文件,“usemtl”標志符用到的material都是從material庫文件中取出的

"g"--組group的名稱,group里面的"f"可以使用0-N個"usemtl"對面的顯示渲染進行控制,當使用了大於1個“usemtl”標志符,程序處理時對於已經讀取的"f"很難控制;同時查看obj讀取的源碼,有的用到了這個標志符,有的沒有使用該標志,有的使用了"usemtl"標志符對所讀取的"f"面進行分割,本文的處理是使用"usemtl"標志符將"f"面分割為mesh,但是考慮到不同的group可以使用相同的"usemtl”標志(即不同的group都使用了 usemtl AAA),因此將"g"與"usemtl"結合起來,二者的名稱作為mesh的名稱

"usemtl"--參見"mtllib","g"。一旦使用了該標志符,則在該標志符后面的"f"全部受影響,直到遇到下一個"usemtl"

mesh結構,解析已經讀取的“f”存儲所面的點坐標/紋理坐標/法向量坐標,其結構為

 1 struct Mesh
 2 {
 3     string name;        // name
 4     ObjIntd mtl_idx;    // the index of material used by mesh 
 5 
 6     vector<ObjVector3> positions;    // "v" flag
 7     vector<ObjVector2> texcoords;    // "vt" flag
 8     vector<ObjVector3> normals;        // "vn" flag
 9     vector<ObjDword> indices;    // the indices of points of face in positions
10 
11     void reset()
12     { 
13         mtl_idx = -1;
14 
15         name.clear();
16         positions.clear();
17         texcoords.clear();
18         normals.clear();
19         indices.clear();
20     }
21 };
View Code

結構中出現的

ObjFloat ObjIntd

是一組typedef,具體為

 1 typedef int32_t ObjBool;
 2 typedef uint8_t ObjByte;
 3 typedef uint16_t ObjWord;
 4 typedef uint32_t ObjDword;
 5 typedef int8_t ObjIntb;
 6 typedef int16_t ObjIntw;
 7 typedef int32_t ObjIntd;
 8 
 9 typedef float ObjFloat;
10 typedef double ObjDouble;
View Code

 對obj文件讀取的主體程序如下

 1     ObjBool obj_file_load(const string& objname)
 2     {
 3         if (objname.empty())
 4             return LIBOBJ_FALSE;
 5 
 6         int mesh_count = 0;
 7         Mesh mesh_;
 8         string groupname;
 9         string usemtl;
10         vector<ObjVector3> positions;
11         vector<ObjVector3> normals;
12         vector<ObjVector2> texcoords;
13         vector<vector<ObjVertexIndex> > faces;
14         map<string, ObjIntd> mtlMap;
15 
16         fstream obj_stream;    
17         obj_stream.open(objname.c_str(), std::ios_base::in);
18         if (!obj_stream.is_open())
19             return LIBOBJ_FALSE;
20 
21         string obj_flag;
22         while (obj_stream.good())
23         {
24             obj_stream >> obj_flag;
25 
26             if (obj_flag.empty() || (obj_flag[0] == '#'))
27             {
28                 obj_stream.ignore(1024, '\n');            // skip line
29                 continue;
30             }
31 
32             if (obj_flag == "v")            // position flag
33                 if (!obj_parse_vector3(obj_stream, positions))
34                     return LIBOBJ_FALSE;
35             else if (obj_flag == "vt")    // texture coordinate flag
36                 if (!obj_parse_vector2(obj_stream, texcoords))
37                     return LIBOBJ_FALSE;
38             else if (obj_flag == "vn")    // normal flag
39                 if (!obj_parse_vector3(obj_stream, normals))
40                     return LIBOBJ_FALSE;
41             else if (obj_flag == "g")        //group name
42                 if (!obj_parse_group(obj_stream, groupname))
43                     return LIBOBJ_FALSE;
44             else if (obj_flag == "f")        // face    flag
45             {
46                 vector<ObjVertexIndex> face;
47 
48                 obj_parse_face(obj_stream, face, positions, texcoords, normals);
49 
50                 faces.push_back(face);
51             }
52             else if (obj_flag == "mtllib")    // Material library
53             {
54                 vector<string> materialnames;
55 
56                 obj_parse_mtllib(obj_stream, materialnames);
57 
58                 for (unsigned i = 0; i < materialnames.size(); i++)
59                     if (!mtl_file_load(get_file_path(objname) + '/' + materialnames[i]))
60                         return LIBOBJ_FALSE;
61             }
62             else if (obj_flag == "usemtl")
63             {
64                 if (obj_save_mesh(positions, texcoords, normals, faces, groupname+usemtl, mesh_))
65                 {
66                     m_meshes.back().mtl_idx = mtl_index(usemtl);
67                     
68                     faces.clear();
69                     mesh_.reset();
70                 }
71 
72                 obj_parse_group(obj_stream, usemtl);
73             }
74             else
75             {
76                 obj_stream.ignore(1024, '\n');            // skip line
77             }
78         }  
79 
80         if (!obj_stream.eof())
81             return LIBOBJ_FALSE;
82 
83         //save mesh data
84         if (obj_save_mesh(positions, texcoords, normals, faces, groupname+usemtl, mesh_))
85         {
86             m_meshes.back().mtl_idx = mtl_index(usemtl);
87 
88             faces.clear();
89             mesh_.reset();
90         }
91 
92         obj_stream.close();
93 
94         return LIBOBJ_TRUE;
95     }
View Code

 

obj文件用到了.mtl格式的material,在對“mtllib”標志符解析時開始讀取該.mtl文件,

對於.mtl文件的介紹,請閱讀

obj + mtl 格式

mtl文件格式

MTL文件格式分析

mtl文件的簡要說明

更為詳細的英文資料:

http://www.fileformat.info/format/material/

http://people.cs.clemson.edu/~dhouse/courses/405/docs/brief-mtl-file-format.html

本程序中存儲mtl文件的結構較為簡單,只使用了mtl文件中的幾個結構而沒有全部處理

 1 struct ObjMaterial
 2 {
 3     string name;
 4     
 5     ObjRgb ambient;
 6     ObjRgb diffuse;
 7     ObjRgb specular;
 8 
 9     ObjWord shininess;
10     ObjByte illumination_model;
11     ObjFloat transparency;
12 
13     string ambient_texture;
14     string diffuse_texture;
15     string specular_texture;
16 };
View Code

對mtl文件的解析代碼:

 1     ObjBool mtl_file_load(const string& mtlname)
 2     {
 3         if (mtlname.empty())        // if obj has no material
 4             return LIBOBJ_FALSE;
 5 
 6         ObjMaterial* pmaterial = NULL;
 7 
 8         fstream mtlstream;
 9         mtlstream.open(mtlname.c_str(), std::ios_base::in);    //open strFileName file
10         if (!mtlstream.is_open())
11             return LIBOBJ_FALSE;
12         
13         string mtlflag;
14         while (mtlstream.good())
15         {
16             mtlstream >> mtlflag;
17 
18             if (mtlflag == "newmtl")
19             {
20                 string materialname;
21                 mtlstream >> materialname;
22                 if (mtl_index(materialname) != -1)
23                     continue;
24 
25                 pmaterial = new ObjMaterial;
26                 pmaterial->name = materialname;
27                 m_materials.push_back(pmaterial);
28             }
29             else if (mtlflag == "Ka")        //Ambient color
30                 if (!mtl_parse_color(mtlstream, pmaterial->ambient))
31                     return LIBOBJ_FALSE;
32             else if (mtlflag == "Kd")        //Diffuse color
33                 if (!mtl_parse_color(mtlstream, pmaterial->diffuse))
34                     return LIBOBJ_FALSE;
35             else if (mtlflag == "Ks")        //Specular color
36                 if (!mtl_parse_color(mtlstream, pmaterial->specular))
37                     return LIBOBJ_FALSE;
38             else if (mtlflag == "Tr"/*"d"*/)        // Alpha, that is transparent
39                 if (!mtl_parse_light(mtlstream, pmaterial->transparency))
40                     return LIBOBJ_FALSE;
41             else if (mtlflag == "Ns")        //Shininess
42                 if (!mtl_parse_light(mtlstream, pmaterial->shininess))
43                     return LIBOBJ_FALSE;
44             else if (mtlflag == "illum")        //Illumination type
45                 if (!mtl_parse_light(mtlstream, pmaterial->illumination_model))
46                     return LIBOBJ_FALSE;
47             else if (mtlflag == "map_Ka")    //ambient Texture
48                 if (!mtl_parse_texture(mtlstream, pmaterial->ambient_texture))
49                     return LIBOBJ_FALSE;
50             else if (mtlflag == "map_Kd")    //diffuse Texture
51                 if (!mtl_parse_texture(mtlstream, pmaterial->diffuse_texture))
52                     return LIBOBJ_FALSE;
53             else if (mtlflag == "map_Ks")    //specular Texture
54                 if (!mtl_parse_texture(mtlstream, pmaterial->specular_texture))
55                     return LIBOBJ_FALSE;
56             else
57                 mtlstream.ignore(1024, '\n');    // ignore this line, on the assumption that the max characters at this line is 1024.                
58         }
59 
60         if (!mtlstream.eof())
61             return LIBOBJ_FALSE;
62 
63         mtlstream.close();
64         return LIBOBJ_TRUE;
65     }
View Code

完整的代碼放在了https://github.com/priseup/obj_load

ps: 手上沒有數據,還沒有進行測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM