OSDN Git Service

OBJ読み込み処理速度改善
[qtgeoviewer/QtGeoViewer.git] / Src / LibQtGeoViewerCore / Format / Obj / ObjMeshIO.cpp
1 #include "stdafx.h"
2 #include "ObjMesh.h"
3 #include "ObjMeshIO.h"
4 #include "ObjMaterialReader.h"
5 #include "IOUtility.h"
6
7 #include <vector>
8 #include <vector>
9 #include <map>
10 #include <string>
11
12 #include <fstream>
13 #include <sstream>
14
15 #include <C2/util/container_cast.h>
16 #include <C2/util/string_util.h>
17
18 #include <LibGeo/Mesh/BaseMesh.h>
19 #include <LibGeo/Path.h>
20 #include <LibGeo/Utility/StringRef.h>
21
22 #include <shlwapi.h>
23
24 using namespace std;
25
26
27
28 namespace lib_geo
29 {
30
31
32 inline float ReadNextFloat(istream& ist)
33 {
34         string s;
35         ist >> s;
36         return (float)atof(s.c_str());
37 }
38
39 inline void ReadVec(istream& ist, lm::vec2f& v)
40 {
41         v.x = ReadNextFloat(ist);
42         v.y = ReadNextFloat(ist);
43 }
44
45 inline void ReadVec(istream& ist, lm::vec3f& v)
46 {
47         v.x = ReadNextFloat(ist);
48         v.y = ReadNextFloat(ist);
49         v.z = ReadNextFloat(ist);
50 }
51
52
53 // \83t\83@\83C\83\8b\93Ç\8d\9e
54 bool ObjMeshReader::Load( ObjMesh& mesh , const string& filename )
55 {
56         InitializeWorkBuffer();
57
58         m_LoadWorkDirPath = Path::GetParentDirPath(filename);
59
60         ifstream ifs( filename.c_str() );
61         if( !ifs.is_open() )
62                 return false;
63
64         try
65         {
66                 if( !LoadObjMain( mesh , ifs , true ) )
67                         return false;
68         }
69         catch(...)
70         {
71                 return false;
72         }
73
74         return true;
75 }
76
77 //! \83X\83g\83\8a\81[\83\80\82©\82ç\82Ì\93Ç\82Ý\8d\9e\82Ý
78 //! \83}\83e\83\8a\83A\83\8b\83t\83@\83C\83\8b\82Í\93Ç\82Ý\8d\9e\82Ü\82È\82¢
79 bool ObjMeshReader::LoadStream( ObjMesh& mesh , istream& ist )
80 {
81         return LoadObjMain( mesh , ist , false );
82 }
83
84 bool ObjMeshReader::LoadObjMain( ObjMesh& mesh , istream& ist , bool LoadMaterialFile )
85 {
86         for(;;)
87         {
88                 if( ist.eof() )
89                         break;
90
91                 string s;
92                 ist >> s;
93                 if(s.empty())
94                         continue;
95
96                 if( s[0] == 'v' )
97                 {
98                         if(s.size() == 1)
99                         {
100                                 ReadVertLine( ist , mesh.m_Verts );
101                         }
102                         else if( s[1] == 't' )
103                         {
104                                 ReadUVLine( ist , mesh.m_UVs );
105                         }
106                         else if( s[1] == 'n' )
107                         {
108                                 ReadNormLine( ist , mesh.m_Normals );
109                         }
110                 }
111                 else if( s == "f" )
112                 {
113                         ReadFaceLine( ist , mesh );
114                 }
115                 else if( s == "o" )
116                 {
117                         ReadObjectSplitLine( ist , mesh );
118                 }
119                 else if( s == "g" )
120                 {
121                         ReadGroupSplitLine( ist , mesh );
122                 }
123                 else if( s == "usemtl" )
124                 {
125                         ReadMaterialSelectLine( ist , mesh );
126                 }
127                 else if( s == "mtllib" )
128                 {
129                         if( LoadMaterialFile )
130                         {
131                                 ReadMaterialGroup( ist , mesh );
132                         }
133                         else
134                         {
135                                 ReadToNextLine( ist );
136                         }
137                 }
138                 else if( s == "l" )
139                 {
140                         ReadPolylineLine( ist , mesh.m_Polylines );
141                 }
142                 else
143                 {
144                         ReadToNextLine( ist );
145                 }
146         }
147
148         if (!mesh.m_Objects.empty())
149                 SetLastObjectVertNum(mesh);
150
151         return true;
152 }
153
154 void ObjMeshReader::InitializeWorkBuffer(void)
155 {
156         m_MatIdxMap.clear();
157         m_PrimaryMatIdx = -1;
158         
159         m_GroupIdxMap.clear();
160         m_PrimaryGroupIdx = 0;
161
162         m_LoadWorkDirPath.clear();
163 }
164
165 void ObjMeshReader::ReadVertLine( istream& ist , vector<lm::vec3f>& verts )
166 {
167         lm::vec3f vert;
168         ReadVec(ist, vert);
169         verts.push_back(vert);
170 }
171
172 void ObjMeshReader::ReadNormLine( istream& ist , vector<lm::vec3f>& normals )
173 {
174         lm::vec3f norm;
175         ReadVec(ist, norm);
176         normals.push_back(norm);
177 }
178
179 void ObjMeshReader::ReadUVLine( istream& ist , vector<lm::vec2f>& uvs )
180 {
181         string s;
182         getline( ist , s );
183         istringstream iss( s.c_str() );
184
185         lm::vec2f uv;
186         ReadVec(iss, uv);
187         uvs.push_back(uv);
188 }
189
190 void ToRefAry(std::string& s, std::vector<util::StringRef>& vs)
191 {
192         vs.reserve(3);
193
194         bool IsInWord = false;
195         char* str_begin = NULL;
196         for(size_t i = 0; i < s.size(); ++i)
197         {
198                 char* c = &s[i];
199
200                 bool IsNoChar = ((*c) == ' ' || (*c) == '\t' || (*c) == '\n');
201                 if(IsInWord)
202                 {
203                         if(IsNoChar)
204                         {
205                                 vs.push_back(util::StringRef(str_begin, c));
206                                 IsInWord = false;
207                         }
208                 }
209                 else
210                 {
211                         if(!IsNoChar)
212                         {
213                                 str_begin = c;
214                                 IsInWord = true;
215                         }
216                 }
217         }
218
219         if(IsInWord)
220         {
221                 char* str_last = &s[s.size()-1];
222                 ++str_last;
223                 vs.push_back(util::StringRef(str_begin, str_last));
224         }
225 }
226
227 void ObjMeshReader::ReadFaceLine( istream& ist , ObjMesh& mesh )
228 {
229         vector<ObjFace>& faces = mesh.m_Faces;
230
231         faces.push_back(ObjFace());
232         ObjFace& face = faces.back();
233
234         string sl;
235         getline( ist , sl );
236         sl.push_back(' ');
237
238         std::vector<util::StringRef> vs;
239         ToRefAry(sl, vs);
240
241         for (size_t i = 0; i < vs.size(); ++i)
242         {
243                 size_t slen = vs[i].GetLength();
244                 char* s = vs[i].GetBegin();
245                 if( !(s[0] >= '1' && s[0] <= '9') && s[0] != '-' )
246                         break;
247
248                 int cnt_val = 1;
249                 char* str_top[3];
250                 str_top[0] = &s[0];
251                 for (size_t j = 0; j < slen; j++)
252                 {
253                         if( s[j] == '/' )
254                         {
255                                 str_top[cnt_val] = &s[j+1];
256                                 s[j] = '\0';
257                                 cnt_val++;
258                         }
259                 }
260
261                 {
262                         int idx = atoi( str_top[0] );
263                         if (idx < 0)
264                                 idx = (int)mesh.m_Verts.size() + idx + 1;
265
266                         face.m_IdxV.Add(mesh.m_FaceBuf.m_FaceIdxV, idx - 1);
267                 }
268
269                 if( cnt_val >= 2 && str_top[1][0] != '\0' )
270                 {
271                         int idx = atoi( str_top[1] );
272                         if (idx < 0)
273                                 idx = (int)mesh.m_UVs.size() + idx + 1;
274
275                         face.m_IdxUV.Add(mesh.m_FaceBuf.m_FaceIdxUV, idx - 1);
276                 }
277
278                 if( cnt_val >= 3 )
279                 {
280                         int idx = atoi( str_top[2] );
281                         if (idx < 0)
282                                 idx = (int)mesh.m_Normals.size() + idx + 1;
283
284                         face.m_IdxN.Add(mesh.m_FaceBuf.m_FaceIdxN, idx - 1);
285                 }
286         }
287
288         face.m_GroupIdx = m_PrimaryGroupIdx;
289         face.m_MatNameIdx = m_PrimaryMatIdx;
290 }
291
292 void ObjMeshReader::ReadPolylineLine( std::istream& ist , std::vector<ObjPolyline>& lines )
293 {
294         lines.push_back(ObjPolyline());
295         ObjPolyline& line = lines.back();
296
297         string sl;
298         for(;;)
299         {
300                 string s;
301                 getline( ist , s );
302                 if(!CheckAndModifyContinueToNextLine(s))
303                 {
304                         sl += s;
305                         break;
306                 }
307                 else
308                 {
309                         sl += s;
310                 }
311         }
312         sl.push_back(' ');
313
314         std::vector<util::StringRef> vs;
315         ToRefAry(sl, vs);
316
317         line.m_IdxV.resize(vs.size());
318         for(size_t i = 0; i < vs.size(); ++i)
319         {
320                 char* s = vs[i].GetBegin();
321                 char* e = vs[i].GetEnd();
322                 (*e) = '\0';
323                 line.m_IdxV[i] = atoi( s ) - 1;
324         }
325
326         line.m_GroupIdx = m_PrimaryGroupIdx;
327 }
328
329 bool ObjMeshReader::CheckAndModifyContinueToNextLine(std::string& s) const
330 {
331         size_t last_char = 0;
332         for(size_t i = s.size(); i > 0; --i)
333         {
334                 char c = s[i];
335                 if(c == ' ' || c == '\t')
336                         continue;
337
338                 last_char = i - 1;
339                 break;
340         }
341
342         if(last_char == 0)
343                 return false;
344
345         if(s[last_char] != '\\')
346                 return false;
347
348         s.resize(last_char);
349         return true;
350 }
351
352 void ObjMeshReader::ReadObjectSplitLine( std::istream& ist , ObjMesh& mesh )
353 {
354         if (!mesh.m_Objects.empty())
355                 SetLastObjectVertNum(mesh);
356
357         SubObject new_object;
358         new_object.m_VertRange.Offset = (int)mesh.m_Verts.size();
359         new_object.m_NormRange.Offset = (int)mesh.m_Normals.size();
360         new_object.m_UVRange.Offset   = (int)mesh.m_UVs.size();
361         new_object.m_FaceRange.Offset = (int)mesh.m_Faces.size();
362
363         GetLineWithTrimed( ist , new_object.m_Name );
364
365         mesh.m_Objects.push_back( new_object );
366 }
367
368 void ObjMeshReader::ReadGroupSplitLine( istream& ist , ObjMesh& mesh )
369 {
370         string grp_name;
371         GetLineWithTrimed( ist , grp_name );
372
373         map<string, int>::iterator found = m_GroupIdxMap.find(grp_name);
374         if( found != m_GroupIdxMap.end() )
375         {
376                 m_PrimaryGroupIdx = found->second;
377         }
378         else
379         {
380                 int new_idx = static_cast<int>( mesh.m_Groups.size() );
381                 mesh.m_Groups.push_back( ObjGroupInfo() );
382                 mesh.m_Groups.back().m_Name = grp_name;
383
384                 m_GroupIdxMap.insert( pair<string, int>( grp_name , new_idx ) );
385                 m_PrimaryGroupIdx = new_idx;
386         }
387 }
388
389 void ObjMeshReader::ReadMaterialSelectLine( istream& ist , ObjMesh& mesh )
390 {
391         string mat_name;
392         GetLineWithTrimed( ist , mat_name );
393         
394         map<string, int>::iterator found = m_MatIdxMap.find(mat_name);
395         if( found != m_MatIdxMap.end() )
396         {
397                 m_PrimaryMatIdx = found->second;
398         }
399         else
400         {
401                 int new_idx = static_cast<int>( m_MatIdxMap.size() );
402                 m_MatIdxMap[mat_name] = new_idx;
403                 m_PrimaryMatIdx = new_idx;
404
405                 mesh.m_MaterialNames.push_back( mat_name );
406         }
407 }
408
409 void ObjMeshReader::ReadMaterialGroup( istream& ist , ObjMesh& mesh )
410 {
411         string mat_filename;
412         GetLineWithTrimed( ist , mat_filename );
413
414         ObjMaterialGroup materials;
415         ObjMaterialReader reader(m_LoadWorkDirPath);
416         if(!reader.ReadMaterialFile(mat_filename, materials))
417                 return;
418
419         mesh.m_MaterialGroups.push_back( materials );
420 }
421
422
423 bool ObjMeshWriter::Save( const ObjMesh& mesh , const string& filename ) const
424 {
425         ofstream ofs( filename.c_str() );
426         if( !ofs.is_open() )
427                 return false;
428
429         return SaveObjMain( mesh , ofs );
430 }
431
432 bool ObjMeshWriter::SaveStream( const ObjMesh& mesh , ostream& ost ) const
433 {
434         return SaveObjMain( mesh , ost );
435 }
436
437 bool ObjMeshWriter::SaveObjMain( const ObjMesh& mesh , ostream& ost ) const
438 {
439         // \92¸\93_\83o\83b\83t\83@
440         for (const lm::vec3f& v : mesh.m_Verts)
441         {
442                 ost << "v " << v.x << " " << v.y << " " << v.z << endl;
443         }
444
445         // UV\83o\83b\83t\83@
446         for(size_t i = 0; i < mesh.m_UVs.size(); ++i)
447         {
448                 const lm::vec2f& uv = mesh.m_UVs[i];
449                 ost << "vt " << uv.x << " " << uv.y << endl;
450         }
451
452         // \96@\90ü\83o\83b\83t\83@
453         for(size_t i = 0; i < mesh.m_Normals.size(); ++i)
454         {
455                 const lm::vec3f& normal = mesh.m_Normals[i];
456                 ost << "vn " << normal.x << " " << normal.y << " " << normal.z << endl;
457         }
458
459         for(size_t i = 0 ; i < mesh.m_Faces.size(); ++i)
460         {
461                 const ObjFace& face = mesh.m_Faces[i];
462                 if (face.m_IdxV.IsEmpty())
463                         continue;
464
465                 bool has_uv = !face.m_IdxUV.IsEmpty();
466                 bool has_n  = !face.m_IdxN.IsEmpty();
467
468                 ost << "f";
469                 for (size_t j = 0; j < face.m_IdxV.m_Count; ++i)
470                 {
471                         int vid, uvid, nid;
472
473                         vid = mesh.m_FaceBuf.m_FaceIdxV[face.m_IdxV.m_Offset + j];
474
475                         if( has_uv )
476                                 uvid = mesh.m_FaceBuf.m_FaceIdxUV[face.m_IdxUV.m_Offset + j];
477
478                         if( has_n )
479                                 nid = mesh.m_FaceBuf.m_FaceIdxN[face.m_IdxN.m_Offset + j];
480
481                         if( !has_uv && !has_n )
482                                 ost << " " << vid+1;
483                         else if( has_uv )
484                                 ost << " " << vid+1 << "/" << uvid+1;
485                         else if( has_n )
486                                 ost << " " << vid+1 << "//" << nid+1;
487                         else
488                                 ost << " " << vid+1 << "/" << uvid+1 << "/" << nid+1;
489                 }
490                 ost << endl;
491         }
492
493         return true;
494 }
495
496
497 void ObjMeshReader::SetLastObjectVertNum(ObjMesh& mesh)
498 {
499         SubObject& last_obj = mesh.m_Objects.back();
500         last_obj.m_VertRange.NumElems = (int)mesh.m_Verts.size()   - last_obj.m_VertRange.Offset;
501         last_obj.m_NormRange.NumElems = (int)mesh.m_Normals.size() - last_obj.m_NormRange.Offset;
502         last_obj.m_UVRange.NumElems   = (int)mesh.m_UVs.size()     - last_obj.m_UVRange.Offset;
503         last_obj.m_FaceRange.NumElems = (int)mesh.m_Faces.size()   - last_obj.m_FaceRange.Offset;
504 }
505
506
507 }