]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/COBJMeshFileLoader.cpp
11fa484ab3d4e6f251f9ae8d3c3fa972dcaf01cd
[irrlicht.git] / source / Irrlicht / COBJMeshFileLoader.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "IrrCompileConfig.h"\r
6 #include "COBJMeshFileLoader.h"\r
7 #include "IMeshManipulator.h"\r
8 #include "IVideoDriver.h"\r
9 #include "SMesh.h"\r
10 #include "SMeshBuffer.h"\r
11 #include "SAnimatedMesh.h"\r
12 #include "IReadFile.h"\r
13 #include "IAttributes.h"\r
14 #include "fast_atof.h"\r
15 #include "coreutil.h"\r
16 #include "os.h"\r
17 \r
18 namespace irr\r
19 {\r
20 namespace scene\r
21 {\r
22 \r
23 #ifdef _DEBUG\r
24 #define _IRR_DEBUG_OBJ_LOADER_\r
25 #endif\r
26 \r
27 //! Constructor\r
28 COBJMeshFileLoader::COBJMeshFileLoader(scene::ISceneManager* smgr, io::IFileSystem* fs)\r
29 : SceneManager(smgr), FileSystem(fs)\r
30 {\r
31         #ifdef _DEBUG\r
32         setDebugName("COBJMeshFileLoader");\r
33         #endif\r
34 \r
35         if (FileSystem)\r
36                 FileSystem->grab();\r
37 }\r
38 \r
39 \r
40 //! destructor\r
41 COBJMeshFileLoader::~COBJMeshFileLoader()\r
42 {\r
43         if (FileSystem)\r
44                 FileSystem->drop();\r
45 }\r
46 \r
47 \r
48 //! returns true if the file maybe is able to be loaded by this class\r
49 //! based on the file extension (e.g. ".bsp")\r
50 bool COBJMeshFileLoader::isALoadableFileExtension(const io::path& filename) const\r
51 {\r
52         return core::hasFileExtension ( filename, "obj" );\r
53 }\r
54 \r
55 \r
56 //! creates/loads an animated mesh from the file.\r
57 //! \return Pointer to the created mesh. Returns 0 if loading failed.\r
58 //! If you no longer need the mesh, you should call IAnimatedMesh::drop().\r
59 //! See IReferenceCounted::drop() for more information.\r
60 IAnimatedMesh* COBJMeshFileLoader::createMesh(io::IReadFile* file)\r
61 {\r
62         if (!file)\r
63                 return 0;\r
64 \r
65         const long filesize = file->getSize();\r
66         if (!filesize)\r
67                 return 0;\r
68 \r
69         const u32 WORD_BUFFER_LENGTH = 512;\r
70 \r
71         core::array<core::vector3df> vertexBuffer(1000);\r
72         core::array<core::vector3df> normalsBuffer(1000);\r
73         core::array<core::vector2df> textureCoordBuffer(1000);\r
74 \r
75         SObjMtl * currMtl = new SObjMtl();\r
76         Materials.push_back(currMtl);\r
77         u32 smoothingGroup=0;\r
78 \r
79         const io::path fullName = file->getFileName();\r
80         const io::path relPath = FileSystem->getFileDir(fullName)+"/";\r
81 \r
82         c8* buf = new c8[filesize];\r
83         memset(buf, 0, filesize);\r
84         file->read((void*)buf, filesize);\r
85         const c8* const bufEnd = buf+filesize;\r
86 \r
87         // Process obj information\r
88         const c8* bufPtr = buf;\r
89         core::stringc grpName, mtlName;\r
90         bool mtlChanged=false;\r
91         bool useGroups = !SceneManager->getParameters()->getAttributeAsBool(OBJ_LOADER_IGNORE_GROUPS);\r
92         bool useMaterials = !SceneManager->getParameters()->getAttributeAsBool(OBJ_LOADER_IGNORE_MATERIAL_FILES);\r
93         irr::u32 lineNr = 1;    // only counts non-empty lines, still useful in debugging to locate errors\r
94         core::array<int> faceCorners;\r
95         faceCorners.reallocate(32); // should be large enough\r
96         const core::stringc TAG_OFF = "off";\r
97         irr::u32 degeneratedFaces = 0;\r
98 \r
99         while(bufPtr != bufEnd)\r
100         {\r
101                 switch(bufPtr[0])\r
102                 {\r
103                 case 'm':       // mtllib (material)\r
104                 {\r
105                         if (useMaterials)\r
106                         {\r
107                                 c8 name[WORD_BUFFER_LENGTH];\r
108                                 bufPtr = goAndCopyNextWord(name, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
109 #ifdef _IRR_DEBUG_OBJ_LOADER_\r
110                                 os::Printer::log("Reading material file",name);\r
111 #endif\r
112                         }\r
113                 }\r
114                         break;\r
115 \r
116                 case 'v':               // v, vn, vt\r
117                         switch(bufPtr[1])\r
118                         {\r
119                         case ' ':          // vertex\r
120                                 {\r
121                                         core::vector3df vec;\r
122                                         bufPtr = readVec3(bufPtr, vec, bufEnd);\r
123                                         vertexBuffer.push_back(vec);\r
124                                 }\r
125                                 break;\r
126 \r
127                         case 'n':       // normal\r
128                                 {\r
129                                         core::vector3df vec;\r
130                                         bufPtr = readVec3(bufPtr, vec, bufEnd);\r
131                                         normalsBuffer.push_back(vec);\r
132                                 }\r
133                                 break;\r
134 \r
135                         case 't':       // texcoord\r
136                                 {\r
137                                         core::vector2df vec;\r
138                                         bufPtr = readUV(bufPtr, vec, bufEnd);\r
139                                         textureCoordBuffer.push_back(vec);\r
140                                 }\r
141                                 break;\r
142                         }\r
143                         break;\r
144 \r
145                 case 'g': // group name\r
146                         {\r
147                                 c8 grp[WORD_BUFFER_LENGTH];\r
148                                 bufPtr = goAndCopyNextWord(grp, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
149 #ifdef _IRR_DEBUG_OBJ_LOADER_\r
150         os::Printer::log("Loaded group start",grp, ELL_DEBUG);\r
151 #endif\r
152                                 if (useGroups)\r
153                                 {\r
154                                         if (0 != grp[0])\r
155                                                 grpName = grp;\r
156                                         else\r
157                                                 grpName = "default";\r
158                                 }\r
159                                 mtlChanged=true;\r
160                         }\r
161                         break;\r
162 \r
163                 case 's': // smoothing can be a group or off (equiv. to 0)\r
164                         {\r
165                                 c8 smooth[WORD_BUFFER_LENGTH];\r
166                                 bufPtr = goAndCopyNextWord(smooth, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
167 #ifdef _IRR_DEBUG_OBJ_LOADER_\r
168         os::Printer::log("Loaded smoothing group start",smooth, ELL_DEBUG);\r
169 #endif\r
170                                 if (TAG_OFF==smooth)\r
171                                         smoothingGroup=0;\r
172                                 else\r
173                                         smoothingGroup=core::strtoul10(smooth);\r
174 \r
175                                 (void)smoothingGroup; // disable unused variable warnings\r
176                         }\r
177                         break;\r
178 \r
179                 case 'u': // usemtl\r
180                         // get name of material\r
181                         {\r
182                                 c8 matName[WORD_BUFFER_LENGTH];\r
183                                 bufPtr = goAndCopyNextWord(matName, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
184 #ifdef _IRR_DEBUG_OBJ_LOADER_\r
185         os::Printer::log("Loaded material start",matName, ELL_DEBUG);\r
186 #endif\r
187                                 mtlName=matName;\r
188                                 mtlChanged=true;\r
189                         }\r
190                         break;\r
191 \r
192                 case 'f':               // face\r
193                 {\r
194                         c8 vertexWord[WORD_BUFFER_LENGTH]; // for retrieving vertex data\r
195                         video::S3DVertex v;\r
196                         // Assign vertex color from currently active material's diffuse color\r
197                         if (mtlChanged)\r
198                         {\r
199                                 // retrieve the material\r
200                                 SObjMtl *useMtl = findMtl(mtlName, grpName);\r
201                                 // only change material if we found it\r
202                                 if (useMtl)\r
203                                         currMtl = useMtl;\r
204                                 mtlChanged=false;\r
205                         }\r
206                         if (currMtl)\r
207                                 v.Color = currMtl->Meshbuffer->Material.DiffuseColor;\r
208 \r
209                         // get all vertices data in this face (current line of obj file)\r
210                         const core::stringc wordBuffer = copyLine(bufPtr, bufEnd);\r
211                         const c8* linePtr = wordBuffer.c_str();\r
212                         const c8* const endPtr = linePtr+wordBuffer.size();\r
213 \r
214                         faceCorners.set_used(0); // fast clear\r
215 \r
216                         // read in all vertices\r
217                         linePtr = goNextWord(linePtr, endPtr);\r
218                         while (0 != linePtr[0])\r
219                         {\r
220                                 // Array to communicate with retrieveVertexIndices()\r
221                                 // sends the buffer sizes and gets the actual indices\r
222                                 // if index not set returns -1\r
223                                 s32 Idx[3];\r
224                                 Idx[0] = Idx[1] = Idx[2] = -1;\r
225 \r
226                                 // read in next vertex's data\r
227                                 u32 wlength = copyWord(vertexWord, linePtr, WORD_BUFFER_LENGTH, endPtr);\r
228                                 // this function will also convert obj's 1-based index to c++'s 0-based index\r
229                                 retrieveVertexIndices(vertexWord, Idx, vertexWord+wlength+1, vertexBuffer.size(), textureCoordBuffer.size(), normalsBuffer.size());\r
230                                 if ( -1 != Idx[0] && Idx[0] < (irr::s32)vertexBuffer.size() )\r
231                                         v.Pos = vertexBuffer[Idx[0]];\r
232                                 else\r
233                                 {\r
234                                         os::Printer::log("Invalid vertex index in this line:", wordBuffer.c_str(), ELL_ERROR);\r
235                                         delete [] buf;\r
236                                         return 0;\r
237                                 }\r
238                                 if ( -1 != Idx[1] && Idx[1] < (irr::s32)textureCoordBuffer.size() )\r
239                                         v.TCoords = textureCoordBuffer[Idx[1]];\r
240                                 else\r
241                                         v.TCoords.set(0.0f,0.0f);\r
242                                 if ( -1 != Idx[2] && Idx[2] < (irr::s32)normalsBuffer.size() )\r
243                                         v.Normal = normalsBuffer[Idx[2]];\r
244                                 else\r
245                                 {\r
246                                         v.Normal.set(0.0f,0.0f,0.0f);\r
247                                         currMtl->RecalculateNormals=true;\r
248                                 }\r
249 \r
250                                 int vertLocation;\r
251                                 auto n = currMtl->VertMap.find(v);\r
252                                 if (n != currMtl->VertMap.end())\r
253                                 {\r
254                                         vertLocation = n->second;\r
255                                 }\r
256                                 else\r
257                                 {\r
258                                         currMtl->Meshbuffer->Vertices.push_back(v);\r
259                                         vertLocation = currMtl->Meshbuffer->Vertices.size() -1;\r
260                                         currMtl->VertMap.emplace(v, vertLocation);\r
261                                 }\r
262 \r
263                                 faceCorners.push_back(vertLocation);\r
264 \r
265                                 // go to next vertex\r
266                                 linePtr = goNextWord(linePtr, endPtr);\r
267                         }\r
268 \r
269                         // triangulate the face\r
270                         const int c = faceCorners[0];\r
271                         for ( u32 i = 1; i < faceCorners.size() - 1; ++i )\r
272                         {\r
273                                 // Add a triangle\r
274                                 const int a = faceCorners[i + 1];\r
275                                 const int b = faceCorners[i];\r
276                                 if (a != b && a != c && b != c) // ignore degenerated faces. We can get them when we merge vertices above in the VertMap.\r
277                                 {\r
278                                         currMtl->Meshbuffer->Indices.push_back(a);\r
279                                         currMtl->Meshbuffer->Indices.push_back(b);\r
280                                         currMtl->Meshbuffer->Indices.push_back(c);\r
281                                 }\r
282                                 else\r
283                                 {\r
284                                         ++degeneratedFaces;\r
285                                 }\r
286                         }\r
287                 }\r
288                 break;\r
289 \r
290                 case '#': // comment\r
291                 default:\r
292                         break;\r
293                 }       // end switch(bufPtr[0])\r
294                 // eat up rest of line\r
295                 bufPtr = goNextLine(bufPtr, bufEnd);\r
296                 ++lineNr;\r
297         }       // end while(bufPtr && (bufPtr-buf<filesize))\r
298 \r
299         if ( degeneratedFaces > 0 )\r
300         {\r
301                 irr::core::stringc log(degeneratedFaces);\r
302                 log += " degenerated faces removed in ";\r
303                 log += irr::core::stringc(fullName);\r
304                 os::Printer::log(log.c_str(), ELL_INFORMATION);\r
305         }\r
306 \r
307         SMesh* mesh = new SMesh();\r
308 \r
309         // Combine all the groups (meshbuffers) into the mesh\r
310         for ( u32 m = 0; m < Materials.size(); ++m )\r
311         {\r
312                 if ( Materials[m]->Meshbuffer->getIndexCount() > 0 )\r
313                 {\r
314                         Materials[m]->Meshbuffer->recalculateBoundingBox();\r
315                         if (Materials[m]->RecalculateNormals)\r
316                                 SceneManager->getMeshManipulator()->recalculateNormals(Materials[m]->Meshbuffer);\r
317                         mesh->addMeshBuffer( Materials[m]->Meshbuffer );\r
318                 }\r
319         }\r
320 \r
321         // Create the Animated mesh if there's anything in the mesh\r
322         SAnimatedMesh* animMesh = 0;\r
323         if ( 0 != mesh->getMeshBufferCount() )\r
324         {\r
325                 mesh->recalculateBoundingBox();\r
326                 animMesh = new SAnimatedMesh();\r
327                 animMesh->Type = EAMT_OBJ;\r
328                 animMesh->addMesh(mesh);\r
329                 animMesh->recalculateBoundingBox();\r
330         }\r
331 \r
332         // Clean up the allocate obj file contents\r
333         delete [] buf;\r
334         // more cleaning up\r
335         cleanUp();\r
336         mesh->drop();\r
337 \r
338         return animMesh;\r
339 }\r
340 \r
341 //! Read RGB color\r
342 const c8* COBJMeshFileLoader::readColor(const c8* bufPtr, video::SColor& color, const c8* const bufEnd)\r
343 {\r
344         const u32 COLOR_BUFFER_LENGTH = 16;\r
345         c8 colStr[COLOR_BUFFER_LENGTH];\r
346 \r
347         bufPtr = goAndCopyNextWord(colStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);\r
348         color.setRed((u32)core::round32(core::fast_atof(colStr)*255.f));\r
349         bufPtr = goAndCopyNextWord(colStr,   bufPtr, COLOR_BUFFER_LENGTH, bufEnd);\r
350         color.setGreen((u32)core::round32(core::fast_atof(colStr)*255.f));\r
351         bufPtr = goAndCopyNextWord(colStr,   bufPtr, COLOR_BUFFER_LENGTH, bufEnd);\r
352         color.setBlue((u32)core::round32(core::fast_atof(colStr)*255.f));\r
353         return bufPtr;\r
354 }\r
355 \r
356 \r
357 //! Read 3d vector of floats\r
358 const c8* COBJMeshFileLoader::readVec3(const c8* bufPtr, core::vector3df& vec, const c8* const bufEnd)\r
359 {\r
360         const u32 WORD_BUFFER_LENGTH = 256;\r
361         c8 wordBuffer[WORD_BUFFER_LENGTH];\r
362 \r
363         bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
364         vec.X=-core::fast_atof(wordBuffer); // change handedness\r
365         bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
366         vec.Y=core::fast_atof(wordBuffer);\r
367         bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
368         vec.Z=core::fast_atof(wordBuffer);\r
369         return bufPtr;\r
370 }\r
371 \r
372 \r
373 //! Read 2d vector of floats\r
374 const c8* COBJMeshFileLoader::readUV(const c8* bufPtr, core::vector2df& vec, const c8* const bufEnd)\r
375 {\r
376         const u32 WORD_BUFFER_LENGTH = 256;\r
377         c8 wordBuffer[WORD_BUFFER_LENGTH];\r
378 \r
379         bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
380         vec.X=core::fast_atof(wordBuffer);\r
381         bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);\r
382         vec.Y=1-core::fast_atof(wordBuffer); // change handedness\r
383         return bufPtr;\r
384 }\r
385 \r
386 \r
387 //! Read boolean value represented as 'on' or 'off'\r
388 const c8* COBJMeshFileLoader::readBool(const c8* bufPtr, bool& tf, const c8* const bufEnd)\r
389 {\r
390         const u32 BUFFER_LENGTH = 8;\r
391         c8 tfStr[BUFFER_LENGTH];\r
392 \r
393         bufPtr = goAndCopyNextWord(tfStr, bufPtr, BUFFER_LENGTH, bufEnd);\r
394         tf = strcmp(tfStr, "off") != 0;\r
395         return bufPtr;\r
396 }\r
397 \r
398 \r
399 COBJMeshFileLoader::SObjMtl* COBJMeshFileLoader::findMtl(const core::stringc& mtlName, const core::stringc& grpName)\r
400 {\r
401         COBJMeshFileLoader::SObjMtl* defMaterial = 0;\r
402         // search existing Materials for best match\r
403         // exact match does return immediately, only name match means a new group\r
404         for (u32 i = 0; i < Materials.size(); ++i)\r
405         {\r
406                 if ( Materials[i]->Name == mtlName )\r
407                 {\r
408                         if ( Materials[i]->Group == grpName )\r
409                                 return Materials[i];\r
410                         else\r
411                                 defMaterial = Materials[i];\r
412                 }\r
413         }\r
414         // we found a partial match\r
415         if (defMaterial)\r
416         {\r
417                 Materials.push_back(new SObjMtl(*defMaterial));\r
418                 Materials.getLast()->Group = grpName;\r
419                 return Materials.getLast();\r
420         }\r
421         // we found a new group for a non-existant material\r
422         else if (grpName.size())\r
423         {\r
424                 Materials.push_back(new SObjMtl(*Materials[0]));\r
425                 Materials.getLast()->Group = grpName;\r
426                 return Materials.getLast();\r
427         }\r
428         return 0;\r
429 }\r
430 \r
431 \r
432 //! skip space characters and stop on first non-space\r
433 const c8* COBJMeshFileLoader::goFirstWord(const c8* buf, const c8* const bufEnd, bool acrossNewlines)\r
434 {\r
435         // skip space characters\r
436         if (acrossNewlines)\r
437                 while((buf != bufEnd) && core::isspace(*buf))\r
438                         ++buf;\r
439         else\r
440                 while((buf != bufEnd) && core::isspace(*buf) && (*buf != '\n'))\r
441                         ++buf;\r
442 \r
443         return buf;\r
444 }\r
445 \r
446 \r
447 //! skip current word and stop at beginning of next one\r
448 const c8* COBJMeshFileLoader::goNextWord(const c8* buf, const c8* const bufEnd, bool acrossNewlines)\r
449 {\r
450         // skip current word\r
451         while(( buf != bufEnd ) && !core::isspace(*buf))\r
452                 ++buf;\r
453 \r
454         return goFirstWord(buf, bufEnd, acrossNewlines);\r
455 }\r
456 \r
457 \r
458 //! Read until line break is reached and stop at the next non-space character\r
459 const c8* COBJMeshFileLoader::goNextLine(const c8* buf, const c8* const bufEnd)\r
460 {\r
461         // look for newline characters\r
462         while(buf != bufEnd)\r
463         {\r
464                 // found it, so leave\r
465                 if (*buf=='\n' || *buf=='\r')\r
466                         break;\r
467                 ++buf;\r
468         }\r
469         return goFirstWord(buf, bufEnd);\r
470 }\r
471 \r
472 \r
473 u32 COBJMeshFileLoader::copyWord(c8* outBuf, const c8* const inBuf, u32 outBufLength, const c8* const bufEnd)\r
474 {\r
475         if (!outBufLength)\r
476                 return 0;\r
477         if (!inBuf)\r
478         {\r
479                 *outBuf = 0;\r
480                 return 0;\r
481         }\r
482 \r
483         u32 i = 0;\r
484         while(inBuf[i])\r
485         {\r
486                 if (core::isspace(inBuf[i]) || &(inBuf[i]) == bufEnd)\r
487                         break;\r
488                 ++i;\r
489         }\r
490 \r
491         u32 length = core::min_(i, outBufLength-1);\r
492         for (u32 j=0; j<length; ++j)\r
493                 outBuf[j] = inBuf[j];\r
494 \r
495         outBuf[length] = 0;\r
496         return length;\r
497 }\r
498 \r
499 \r
500 core::stringc COBJMeshFileLoader::copyLine(const c8* inBuf, const c8* bufEnd)\r
501 {\r
502         if (!inBuf)\r
503                 return core::stringc();\r
504 \r
505         const c8* ptr = inBuf;\r
506         while (ptr<bufEnd)\r
507         {\r
508                 if (*ptr=='\n' || *ptr=='\r')\r
509                         break;\r
510                 ++ptr;\r
511         }\r
512         // we must avoid the +1 in case the array is used up\r
513         return core::stringc(inBuf, (u32)(ptr-inBuf+((ptr < bufEnd) ? 1 : 0)));\r
514 }\r
515 \r
516 \r
517 const c8* COBJMeshFileLoader::goAndCopyNextWord(c8* outBuf, const c8* inBuf, u32 outBufLength, const c8* bufEnd)\r
518 {\r
519         inBuf = goNextWord(inBuf, bufEnd, false);\r
520         copyWord(outBuf, inBuf, outBufLength, bufEnd);\r
521         return inBuf;\r
522 }\r
523 \r
524 \r
525 bool COBJMeshFileLoader::retrieveVertexIndices(c8* vertexData, s32* idx, const c8* bufEnd, u32 vbsize, u32 vtsize, u32 vnsize)\r
526 {\r
527         const u32 BUFFER_LENGTH = 16;\r
528         c8 word[BUFFER_LENGTH];\r
529         const c8* p = goFirstWord(vertexData, bufEnd);\r
530         u32 idxType = 0;        // 0 = posIdx, 1 = texcoordIdx, 2 = normalIdx\r
531 \r
532         u32 i = 0;\r
533         while ( p != bufEnd )\r
534         {\r
535                 if ( i >= BUFFER_LENGTH )\r
536                 {\r
537                         return false;\r
538                 }\r
539                 if ( ( core::isdigit(*p)) || (*p == '-') )\r
540                 {\r
541                         // build up the number\r
542                         word[i++] = *p;\r
543                 }\r
544                 else if ( *p == '/' || *p == ' ' || *p == '\0' )\r
545                 {\r
546                         // number is completed. Convert and store it\r
547                         word[i] = '\0';\r
548                         // if no number was found index will become 0 and later on -1 by decrement\r
549                         idx[idxType] = core::strtol10(word);\r
550                         if (idx[idxType]<0)\r
551                         {\r
552                                 switch (idxType)\r
553                                 {\r
554                                         case 0:\r
555                                                 idx[idxType] += vbsize;\r
556                                                 break;\r
557                                         case 1:\r
558                                                 idx[idxType] += vtsize;\r
559                                                 break;\r
560                                         case 2:\r
561                                                 idx[idxType] += vnsize;\r
562                                                 break;\r
563                                 }\r
564                         }\r
565                         else\r
566                                 idx[idxType]-=1;\r
567 \r
568                         // reset the word\r
569                         word[0] = '\0';\r
570                         i = 0;\r
571 \r
572                         // go to the next kind of index type\r
573                         if (*p == '/')\r
574                         {\r
575                                 if ( ++idxType > 2 )\r
576                                 {\r
577                                         // error checking, shouldn't reach here unless file is wrong\r
578                                         idxType = 0;\r
579                                 }\r
580                         }\r
581                         else\r
582                         {\r
583                                 // set all missing values to disable (=-1)\r
584                                 while (++idxType < 3)\r
585                                         idx[idxType]=-1;\r
586                                 ++p;\r
587                                 break; // while\r
588                         }\r
589                 }\r
590 \r
591                 // go to the next char\r
592                 ++p;\r
593         }\r
594 \r
595         return true;\r
596 }\r
597 \r
598 \r
599 void COBJMeshFileLoader::cleanUp()\r
600 {\r
601         for (u32 i=0; i < Materials.size(); ++i )\r
602         {\r
603                 Materials[i]->Meshbuffer->drop();\r
604                 delete Materials[i];\r
605         }\r
606 \r
607         Materials.clear();\r
608 }\r
609 \r
610 \r
611 } // end namespace scene\r
612 } // end namespace irr\r