--- Irrlicht.org/CDefaultMeshFormatLoader.h 2005-08-20 21:20:52.000000000 +0200 +++ IrrlichtObj/CDefaultMeshFormatLoader.h 2006-01-25 00:58:06.000000000 +0100 @@ -15,8 +15,7 @@ { //! Meshloader capable of loading all Irrlicht default build in formats. -/** Which are: Quake 3 Bsp level, Quake 2 MD2 model, Maya .obj mesh, - Milkshape .ms3d model. */ +/** Which are: Quake 3 Bsp level, Quake 2 MD2 model, Milkshape .ms3d model. */ class CDefaultMeshFormatLoader : public IMeshLoader { public: --- Irrlicht.org/CDefaultMeshFormatLoader.cpp 2005-08-20 21:24:54.000000000 +0200 +++ IrrlichtObj/CDefaultMeshFormatLoader.cpp 2006-01-25 00:58:06.000000000 +0100 @@ -4,7 +4,6 @@ #include "CDefaultMeshFormatLoader.h" #include "CAnimatedMeshMD2.h" -#include "CStaticMeshOBJ.h" #include "CAnimatedMeshMS3D.h" #include "CQ3LevelMesh.h" #include @@ -43,7 +42,7 @@ //! based on the file extension (e.g. ".bsp") bool CDefaultMeshFormatLoader::isALoadableFileExtension(const c8* filename) { - return (strstr(filename, ".md2") || strstr(filename, ".obj") || + return (strstr(filename, ".md2") || strstr(filename, ".ms3d") || strstr(filename, ".bsp")); } @@ -76,19 +75,6 @@ msh->drop(); } - // load maya obj - if (strstr(file->getFileName(), ".obj")) - { - file->seek(0); - - msh = new CStaticMeshOBJ(); - success = ((CStaticMeshOBJ*)msh)->loadFile(file); - if (success) - return msh; - - msh->drop(); - } - // load milkshape if (strstr(file->getFileName(), ".ms3d")) { --- Irrlicht.org/CStaticMeshOBJ.h 2005-08-20 21:17:52.000000000 +0200 +++ IrrlichtObj/CStaticMeshOBJ.h 2006-04-09 16:22:24.649379378 +0200 @@ -1,59 +0,0 @@ -// Copyright (C) 2002-2005 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#ifndef __C_STATIC_MESH_OBJ_H_INCLUDED__ -#define __C_STATIC_MESH_OBJ_H_INCLUDED__ - -#include "IAnimatedMesh.h" -#include "IReadFile.h" -#include "S3DVertex.h" -#include "SMesh.h" - -namespace irr -{ -namespace scene -{ - - class CStaticMeshOBJ : public IAnimatedMesh - { - public: - - //! constructor - CStaticMeshOBJ(); - - //! destructor - virtual ~CStaticMeshOBJ(); - - //! loads an obj file - virtual bool loadFile(io::IReadFile* file); - - //! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. - virtual s32 getFrameCount(); - - //! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level. - virtual IMesh* getMesh(s32 frame, s32 detailLevel=255, s32 startFrameLoop=-1, s32 endFrameLoop=-1); - - //! Returns an axis aligned bounding box of the mesh. - //! \return A bounding box of this mesh is returned. - virtual const core::aabbox3d& getBoundingBox() const; - - //! Returns the type of the animated mesh. - virtual E_ANIMATED_MESH_TYPE getMeshType() const; - - private: - - c8* getFirstWord(c8* buf); - void copyWord(c8* outBuf, s32 bufLength, c8* word); - void copyWordLineEnd(c8* outBuf, s32 bufLength, c8* word); - void copyWordSpaceEnd(c8* outBuf, s32 bufLength, c8* word); - c8* getNextWord(c8* word); - - scene::SMesh Mesh; - }; - -} // end namespace scene -} // end namespace irr - -#endif - --- Irrlicht.org/CStaticMeshOBJ.cpp 2006-04-09 13:47:10.249251637 +0200 +++ IrrlichtObj/CStaticMeshOBJ.cpp 2006-04-09 16:22:23.209598052 +0200 @@ -1,357 +0,0 @@ -// Copyright (C) 2002-2005 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "CStaticMeshOBJ.h" -#include -#include -#include "irrString.h" -#include "SMeshBuffer.h" -#include "os.h" -#include "fast_atof.h" - - -namespace irr -{ -namespace scene -{ - - -//! constructor -CStaticMeshOBJ::CStaticMeshOBJ() -{ -} - - - -//! destructor -CStaticMeshOBJ::~CStaticMeshOBJ() -{ -} - - - -//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. -s32 CStaticMeshOBJ::getFrameCount() -{ - return 1; -} - - - -//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level. -IMesh* CStaticMeshOBJ::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) -{ - return &Mesh; -} - - - -//! loads an obj file -bool CStaticMeshOBJ::loadFile(io::IReadFile* file) -{ - const s32 WORD_BUFFER_LENGTH = 255; - c8 wordBuffer[WORD_BUFFER_LENGTH]; - - SMeshBuffer *meshbuffer = new SMeshBuffer; - - core::array< core::vector3df > vertexBuffer; - core::array< core::vector2d > textureCoordBuffer; - core::array< core::vector3df > normalsBuffer; - - s32 filesize = (s32)file->getSize(); - - if (!filesize) - return false; - - c8* buf = new c8[filesize]; - file->read((void*)buf, filesize); - - c8* line = buf; - - while(line) - { - c8* word = line; - copyWord(wordBuffer, WORD_BUFFER_LENGTH, word); - - if (wordBuffer[0] == 'v' && wordBuffer[1] == 0x0) - { - // vector - c8* p1 = getNextWord(word); - c8* p2 = getNextWord(p1); - c8* p3 = getNextWord(p2); - - vertexBuffer.push_back(core::vector3df( core::fast_atof(p1), - core::fast_atof(p2), - core::fast_atof(p3))); - } - else - if (wordBuffer[0] == 'v' && wordBuffer[1] == 't') - { - // texture coordinate - c8* p1 = getNextWord(word); - c8* p2 = getNextWord(p1); - - textureCoordBuffer.push_back(core::vector2d( core::fast_atof(p1), - core::fast_atof(p2))); - } - else - if (wordBuffer[0] == 'v' && wordBuffer[1] == 'n') - { - // vertex normal - c8* p1 = getNextWord(word); - c8* p2 = getNextWord(p1); - c8* p3 = getNextWord(p2); - - normalsBuffer.push_back(core::vector3df( core::fast_atof(p1), - core::fast_atof(p2), - core::fast_atof(p3))); - } - else - if (wordBuffer[0] == 'f' && wordBuffer[1] == 0x0) - { - // face - c8 faceBuf[20]; - copyWordLineEnd(wordBuffer, WORD_BUFFER_LENGTH, word); - //copyWord(wordBuffer, WORD_BUFFER_LENGTH, word); - - const s32 MAX_FACE_POINT_COUNT = 40; - u32 facePointCount = 0; - s32 facePoints[MAX_FACE_POINT_COUNT][3]; - - for (s32 k=0; k= (u32)MAX_FACE_POINT_COUNT) - { - meshbuffer->drop(); - os::Printer::log("Face with more than 40 face points found s32 file. Not loading.", file->getFileName(), ELL_ERROR); - delete [] buf; - return false; - } - - face = getNextWord(face); - } - - // now we've got a face. - // and have to create some vertices and indices from them - - s32 currentVertexCount = meshbuffer->Vertices.size(); - - for (u32 i=0; i= 0 && facePoints[i][2]-1 < (s32)normalsBuffer.size()) - { - v.Normal.X = normalsBuffer[ facePoints[i][2]-1 ].X; - v.Normal.Y = normalsBuffer[ facePoints[i][2]-1 ].Y; - v.Normal.Z = normalsBuffer[ facePoints[i][2]-1].Z; - } - else - { - v.Normal.X = v.Normal.Y = v.Normal.Z = 0; - } - - if (facePoints[i][0]-1 >= 0 && facePoints[i][0]-1 < (s32)vertexBuffer.size()) - { - v.Pos.X = vertexBuffer [ facePoints[i][0]-1 ].X; - v.Pos.Y = vertexBuffer [ facePoints[i][0]-1 ].Y; - v.Pos.Z = vertexBuffer [ facePoints[i][0]-1 ].Z; - } - else - { - v.Pos.X = v.Pos.Y = v.Pos.Z = 0; - } - - if (facePoints[i][1]-1 >= 0 && facePoints[i][1]-1 < (s32)textureCoordBuffer.size()) - { - v.TCoords.X = textureCoordBuffer[ facePoints[i][1]-1].X; - v.TCoords.Y = -textureCoordBuffer[ facePoints[i][1]-1].Y; - } - else - { - v.TCoords.X = v.TCoords.Y = 0; - } - - - meshbuffer->Vertices.push_back(v); - } - - meshbuffer->Indices.push_back(0 + currentVertexCount); - meshbuffer->Indices.push_back(1 + currentVertexCount); - meshbuffer->Indices.push_back((facePointCount-1) + currentVertexCount); - - - for (u32 jk=0; jkIndices.push_back(1 + currentVertexCount); - meshbuffer->Indices.push_back((facePointCount-2-jk) + currentVertexCount); - meshbuffer->Indices.push_back((facePointCount-1-jk) + currentVertexCount); - } - - } - else - if (wordBuffer[0] == '#' || wordBuffer[0] == 'u' ||wordBuffer[0] =='g') - { - // comment - // find end of line - s32 i=0; - while(line[i]) - { - if (line[i]=='\n' || line[i]=='\r') - break; - - ++i; - } - - line = &line[i]; - } - - - line = getNextWord(line); - } - - delete [] buf; - - meshbuffer->recalculateBoundingBox(); - Mesh.addMeshBuffer(meshbuffer); - Mesh.recalculateBoundingBox(); - meshbuffer->drop(); - return true; -} - - -c8* CStaticMeshOBJ::getFirstWord(c8* buf) -{ - s32 i = 0; - while(buf[i] && (buf[i]==' ' || buf[i]=='\n' || buf[i]=='\r' || buf[i]=='\t')) - ++i; - - return &buf[i]; -} - - - -void CStaticMeshOBJ::copyWord(c8* outBuf, s32 bufLength, c8* word) -{ - if (!word) - return; - - s32 i = 0; - while(word[i]) - { - if (word[i]==' ' || word[i]=='\n' || word[i]=='\r' || word[i]=='\t') - break; - ++i; - } - - for (s32 j=0; j& CStaticMeshOBJ::getBoundingBox() const -{ - return Mesh.getBoundingBox(); -} - - -//! Returns the type of the animated mesh. -E_ANIMATED_MESH_TYPE CStaticMeshOBJ::getMeshType() const -{ - return EAMT_OBJ; -} - - -} // end namespace scene -} // end namespace irr - --- Irrlicht.org/CSceneManager.cpp 2006-04-09 01:41:16.083231923 +0200 +++ IrrlichtObj/CSceneManager.cpp 2006-01-09 00:03:30.000000000 +0100 @@ -22,6 +22,7 @@ #include "CMY3DMeshFileLoader.h" #include "CColladaFileLoader.h" #include "CDMFLoader.h" +#include "COBJMeshFileLoader.h" #include "CTestSceneNode.h" #include "CAnimatedMeshSceneNode.h" @@ -111,6 +112,7 @@ MeshLoaderList.push_back(new CMY3DMeshFileLoader(FileSystem, Driver, this)); MeshLoaderList.push_back(new CColladaFileLoader(Driver, this, FileSystem)); MeshLoaderList.push_back(new CDMFLoader(Driver, this)); + MeshLoaderList.push_back(new COBJMeshFileLoader(FileSystem, Driver)); } --- Irrlicht.org/COBJMeshFileLoader.h 2006-04-10 14:21:01.000000000 +0200 +++ Irrlicht.new/COBJMeshFileLoader.h 2006-04-13 14:38:31.000000000 +0200 @@ -0,0 +1,108 @@ +// Copyright (C) 2002-2005 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_OBJ_MESH_FILE_LOADER_H_INCLUDED__ +#define __C_OBJ_MESH_FILE_LOADER_H_INCLUDED__ + +#include "IMeshLoader.h" +#include "IFileSystem.h" +#include "IVideoDriver.h" +#include "irrString.h" +#include "SMesh.h" +#include "SMeshBuffer.h" + +namespace irr +{ +namespace scene +{ + +//! Meshloader capable of loading 3ds meshes. +class COBJMeshFileLoader : public IMeshLoader +{ +public: + + //! Constructor + COBJMeshFileLoader(io::IFileSystem* fs, video::IVideoDriver* driver); + + //! destructor + virtual ~COBJMeshFileLoader(); + + //! returns true if the file maybe is able to be loaded by this class + //! based on the file extension (e.g. ".cob") + virtual bool isALoadableFileExtension(const c8* fileName); + + //! creates/loads an animated mesh from the file. + //! \return Pointer to the created mesh. Returns 0 if loading failed. + //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). + //! See IUnknown::drop() for more information. + virtual IAnimatedMesh* createMesh(irr::io::IReadFile* file); + +private: + + struct SObjMtl + { + SObjMtl() : pMeshbuffer(0), illumination(0) { + this->pMeshbuffer = new SMeshBuffer(); + this->pMeshbuffer->Material.Shininess = 0.0f; + this->pMeshbuffer->Material.AmbientColor = video::SColorf(0.2f, 0.2f, 0.2f, 1.0f).toSColor(); + this->pMeshbuffer->Material.DiffuseColor = video::SColorf(0.8f, 0.8f, 0.8f, 1.0f).toSColor(); + this->pMeshbuffer->Material.SpecularColor = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f).toSColor(); + }; + SObjMtl(SObjMtl& o) : pMeshbuffer(o.pMeshbuffer) { o.pMeshbuffer->grab(); }; + + ~SObjMtl() { }; + + scene::SMeshBuffer *pMeshbuffer; + core::stringc name; + c8 illumination; + }; + + struct SObjGroup + { + SObjGroup() {}; + SObjGroup(SObjGroup& o) {}; + ~SObjGroup() { }; + + core::stringc name; + }; + + // returns a pointer to the first printable character available in the buffer + c8* goFirstWord(c8* buf, const c8* pBufEnd); + // returns a pointer to the first printable character after the first non-printable + c8* goNextWord(c8* buf, const c8* pBufEnd); + // returns a pointer to the next printable character after the first line break + c8* goNextLine(c8* buf, const c8* pBufEnd); + // copies the current word from the inBuf to the outBuf + void copyWord(c8* outBuf, const c8* inBuf, s32 outBufLength, const c8* pBufEnd); + // copies the current line from the inBuf to the outBuf + void copyLine(c8* outBuf, const c8* inBuf, s32 outBufLength, const c8* pBufEnd); + // combination of goNextWord followed by copyWord + c8* goAndCopyNextWord(c8* outBuf, c8* inBuf, s32 outBufLength, const c8* pBufEnd); + + void readMTL(const c8* pFileName, core::stringc relPath); + c8* readColor(c8* pBufPtr, video::SColor& color, const c8* pBufEnd); + SObjMtl * findMtl(const core::stringc& pMtlName); + SObjGroup * findGroup(const core::stringc& pGroupName); + SObjGroup * findOrAddGroup(const core::stringc& pGroupName); + + // reads and convert to integer the vertex indices in a line of obj file's face statement + // -1 for the index if it doesn't exist + // indices are changed to 0-based index instead of 1-based from the obj file + bool retrieveVertexIndices(c8* pVertexData, s32* Idx, const c8* pBufEnd); + + void cleanUp(); + + io::IFileSystem* FileSystem; + video::IVideoDriver* Driver; + + core::array materials; + core::array groups; + SMesh* Mesh; +}; + +} // end namespace scene +} // end namespace irr + +#endif + --- Irrlicht.org/COBJMeshFileLoader.cpp 2006-04-10 14:20:59.000000000 +0200 +++ Irrlicht.new/COBJMeshFileLoader.cpp 2006-04-14 18:59:48.715751700 +0200 @@ -0,0 +1,764 @@ +// Copyright (C) 2002-2005 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "COBJMeshFileLoader.h" +#include +#include +#include +#include "SMeshBuffer.h" +#include "SAnimatedMesh.h" +#include "fast_atof.h" + +namespace irr +{ +namespace scene +{ + +//! Constructor +COBJMeshFileLoader::COBJMeshFileLoader(io::IFileSystem* fs, video::IVideoDriver* driver) +: FileSystem(fs), Driver(driver), Mesh(0) +{ + if (FileSystem) + FileSystem->grab(); + + if (Driver) + Driver->grab(); +} + + + +//! destructor +COBJMeshFileLoader::~COBJMeshFileLoader() +{ + if (FileSystem) + FileSystem->drop(); + + if (Driver) + Driver->drop(); + + if (Mesh) + Mesh->drop(); +} + + + +//! returns true if the file maybe is able to be loaded by this class +//! based on the file extension (e.g. ".bsp") +bool COBJMeshFileLoader::isALoadableFileExtension(const c8* filename) +{ + return strstr(filename, ".obj")!=0; +} + + + +//! creates/loads an animated mesh from the file. +//! \return Pointer to the created mesh. Returns 0 if loading failed. +//! If you no longer need the mesh, you should call IAnimatedMesh::drop(). +//! See IUnknown::drop() for more information. +IAnimatedMesh* COBJMeshFileLoader::createMesh(io::IReadFile* file) +{ + const s32 WORD_BUFFER_LENGTH = 255; + c8 wordBuffer[WORD_BUFFER_LENGTH]; + + if (Mesh) + Mesh->drop(); + Mesh = new SMesh(); + + core::array< core::vector3df > vertexBuffer; + core::array< core::vector2d > textureCoordBuffer; + core::array< core::vector3df > normalsBuffer; + SObjGroup * pCurrGroup = 0; + SObjMtl * pCurrMtl = new SObjMtl(); + pCurrMtl->name=""; + materials.push_back(pCurrMtl); + + s32 filesize = (s32)file->getSize(); + if (!filesize) + return false; + + // ******************************************************************** + // Patch to locate the file in the same folder as the .obj. + // If you load the file as "data/some.obj" and mtllib contains + // "mtlname test.mtl" (as usual), the loading will fail. Instead it + // must look for data/test.tml. This patch does exactly that. + // + // patch by mandrav@codeblocks.org + // ******************************************************************** + core::stringc obj_fullname = file->getFileName(); + core::stringc obj_relpath = ""; + s32 pathend = obj_fullname.findLast('/'); + if (pathend == -1) + pathend = obj_fullname.findLast('\\'); + if (pathend != -1) + obj_relpath = obj_fullname.subString(0, pathend + 1); + // ******************************************************************** + // end of mtl folder patch + // ******************************************************************** + + c8* pBuf = new c8[filesize]; + file->read((void*)pBuf, filesize); + const c8* pBufEnd = pBuf+filesize; + + // Process obj information + c8* pBufPtr = pBuf; + while(pBufPtr && (pBufPtr-pBufpMeshbuffer->Vertices.size(); + u32 facePointCount = 0; // number of vertices in this face + + // Assign vertex colour from currently active material's diffuse colour + if (pCurrMtl) + v.Color = pCurrMtl->pMeshbuffer->Material.DiffuseColor; + + // get all vertices data in this face (current line of obj file) + copyLine(wordBuffer, pBufPtr, WORD_BUFFER_LENGTH, pBufEnd); + c8 * pLinePtr = wordBuffer; + + // read in all vertices + pLinePtr = goNextWord(pLinePtr, pBufEnd); + while (0 != pLinePtr[0]) + { + // Array to communicate with retrieveVertexIndices() + // sends the buffer sizes and gets the actual indices + // if index not set returns -1 + s32 Idx[3]; + Idx[0]=vertexBuffer.size(); + Idx[1]=textureCoordBuffer.size(); + Idx[2]=normalsBuffer.size(); + + // read in next vertex's data + copyWord(vertexWord, pLinePtr, WORD_BUFFER_LENGTH, pBufEnd); + // this function will also convert obj's 1-based index to c++'s 0-based index + retrieveVertexIndices(vertexWord, Idx, pBufEnd); + if ( -1 != Idx[0] ) + { + v.Pos = vertexBuffer[Idx[0]]; + } + if ( -1 != Idx[1] ) + { + v.TCoords = textureCoordBuffer[Idx[1]]; + } + if ( -1 != Idx[2] ) + { + v.Normal = normalsBuffer[Idx[2]]; + } + pCurrMtl->pMeshbuffer->Vertices.push_back(v); + ++facePointCount; + + // go to next vertex + pLinePtr = goNextWord(pLinePtr, pBufEnd); + } + + // Add indices for first 3 vertices + pCurrMtl->pMeshbuffer->Indices.push_back( currentVertexCount ); + pCurrMtl->pMeshbuffer->Indices.push_back( ( facePointCount - 1 ) + currentVertexCount ); + pCurrMtl->pMeshbuffer->Indices.push_back( ( facePointCount - 2 ) + currentVertexCount ); + // Add indices for subsequent vertices + for ( u32 i = 0; i < facePointCount - 3; ++i ) + { + pCurrMtl->pMeshbuffer->Indices.push_back( currentVertexCount ); + pCurrMtl->pMeshbuffer->Indices.push_back( ( facePointCount - 2 - i ) + currentVertexCount ); + pCurrMtl->pMeshbuffer->Indices.push_back( ( facePointCount - 3 - i ) + currentVertexCount ); + } + + pBufPtr = goNextLine(pBufPtr, pBufEnd); + } + break; + + default: + // eat up rest of line + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + } // end switch(pBufPtr[0]) + } // end while(pBufPtr && (pBufPtr-pBufpMeshbuffer->getIndices() > 0 ) + { + materials[m]->pMeshbuffer->recalculateBoundingBox(); + Mesh->addMeshBuffer( materials[m]->pMeshbuffer ); + } + } + + // Create the Animated mesh if there's anything in the mesh + SAnimatedMesh* pAM = 0; + if ( 0 != Mesh->getMeshBufferCount() ) + { + Mesh->recalculateBoundingBox(); + pAM = new SAnimatedMesh(); + pAM->Type = EAMT_OBJ; + pAM->addMesh(Mesh); + pAM->recalculateBoundingBox(); + } + + // Clean up the allocate obj file contents + delete [] pBuf; + // more cleaning up + cleanUp(); + Mesh->drop(); + Mesh = 0; + + return pAM; +} + + +void COBJMeshFileLoader::readMTL(const c8* pFileName, core::stringc relPath) +{ + const s32 WORD_BUFFER_LENGTH = 255; + + io::IReadFile * pMtlReader; + if (FileSystem->existFile(pFileName)) + pMtlReader = FileSystem->createAndOpenFile(pFileName); + else + // try to read in the relative path, the .obj is loaded from + pMtlReader = FileSystem->createAndOpenFile((relPath + pFileName).c_str()); + if (!pMtlReader) // fail to open and read file + return; + + s32 filesize = pMtlReader->getSize(); + if (!filesize) + return; + + c8* pBuf = new c8[filesize]; + pMtlReader->read((void*)pBuf, filesize); + const c8* pBufEnd = pBuf+filesize; + + SObjMtl* pCurrMaterial = 0; + + c8* pBufPtr = pBuf; + while(pBufPtr && (pBufPtr-pBuf<=filesize)) + { + switch(pBufPtr[0]) + { + case 'n': // newmtl + { + // if there's an existing material, store it first + if ( 0 != pCurrMaterial ) + { + materials.push_back( pCurrMaterial ); + } + + // extract new material's name + c8 mtlNameBuf[WORD_BUFFER_LENGTH]; + pBufPtr = goAndCopyNextWord(mtlNameBuf, pBufPtr, WORD_BUFFER_LENGTH, pBufEnd); + + pCurrMaterial = new SObjMtl; + pCurrMaterial->name = mtlNameBuf; + + // go to next line + pBufPtr = goNextLine(pBufPtr, pBufEnd); + } + break; + case 'i': // illum - illumination + if ( 0 != pCurrMaterial ) + { + const s32 COLOR_BUFFER_LENGTH = 16; + c8 illumStr[COLOR_BUFFER_LENGTH]; + + pBufPtr = goAndCopyNextWord(illumStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + pCurrMaterial->illumination = atol(illumStr); + } + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + case 'N': // Ns - shininess + if ( 0 != pCurrMaterial ) + { + const s32 COLOR_BUFFER_LENGTH = 16; + c8 nsStr[COLOR_BUFFER_LENGTH]; + + pBufPtr = goAndCopyNextWord(nsStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + f32 shininessValue = core::fast_atof(nsStr); + + // wavefront shininess is from [0, 1000], so scale for OpenGL + shininessValue *= 0.128f; + pCurrMaterial->pMeshbuffer->Material.Shininess = shininessValue; + } + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + case 'K': + if ( 0 != pCurrMaterial ) + { + switch(pBufPtr[1]) + { + case 'd': // Kd = diffuse + { + pBufPtr = readColor(pBufPtr, pCurrMaterial->pMeshbuffer->Material.DiffuseColor, pBufEnd); + + } + break; + + case 's': // Ks = specular + { + pBufPtr = readColor(pBufPtr, pCurrMaterial->pMeshbuffer->Material.SpecularColor, pBufEnd); + } + break; + + case 'a': // Ka = ambience + { + pBufPtr=readColor(pBufPtr, pCurrMaterial->pMeshbuffer->Material.AmbientColor, pBufEnd); + } + break; + + default: + // eat up rest of line + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + } // end switch(pBufPtr[1]) + } // end case 'K': if ( 0 != pCurrMaterial )... + break; + case 'm': // map_Kd - diffuse texture map + if (0 != pCurrMaterial) + { + char type=0; + if (!strncmp(pBufPtr,"map_bump",8)) + type=1; + else if (!strncmp(pBufPtr,"map_d",5)) + type=2; + else if (!strncmp(pBufPtr,"map_refl",8)) + type=2; + // extract new material's name + c8 textureNameBuf[WORD_BUFFER_LENGTH]; + pBufPtr = goAndCopyNextWord(textureNameBuf, pBufPtr, WORD_BUFFER_LENGTH, pBufEnd); + if (type==1) { + pBufPtr = goAndCopyNextWord(textureNameBuf, pBufPtr, WORD_BUFFER_LENGTH, pBufEnd); + pCurrMaterial->pMeshbuffer->Material.MaterialTypeParam=core::fast_atof(textureNameBuf); + pBufPtr = goAndCopyNextWord(textureNameBuf, pBufPtr, WORD_BUFFER_LENGTH, pBufEnd); + } + + video::ITexture * pTexture = 0; + if (FileSystem->existFile(textureNameBuf)) + pTexture = Driver->getTexture( textureNameBuf ); + else + // try to read in the relative path, the .obj is loaded from + pTexture = Driver->getTexture( (relPath + textureNameBuf).c_str() ); + if ( 0 != pTexture ) + { + if (type==0) + pCurrMaterial->pMeshbuffer->Material.Texture1 = pTexture; + else if (type==1) { + Driver->makeNormalMapTexture(pTexture); + pCurrMaterial->pMeshbuffer->Material.Texture2 = pTexture; + pCurrMaterial->pMeshbuffer->Material.MaterialType=video::EMT_PARALLAX_MAP_SOLID; + } + else if (type==2) { + pCurrMaterial->pMeshbuffer->Material.Texture1 = pTexture; + pCurrMaterial->pMeshbuffer->Material.MaterialType=video::EMT_TRANSPARENT_ADD_COLOR; + } + // Set diffuse material colour to white so as not to affect texture colour + // Because Maya set diffuse colour Kd to black when you use a diffuse colour map + // But is this the right thing to do? + pCurrMaterial->pMeshbuffer->Material.DiffuseColor.set( + pCurrMaterial->pMeshbuffer->Material.DiffuseColor.getAlpha(), 255, 255, 255 ); + } + } + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + case 'd': // d - transparency + if ( 0 != pCurrMaterial ) + { + const s32 COLOR_BUFFER_LENGTH = 16; + c8 dStr[COLOR_BUFFER_LENGTH]; + + pBufPtr = goAndCopyNextWord(dStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + f32 dValue = core::fast_atof(dStr); + + pCurrMaterial->pMeshbuffer->Material.DiffuseColor.setAlpha( (s32)(dValue * 255) ); + if (dValue<1.0f) + pCurrMaterial->pMeshbuffer->Material.MaterialType = irr::video::EMT_TRANSPARENT_VERTEX_ALPHA; + } + + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + + case 'T': + if ( 0 != pCurrMaterial ) + { + switch ( pBufPtr[1] ) + { + case 'f': // Tf - Transmitivity + const s32 COLOR_BUFFER_LENGTH = 16; + c8 redStr[COLOR_BUFFER_LENGTH]; + c8 greenStr[COLOR_BUFFER_LENGTH]; + c8 blueStr[COLOR_BUFFER_LENGTH]; + + pBufPtr = goAndCopyNextWord(redStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + pBufPtr = goAndCopyNextWord(greenStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + pBufPtr = goAndCopyNextWord(blueStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + + f32 transparency = ( core::fast_atof(redStr) + core::fast_atof(greenStr) + core::fast_atof(blueStr) ) / 3; + + pCurrMaterial->pMeshbuffer->Material.DiffuseColor.setAlpha( (s32)(transparency * 255) ); + pCurrMaterial->pMeshbuffer->Material.MaterialType = irr::video::EMT_TRANSPARENT_VERTEX_ALPHA; + } + } + + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + + default: // comments or not recognised + // eat up rest of line + pBufPtr = goNextLine(pBufPtr, pBufEnd); + break; + } // end switch(pBufPtr[0]) + } // end while (pBufPtr) + + // end of file. if there's an existing material, store it + if ( 0 != pCurrMaterial ) + { + materials.push_back( pCurrMaterial ); + pCurrMaterial = 0; + } + + delete [] pBuf; + pMtlReader->drop(); +} + +c8* COBJMeshFileLoader::readColor(c8* pBufPtr, video::SColor& color, const c8* pBufEnd) +{ + const s32 COLOR_BUFFER_LENGTH = 16; + c8 colStr[COLOR_BUFFER_LENGTH]; + + color.setAlpha(255); + pBufPtr = goAndCopyNextWord(colStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + color.setRed((s32)(255-core::fast_atof(colStr) * 255)); + pBufPtr = goAndCopyNextWord(colStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + color.setGreen((s32)(255-core::fast_atof(colStr) * 255)); + pBufPtr = goAndCopyNextWord(colStr, pBufPtr, COLOR_BUFFER_LENGTH, pBufEnd); + color.setBlue((s32)(255-core::fast_atof(colStr) * 255)); + + return goNextLine(pBufPtr, pBufEnd); +} + + +COBJMeshFileLoader::SObjMtl* COBJMeshFileLoader::findMtl(const core::stringc& pMtlName) +{ + for (u32 i = 0; i < materials.size(); ++i) + { + if ( materials[i]->name == pMtlName ) + return materials[i]; + } + return 0; +} + + + +COBJMeshFileLoader::SObjGroup * COBJMeshFileLoader::findGroup(const core::stringc& pGroupName) +{ + for (u32 i = 0; i < groups.size(); ++i) + { + if ( groups[i]->name == pGroupName ) + return groups[i]; + } + return 0; +} + + +COBJMeshFileLoader::SObjGroup * COBJMeshFileLoader::findOrAddGroup(const core::stringc& pGroupName) +{ + SObjGroup * pGroup = findGroup( pGroupName ); + if ( 0 != pGroup ) + { + // group found, return it + return pGroup; + } + // group not found, create a new group + SObjGroup* group = new SObjGroup(); + group->name = pGroupName; + groups.push_back(group); + return group; +} + + +c8* COBJMeshFileLoader::goFirstWord(c8* buf, const c8* pBufEnd) +{ + s32 i = 0; + // skip pass non-printable characters + while(buf[i] && isspace(buf[i])) + { + if ( &(buf[i]) == pBufEnd ) + { + // end of buffer check + return 0; + } + ++i; + } + + return &buf[i]; +} + + +c8* COBJMeshFileLoader::goNextWord(c8* buf, const c8* pBufEnd) +{ + if (!buf) + return 0; + + s32 i = 0; + // look for non-printable characters + while(buf[i]) + { + if ( &(buf[i]) == pBufEnd ) + { + // end of buffer check + return 0; + } + if (buf[i]==' ' || buf[i]=='\n' || buf[i]=='\r' || buf[i]=='\t') + break; + + ++i; + } + + // look for printable character after non-printable character + c8* nextWord = goFirstWord(&buf[i], pBufEnd); + // if can't find, return null + return nextWord == buf ? 0 : nextWord; +} + + +c8* COBJMeshFileLoader::goNextLine(c8* buf, const c8* pBufEnd) +{ + // Have to use pointer to pointer in parameter or else string address won't be changed + s32 i=0; + // look for newline characters + while(buf[i]) + { + if ( &(buf[i]) == pBufEnd ) + { + // end of buffer check + return 0; + } + if (buf[i]=='\n' || buf[i]=='\r') + { + // found it, so skip pass it + break; + } + + ++i; + } + while (isspace(buf[++i])) ; + + return &buf[i]; +} + + +void COBJMeshFileLoader::copyWord(c8* outBuf, const c8* inBuf, s32 outBufLength, const c8* pBufEnd) +{ + if (!inBuf) + return; + + s32 i = 0; + while(inBuf[i]) + { + if (inBuf[i]==' ' || inBuf[i]=='\n' || inBuf[i]=='\r' || inBuf[i]=='\t' || &(inBuf[i]) == pBufEnd) + break; + ++i; + } + + for (s32 j=0; j 2 ) + { + // error checking, shouldn't reach here unless file is wrong + idxType = 0; + } + } + else if (*pChar == '\0') + { + // set all missing values to disable (=-1) + while (++idxType < 3) + pIdx[idxType]=-1; + ++pChar; + break; // while + } + } + + // go to the next char + ++pChar; + } + + return true; +} + + +void COBJMeshFileLoader::cleanUp() +{ + for (u32 i = 0; i < materials.size(); ++i ) + { + materials[i]->pMeshbuffer->drop(); + delete materials[i]; + } + materials.clear(); + for (u32 i = 0; i < groups.size(); ++i ) + { + delete groups[i]; + } + groups.clear(); +} + + +} // end namespace scene +} // end namespace irr +