aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/others/irrlicht-1.8.1/source/Irrlicht/COBJMeshFileLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/others/irrlicht-1.8.1/source/Irrlicht/COBJMeshFileLoader.cpp')
-rw-r--r--src/others/irrlicht-1.8.1/source/Irrlicht/COBJMeshFileLoader.cpp931
1 files changed, 931 insertions, 0 deletions
diff --git a/src/others/irrlicht-1.8.1/source/Irrlicht/COBJMeshFileLoader.cpp b/src/others/irrlicht-1.8.1/source/Irrlicht/COBJMeshFileLoader.cpp
new file mode 100644
index 0000000..47a4dad
--- /dev/null
+++ b/src/others/irrlicht-1.8.1/source/Irrlicht/COBJMeshFileLoader.cpp
@@ -0,0 +1,931 @@
1// Copyright (C) 2002-2012 Nikolaus Gebhardt
2// This file is part of the "Irrlicht Engine".
3// For conditions of distribution and use, see copyright notice in irrlicht.h
4
5#include "IrrCompileConfig.h"
6#ifdef _IRR_COMPILE_WITH_OBJ_LOADER_
7
8#include "COBJMeshFileLoader.h"
9#include "IMeshManipulator.h"
10#include "IVideoDriver.h"
11#include "SMesh.h"
12#include "SMeshBuffer.h"
13#include "SAnimatedMesh.h"
14#include "IReadFile.h"
15#include "IAttributes.h"
16#include "fast_atof.h"
17#include "coreutil.h"
18#include "os.h"
19
20namespace irr
21{
22namespace scene
23{
24
25#ifdef _DEBUG
26#define _IRR_DEBUG_OBJ_LOADER_
27#endif
28
29static const u32 WORD_BUFFER_LENGTH = 512;
30
31//! Constructor
32COBJMeshFileLoader::COBJMeshFileLoader(scene::ISceneManager* smgr, io::IFileSystem* fs)
33: SceneManager(smgr), FileSystem(fs)
34{
35 #ifdef _DEBUG
36 setDebugName("COBJMeshFileLoader");
37 #endif
38
39 if (FileSystem)
40 FileSystem->grab();
41}
42
43
44//! destructor
45COBJMeshFileLoader::~COBJMeshFileLoader()
46{
47 if (FileSystem)
48 FileSystem->drop();
49}
50
51
52//! returns true if the file maybe is able to be loaded by this class
53//! based on the file extension (e.g. ".bsp")
54bool COBJMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
55{
56 return core::hasFileExtension ( filename, "obj" );
57}
58
59
60//! creates/loads an animated mesh from the file.
61//! \return Pointer to the created mesh. Returns 0 if loading failed.
62//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
63//! See IReferenceCounted::drop() for more information.
64IAnimatedMesh* COBJMeshFileLoader::createMesh(io::IReadFile* file)
65{
66 const long filesize = file->getSize();
67 if (!filesize)
68 return 0;
69
70 const u32 WORD_BUFFER_LENGTH = 512;
71
72 core::array<core::vector3df> vertexBuffer;
73 core::array<core::vector3df> normalsBuffer;
74 core::array<core::vector2df> textureCoordBuffer;
75
76 SObjMtl * currMtl = new SObjMtl();
77 Materials.push_back(currMtl);
78 u32 smoothingGroup=0;
79
80 const io::path fullName = file->getFileName();
81 const io::path relPath = FileSystem->getFileDir(fullName)+"/";
82
83 c8* buf = new c8[filesize];
84 memset(buf, 0, filesize);
85 file->read((void*)buf, filesize);
86 const c8* const bufEnd = buf+filesize;
87
88 // Process obj information
89 const c8* bufPtr = buf;
90 core::stringc grpName, mtlName;
91 bool mtlChanged=false;
92 bool useGroups = !SceneManager->getParameters()->getAttributeAsBool(OBJ_LOADER_IGNORE_GROUPS);
93 bool useMaterials = !SceneManager->getParameters()->getAttributeAsBool(OBJ_LOADER_IGNORE_MATERIAL_FILES);
94 while(bufPtr != bufEnd)
95 {
96 switch(bufPtr[0])
97 {
98 case 'm': // mtllib (material)
99 {
100 if (useMaterials)
101 {
102 c8 name[WORD_BUFFER_LENGTH];
103 bufPtr = goAndCopyNextWord(name, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
104#ifdef _IRR_DEBUG_OBJ_LOADER_
105 os::Printer::log("Reading material file",name);
106#endif
107 readMTL(name, relPath);
108 }
109 }
110 break;
111
112 case 'v': // v, vn, vt
113 switch(bufPtr[1])
114 {
115 case ' ': // vertex
116 {
117 core::vector3df vec;
118 bufPtr = readVec3(bufPtr, vec, bufEnd);
119 vertexBuffer.push_back(vec);
120 }
121 break;
122
123 case 'n': // normal
124 {
125 core::vector3df vec;
126 bufPtr = readVec3(bufPtr, vec, bufEnd);
127 normalsBuffer.push_back(vec);
128 }
129 break;
130
131 case 't': // texcoord
132 {
133 core::vector2df vec;
134 bufPtr = readUV(bufPtr, vec, bufEnd);
135 textureCoordBuffer.push_back(vec);
136 }
137 break;
138 }
139 break;
140
141 case 'g': // group name
142 {
143 c8 grp[WORD_BUFFER_LENGTH];
144 bufPtr = goAndCopyNextWord(grp, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
145#ifdef _IRR_DEBUG_OBJ_LOADER_
146 os::Printer::log("Loaded group start",grp, ELL_DEBUG);
147#endif
148 if (useGroups)
149 {
150 if (0 != grp[0])
151 grpName = grp;
152 else
153 grpName = "default";
154 }
155 mtlChanged=true;
156 }
157 break;
158
159 case 's': // smoothing can be a group or off (equiv. to 0)
160 {
161 c8 smooth[WORD_BUFFER_LENGTH];
162 bufPtr = goAndCopyNextWord(smooth, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
163#ifdef _IRR_DEBUG_OBJ_LOADER_
164 os::Printer::log("Loaded smoothing group start",smooth, ELL_DEBUG);
165#endif
166 if (core::stringc("off")==smooth)
167 smoothingGroup=0;
168 else
169 smoothingGroup=core::strtoul10(smooth);
170 }
171 break;
172
173 case 'u': // usemtl
174 // get name of material
175 {
176 c8 matName[WORD_BUFFER_LENGTH];
177 bufPtr = goAndCopyNextWord(matName, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
178#ifdef _IRR_DEBUG_OBJ_LOADER_
179 os::Printer::log("Loaded material start",matName, ELL_DEBUG);
180#endif
181 mtlName=matName;
182 mtlChanged=true;
183 }
184 break;
185
186 case 'f': // face
187 {
188 c8 vertexWord[WORD_BUFFER_LENGTH]; // for retrieving vertex data
189 video::S3DVertex v;
190 // Assign vertex color from currently active material's diffuse color
191 if (mtlChanged)
192 {
193 // retrieve the material
194 SObjMtl *useMtl = findMtl(mtlName, grpName);
195 // only change material if we found it
196 if (useMtl)
197 currMtl = useMtl;
198 mtlChanged=false;
199 }
200 if (currMtl)
201 v.Color = currMtl->Meshbuffer->Material.DiffuseColor;
202
203 // get all vertices data in this face (current line of obj file)
204 const core::stringc wordBuffer = copyLine(bufPtr, bufEnd);
205 const c8* linePtr = wordBuffer.c_str();
206 const c8* const endPtr = linePtr+wordBuffer.size();
207
208 core::array<int> faceCorners;
209 faceCorners.reallocate(32); // should be large enough
210
211 // read in all vertices
212 linePtr = goNextWord(linePtr, endPtr);
213 while (0 != linePtr[0])
214 {
215 // Array to communicate with retrieveVertexIndices()
216 // sends the buffer sizes and gets the actual indices
217 // if index not set returns -1
218 s32 Idx[3];
219 Idx[1] = Idx[2] = -1;
220
221 // read in next vertex's data
222 u32 wlength = copyWord(vertexWord, linePtr, WORD_BUFFER_LENGTH, endPtr);
223 // this function will also convert obj's 1-based index to c++'s 0-based index
224 retrieveVertexIndices(vertexWord, Idx, vertexWord+wlength+1, vertexBuffer.size(), textureCoordBuffer.size(), normalsBuffer.size());
225 v.Pos = vertexBuffer[Idx[0]];
226 if ( -1 != Idx[1] )
227 v.TCoords = textureCoordBuffer[Idx[1]];
228 else
229 v.TCoords.set(0.0f,0.0f);
230 if ( -1 != Idx[2] )
231 v.Normal = normalsBuffer[Idx[2]];
232 else
233 {
234 v.Normal.set(0.0f,0.0f,0.0f);
235 currMtl->RecalculateNormals=true;
236 }
237
238 int vertLocation;
239 core::map<video::S3DVertex, int>::Node* n = currMtl->VertMap.find(v);
240 if (n)
241 {
242 vertLocation = n->getValue();
243 }
244 else
245 {
246 currMtl->Meshbuffer->Vertices.push_back(v);
247 vertLocation = currMtl->Meshbuffer->Vertices.size() -1;
248 currMtl->VertMap.insert(v, vertLocation);
249 }
250
251 faceCorners.push_back(vertLocation);
252
253 // go to next vertex
254 linePtr = goNextWord(linePtr, endPtr);
255 }
256
257 // triangulate the face
258 for ( u32 i = 1; i < faceCorners.size() - 1; ++i )
259 {
260 // Add a triangle
261 currMtl->Meshbuffer->Indices.push_back( faceCorners[i+1] );
262 currMtl->Meshbuffer->Indices.push_back( faceCorners[i] );
263 currMtl->Meshbuffer->Indices.push_back( faceCorners[0] );
264 }
265 faceCorners.set_used(0); // fast clear
266 faceCorners.reallocate(32);
267 }
268 break;
269
270 case '#': // comment
271 default:
272 break;
273 } // end switch(bufPtr[0])
274 // eat up rest of line
275 bufPtr = goNextLine(bufPtr, bufEnd);
276 } // end while(bufPtr && (bufPtr-buf<filesize))
277
278 SMesh* mesh = new SMesh();
279
280 // Combine all the groups (meshbuffers) into the mesh
281 for ( u32 m = 0; m < Materials.size(); ++m )
282 {
283 if ( Materials[m]->Meshbuffer->getIndexCount() > 0 )
284 {
285 Materials[m]->Meshbuffer->recalculateBoundingBox();
286 if (Materials[m]->RecalculateNormals)
287 SceneManager->getMeshManipulator()->recalculateNormals(Materials[m]->Meshbuffer);
288 if (Materials[m]->Meshbuffer->Material.MaterialType == video::EMT_PARALLAX_MAP_SOLID)
289 {
290 SMesh tmp;
291 tmp.addMeshBuffer(Materials[m]->Meshbuffer);
292 IMesh* tangentMesh = SceneManager->getMeshManipulator()->createMeshWithTangents(&tmp);
293 mesh->addMeshBuffer(tangentMesh->getMeshBuffer(0));
294 tangentMesh->drop();
295 }
296 else
297 mesh->addMeshBuffer( Materials[m]->Meshbuffer );
298 }
299 }
300
301 // Create the Animated mesh if there's anything in the mesh
302 SAnimatedMesh* animMesh = 0;
303 if ( 0 != mesh->getMeshBufferCount() )
304 {
305 mesh->recalculateBoundingBox();
306 animMesh = new SAnimatedMesh();
307 animMesh->Type = EAMT_OBJ;
308 animMesh->addMesh(mesh);
309 animMesh->recalculateBoundingBox();
310 }
311
312 // Clean up the allocate obj file contents
313 delete [] buf;
314 // more cleaning up
315 cleanUp();
316 mesh->drop();
317
318 return animMesh;
319}
320
321
322const c8* COBJMeshFileLoader::readTextures(const c8* bufPtr, const c8* const bufEnd, SObjMtl* currMaterial, const io::path& relPath)
323{
324 u8 type=0; // map_Kd - diffuse color texture map
325 // map_Ks - specular color texture map
326 // map_Ka - ambient color texture map
327 // map_Ns - shininess texture map
328 if ((!strncmp(bufPtr,"map_bump",8)) || (!strncmp(bufPtr,"bump",4)))
329 type=1; // normal map
330 else if ((!strncmp(bufPtr,"map_d",5)) || (!strncmp(bufPtr,"map_opacity",11)))
331 type=2; // opacity map
332 else if (!strncmp(bufPtr,"map_refl",8))
333 type=3; // reflection map
334 // extract new material's name
335 c8 textureNameBuf[WORD_BUFFER_LENGTH];
336 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
337
338 f32 bumpiness = 6.0f;
339 bool clamp = false;
340 // handle options
341 while (textureNameBuf[0]=='-')
342 {
343 if (!strncmp(bufPtr,"-bm",3))
344 {
345 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
346 currMaterial->Meshbuffer->Material.MaterialTypeParam=core::fast_atof(textureNameBuf);
347 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
348 continue;
349 }
350 else
351 if (!strncmp(bufPtr,"-blendu",7))
352 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
353 else
354 if (!strncmp(bufPtr,"-blendv",7))
355 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
356 else
357 if (!strncmp(bufPtr,"-cc",3))
358 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
359 else
360 if (!strncmp(bufPtr,"-clamp",6))
361 bufPtr = readBool(bufPtr, clamp, bufEnd);
362 else
363 if (!strncmp(bufPtr,"-texres",7))
364 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
365 else
366 if (!strncmp(bufPtr,"-type",5))
367 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
368 else
369 if (!strncmp(bufPtr,"-mm",3))
370 {
371 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
372 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
373 }
374 else
375 if (!strncmp(bufPtr,"-o",2)) // texture coord translation
376 {
377 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
378 // next parameters are optional, so skip rest of loop if no number is found
379 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
380 if (!core::isdigit(textureNameBuf[0]))
381 continue;
382 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
383 if (!core::isdigit(textureNameBuf[0]))
384 continue;
385 }
386 else
387 if (!strncmp(bufPtr,"-s",2)) // texture coord scale
388 {
389 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
390 // next parameters are optional, so skip rest of loop if no number is found
391 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
392 if (!core::isdigit(textureNameBuf[0]))
393 continue;
394 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
395 if (!core::isdigit(textureNameBuf[0]))
396 continue;
397 }
398 else
399 if (!strncmp(bufPtr,"-t",2))
400 {
401 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
402 // next parameters are optional, so skip rest of loop if no number is found
403 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
404 if (!core::isdigit(textureNameBuf[0]))
405 continue;
406 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
407 if (!core::isdigit(textureNameBuf[0]))
408 continue;
409 }
410 // get next word
411 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
412 }
413
414 if ((type==1) && (core::isdigit(textureNameBuf[0])))
415 {
416 currMaterial->Meshbuffer->Material.MaterialTypeParam=core::fast_atof(textureNameBuf);
417 bufPtr = goAndCopyNextWord(textureNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
418 }
419 if (clamp)
420 currMaterial->Meshbuffer->Material.setFlag(video::EMF_TEXTURE_WRAP, video::ETC_CLAMP);
421
422 io::path texname(textureNameBuf);
423 texname.replace('\\', '/');
424
425 video::ITexture * texture = 0;
426 bool newTexture=false;
427 if (texname.size())
428 {
429 io::path texnameWithUserPath( SceneManager->getParameters()->getAttributeAsString(OBJ_TEXTURE_PATH) );
430 if ( texnameWithUserPath.size() )
431 {
432 texnameWithUserPath += '/';
433 texnameWithUserPath += texname;
434 }
435 if (FileSystem->existFile(texnameWithUserPath))
436 texture = SceneManager->getVideoDriver()->getTexture(texnameWithUserPath);
437 else if (FileSystem->existFile(texname))
438 {
439 newTexture = SceneManager->getVideoDriver()->findTexture(texname) == 0;
440 texture = SceneManager->getVideoDriver()->getTexture(texname);
441 }
442 else
443 {
444 newTexture = SceneManager->getVideoDriver()->findTexture(relPath + texname) == 0;
445 // try to read in the relative path, the .obj is loaded from
446 texture = SceneManager->getVideoDriver()->getTexture( relPath + texname );
447 }
448 }
449 if ( texture )
450 {
451 if (type==0)
452 currMaterial->Meshbuffer->Material.setTexture(0, texture);
453 else if (type==1)
454 {
455 if (newTexture)
456 SceneManager->getVideoDriver()->makeNormalMapTexture(texture, bumpiness);
457 currMaterial->Meshbuffer->Material.setTexture(1, texture);
458 currMaterial->Meshbuffer->Material.MaterialType=video::EMT_PARALLAX_MAP_SOLID;
459 currMaterial->Meshbuffer->Material.MaterialTypeParam=0.035f;
460 }
461 else if (type==2)
462 {
463 currMaterial->Meshbuffer->Material.setTexture(0, texture);
464 currMaterial->Meshbuffer->Material.MaterialType=video::EMT_TRANSPARENT_ADD_COLOR;
465 }
466 else if (type==3)
467 {
468// currMaterial->Meshbuffer->Material.Textures[1] = texture;
469// currMaterial->Meshbuffer->Material.MaterialType=video::EMT_REFLECTION_2_LAYER;
470 }
471 // Set diffuse material color to white so as not to affect texture color
472 // Because Maya set diffuse color Kd to black when you use a diffuse color map
473 // But is this the right thing to do?
474 currMaterial->Meshbuffer->Material.DiffuseColor.set(
475 currMaterial->Meshbuffer->Material.DiffuseColor.getAlpha(), 255, 255, 255 );
476 }
477 return bufPtr;
478}
479
480
481void COBJMeshFileLoader::readMTL(const c8* fileName, const io::path& relPath)
482{
483 const io::path realFile(fileName);
484 io::IReadFile * mtlReader;
485
486 if (FileSystem->existFile(realFile))
487 mtlReader = FileSystem->createAndOpenFile(realFile);
488 else if (FileSystem->existFile(relPath + realFile))
489 mtlReader = FileSystem->createAndOpenFile(relPath + realFile);
490 else if (FileSystem->existFile(FileSystem->getFileBasename(realFile)))
491 mtlReader = FileSystem->createAndOpenFile(FileSystem->getFileBasename(realFile));
492 else
493 mtlReader = FileSystem->createAndOpenFile(relPath + FileSystem->getFileBasename(realFile));
494 if (!mtlReader) // fail to open and read file
495 {
496 os::Printer::log("Could not open material file", realFile, ELL_WARNING);
497 return;
498 }
499
500 const long filesize = mtlReader->getSize();
501 if (!filesize)
502 {
503 os::Printer::log("Skipping empty material file", realFile, ELL_WARNING);
504 mtlReader->drop();
505 return;
506 }
507
508 c8* buf = new c8[filesize];
509 mtlReader->read((void*)buf, filesize);
510 const c8* bufEnd = buf+filesize;
511
512 SObjMtl* currMaterial = 0;
513
514 const c8* bufPtr = buf;
515 while(bufPtr != bufEnd)
516 {
517 switch(*bufPtr)
518 {
519 case 'n': // newmtl
520 {
521 // if there's an existing material, store it first
522 if ( currMaterial )
523 Materials.push_back( currMaterial );
524
525 // extract new material's name
526 c8 mtlNameBuf[WORD_BUFFER_LENGTH];
527 bufPtr = goAndCopyNextWord(mtlNameBuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
528
529 currMaterial = new SObjMtl;
530 currMaterial->Name = mtlNameBuf;
531 }
532 break;
533 case 'i': // illum - illumination
534 if ( currMaterial )
535 {
536 const u32 COLOR_BUFFER_LENGTH = 16;
537 c8 illumStr[COLOR_BUFFER_LENGTH];
538
539 bufPtr = goAndCopyNextWord(illumStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
540 currMaterial->Illumination = (c8)atol(illumStr);
541 }
542 break;
543 case 'N':
544 if ( currMaterial )
545 {
546 switch(bufPtr[1])
547 {
548 case 's': // Ns - shininess
549 {
550 const u32 COLOR_BUFFER_LENGTH = 16;
551 c8 nsStr[COLOR_BUFFER_LENGTH];
552
553 bufPtr = goAndCopyNextWord(nsStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
554 f32 shininessValue = core::fast_atof(nsStr);
555
556 // wavefront shininess is from [0, 1000], so scale for OpenGL
557 shininessValue *= 0.128f;
558 currMaterial->Meshbuffer->Material.Shininess = shininessValue;
559 }
560 break;
561 case 'i': // Ni - refraction index
562 {
563 c8 tmpbuf[WORD_BUFFER_LENGTH];
564 bufPtr = goAndCopyNextWord(tmpbuf, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
565 }
566 break;
567 }
568 }
569 break;
570 case 'K':
571 if ( currMaterial )
572 {
573 switch(bufPtr[1])
574 {
575 case 'd': // Kd = diffuse
576 {
577 bufPtr = readColor(bufPtr, currMaterial->Meshbuffer->Material.DiffuseColor, bufEnd);
578
579 }
580 break;
581
582 case 's': // Ks = specular
583 {
584 bufPtr = readColor(bufPtr, currMaterial->Meshbuffer->Material.SpecularColor, bufEnd);
585 }
586 break;
587
588 case 'a': // Ka = ambience
589 {
590 bufPtr=readColor(bufPtr, currMaterial->Meshbuffer->Material.AmbientColor, bufEnd);
591 }
592 break;
593 case 'e': // Ke = emissive
594 {
595 bufPtr=readColor(bufPtr, currMaterial->Meshbuffer->Material.EmissiveColor, bufEnd);
596 }
597 break;
598 } // end switch(bufPtr[1])
599 } // end case 'K': if ( 0 != currMaterial )...
600 break;
601 case 'b': // bump
602 case 'm': // texture maps
603 if (currMaterial)
604 {
605 bufPtr=readTextures(bufPtr, bufEnd, currMaterial, relPath);
606 }
607 break;
608 case 'd': // d - transparency
609 if ( currMaterial )
610 {
611 const u32 COLOR_BUFFER_LENGTH = 16;
612 c8 dStr[COLOR_BUFFER_LENGTH];
613
614 bufPtr = goAndCopyNextWord(dStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
615 f32 dValue = core::fast_atof(dStr);
616
617 currMaterial->Meshbuffer->Material.DiffuseColor.setAlpha( (s32)(dValue * 255) );
618 if (dValue<1.0f)
619 currMaterial->Meshbuffer->Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
620 }
621 break;
622 case 'T':
623 if ( currMaterial )
624 {
625 switch ( bufPtr[1] )
626 {
627 case 'f': // Tf - Transmitivity
628 const u32 COLOR_BUFFER_LENGTH = 16;
629 c8 redStr[COLOR_BUFFER_LENGTH];
630 c8 greenStr[COLOR_BUFFER_LENGTH];
631 c8 blueStr[COLOR_BUFFER_LENGTH];
632
633 bufPtr = goAndCopyNextWord(redStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
634 bufPtr = goAndCopyNextWord(greenStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
635 bufPtr = goAndCopyNextWord(blueStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
636
637 f32 transparency = ( core::fast_atof(redStr) + core::fast_atof(greenStr) + core::fast_atof(blueStr) ) / 3;
638
639 currMaterial->Meshbuffer->Material.DiffuseColor.setAlpha( (s32)(transparency * 255) );
640 if (transparency < 1.0f)
641 currMaterial->Meshbuffer->Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
642 }
643 }
644 break;
645 default: // comments or not recognised
646 break;
647 } // end switch(bufPtr[0])
648 // go to next line
649 bufPtr = goNextLine(bufPtr, bufEnd);
650 } // end while (bufPtr)
651
652 // end of file. if there's an existing material, store it
653 if ( currMaterial )
654 Materials.push_back( currMaterial );
655
656 delete [] buf;
657 mtlReader->drop();
658}
659
660
661//! Read RGB color
662const c8* COBJMeshFileLoader::readColor(const c8* bufPtr, video::SColor& color, const c8* const bufEnd)
663{
664 const u32 COLOR_BUFFER_LENGTH = 16;
665 c8 colStr[COLOR_BUFFER_LENGTH];
666
667 color.setAlpha(255);
668 bufPtr = goAndCopyNextWord(colStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
669 color.setRed((s32)(core::fast_atof(colStr) * 255.0f));
670 bufPtr = goAndCopyNextWord(colStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
671 color.setGreen((s32)(core::fast_atof(colStr) * 255.0f));
672 bufPtr = goAndCopyNextWord(colStr, bufPtr, COLOR_BUFFER_LENGTH, bufEnd);
673 color.setBlue((s32)(core::fast_atof(colStr) * 255.0f));
674 return bufPtr;
675}
676
677
678//! Read 3d vector of floats
679const c8* COBJMeshFileLoader::readVec3(const c8* bufPtr, core::vector3df& vec, const c8* const bufEnd)
680{
681 const u32 WORD_BUFFER_LENGTH = 256;
682 c8 wordBuffer[WORD_BUFFER_LENGTH];
683
684 bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
685 vec.X=-core::fast_atof(wordBuffer); // change handedness
686 bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
687 vec.Y=core::fast_atof(wordBuffer);
688 bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
689 vec.Z=core::fast_atof(wordBuffer);
690 return bufPtr;
691}
692
693
694//! Read 2d vector of floats
695const c8* COBJMeshFileLoader::readUV(const c8* bufPtr, core::vector2df& vec, const c8* const bufEnd)
696{
697 const u32 WORD_BUFFER_LENGTH = 256;
698 c8 wordBuffer[WORD_BUFFER_LENGTH];
699
700 bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
701 vec.X=core::fast_atof(wordBuffer);
702 bufPtr = goAndCopyNextWord(wordBuffer, bufPtr, WORD_BUFFER_LENGTH, bufEnd);
703 vec.Y=1-core::fast_atof(wordBuffer); // change handedness
704 return bufPtr;
705}
706
707
708//! Read boolean value represented as 'on' or 'off'
709const c8* COBJMeshFileLoader::readBool(const c8* bufPtr, bool& tf, const c8* const bufEnd)
710{
711 const u32 BUFFER_LENGTH = 8;
712 c8 tfStr[BUFFER_LENGTH];
713
714 bufPtr = goAndCopyNextWord(tfStr, bufPtr, BUFFER_LENGTH, bufEnd);
715 tf = strcmp(tfStr, "off") != 0;
716 return bufPtr;
717}
718
719
720COBJMeshFileLoader::SObjMtl* COBJMeshFileLoader::findMtl(const core::stringc& mtlName, const core::stringc& grpName)
721{
722 COBJMeshFileLoader::SObjMtl* defMaterial = 0;
723 // search existing Materials for best match
724 // exact match does return immediately, only name match means a new group
725 for (u32 i = 0; i < Materials.size(); ++i)
726 {
727 if ( Materials[i]->Name == mtlName )
728 {
729 if ( Materials[i]->Group == grpName )
730 return Materials[i];
731 else
732 defMaterial = Materials[i];
733 }
734 }
735 // we found a partial match
736 if (defMaterial)
737 {
738 Materials.push_back(new SObjMtl(*defMaterial));
739 Materials.getLast()->Group = grpName;
740 return Materials.getLast();
741 }
742 // we found a new group for a non-existant material
743 else if (grpName.size())
744 {
745 Materials.push_back(new SObjMtl(*Materials[0]));
746 Materials.getLast()->Group = grpName;
747 return Materials.getLast();
748 }
749 return 0;
750}
751
752
753//! skip space characters and stop on first non-space
754const c8* COBJMeshFileLoader::goFirstWord(const c8* buf, const c8* const bufEnd, bool acrossNewlines)
755{
756 // skip space characters
757 if (acrossNewlines)
758 while((buf != bufEnd) && core::isspace(*buf))
759 ++buf;
760 else
761 while((buf != bufEnd) && core::isspace(*buf) && (*buf != '\n'))
762 ++buf;
763
764 return buf;
765}
766
767
768//! skip current word and stop at beginning of next one
769const c8* COBJMeshFileLoader::goNextWord(const c8* buf, const c8* const bufEnd, bool acrossNewlines)
770{
771 // skip current word
772 while(( buf != bufEnd ) && !core::isspace(*buf))
773 ++buf;
774
775 return goFirstWord(buf, bufEnd, acrossNewlines);
776}
777
778
779//! Read until line break is reached and stop at the next non-space character
780const c8* COBJMeshFileLoader::goNextLine(const c8* buf, const c8* const bufEnd)
781{
782 // look for newline characters
783 while(buf != bufEnd)
784 {
785 // found it, so leave
786 if (*buf=='\n' || *buf=='\r')
787 break;
788 ++buf;
789 }
790 return goFirstWord(buf, bufEnd);
791}
792
793
794u32 COBJMeshFileLoader::copyWord(c8* outBuf, const c8* const inBuf, u32 outBufLength, const c8* const bufEnd)
795{
796 if (!outBufLength)
797 return 0;
798 if (!inBuf)
799 {
800 *outBuf = 0;
801 return 0;
802 }
803
804 u32 i = 0;
805 while(inBuf[i])
806 {
807 if (core::isspace(inBuf[i]) || &(inBuf[i]) == bufEnd)
808 break;
809 ++i;
810 }
811
812 u32 length = core::min_(i, outBufLength-1);
813 for (u32 j=0; j<length; ++j)
814 outBuf[j] = inBuf[j];
815
816 outBuf[length] = 0;
817 return length;
818}
819
820
821core::stringc COBJMeshFileLoader::copyLine(const c8* inBuf, const c8* bufEnd)
822{
823 if (!inBuf)
824 return core::stringc();
825
826 const c8* ptr = inBuf;
827 while (ptr<bufEnd)
828 {
829 if (*ptr=='\n' || *ptr=='\r')
830 break;
831 ++ptr;
832 }
833 // we must avoid the +1 in case the array is used up
834 return core::stringc(inBuf, (u32)(ptr-inBuf+((ptr < bufEnd) ? 1 : 0)));
835}
836
837
838const c8* COBJMeshFileLoader::goAndCopyNextWord(c8* outBuf, const c8* inBuf, u32 outBufLength, const c8* bufEnd)
839{
840 inBuf = goNextWord(inBuf, bufEnd, false);
841 copyWord(outBuf, inBuf, outBufLength, bufEnd);
842 return inBuf;
843}
844
845
846bool COBJMeshFileLoader::retrieveVertexIndices(c8* vertexData, s32* idx, const c8* bufEnd, u32 vbsize, u32 vtsize, u32 vnsize)
847{
848 c8 word[16] = "";
849 const c8* p = goFirstWord(vertexData, bufEnd);
850 u32 idxType = 0; // 0 = posIdx, 1 = texcoordIdx, 2 = normalIdx
851
852 u32 i = 0;
853 while ( p != bufEnd )
854 {
855 if ( ( core::isdigit(*p)) || (*p == '-') )
856 {
857 // build up the number
858 word[i++] = *p;
859 }
860 else if ( *p == '/' || *p == ' ' || *p == '\0' )
861 {
862 // number is completed. Convert and store it
863 word[i] = '\0';
864 // if no number was found index will become 0 and later on -1 by decrement
865 idx[idxType] = core::strtol10(word);
866 if (idx[idxType]<0)
867 {
868 switch (idxType)
869 {
870 case 0:
871 idx[idxType] += vbsize;
872 break;
873 case 1:
874 idx[idxType] += vtsize;
875 break;
876 case 2:
877 idx[idxType] += vnsize;
878 break;
879 }
880 }
881 else
882 idx[idxType]-=1;
883
884 // reset the word
885 word[0] = '\0';
886 i = 0;
887
888 // go to the next kind of index type
889 if (*p == '/')
890 {
891 if ( ++idxType > 2 )
892 {
893 // error checking, shouldn't reach here unless file is wrong
894 idxType = 0;
895 }
896 }
897 else
898 {
899 // set all missing values to disable (=-1)
900 while (++idxType < 3)
901 idx[idxType]=-1;
902 ++p;
903 break; // while
904 }
905 }
906
907 // go to the next char
908 ++p;
909 }
910
911 return true;
912}
913
914
915void COBJMeshFileLoader::cleanUp()
916{
917 for (u32 i=0; i < Materials.size(); ++i )
918 {
919 Materials[i]->Meshbuffer->drop();
920 delete Materials[i];
921 }
922
923 Materials.clear();
924}
925
926
927} // end namespace scene
928} // end namespace irr
929
930#endif // _IRR_COMPILE_WITH_OBJ_LOADER_
931