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