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