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