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