diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index f1a7f56e4..b05b9904b 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -313,6 +313,9 @@ SET( Obj_SRCS ObjFileParser.cpp ObjFileParser.h ObjTools.h + + ObjExporter.h + ObjExporter.cpp ) SOURCE_GROUP( Obj FILES ${Obj_SRCS}) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index a9d4c295b..4bc5a479c 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -41,6 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER #include "ColladaExporter.h" using namespace Assimp; @@ -69,10 +70,13 @@ void ExportSceneCollada(const char* pFile,IOSystem* pIOSystem, const aiScene* pS // Constructor for a specific scene to export ColladaExporter::ColladaExporter( const aiScene* pScene) { + // make sure that all formatting happens using the standard, C locale and not the user's current locale + mOutput.imbue( std::locale("C") ); + mScene = pScene; // set up strings - endstr = "\n"; // std::endl is too complicated for me to insert here. + endstr = "\n"; // start writing WriteFile(); @@ -366,4 +370,5 @@ void ColladaExporter::WriteNode( const aiNode* pNode) } #endif +#endif diff --git a/code/Exporter.cpp b/code/Exporter.cpp index d5a6f3e6d..e805b952c 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -62,6 +62,7 @@ namespace Assimp { // ------------------------------------------------------------------------------------------------ // Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype void ExportSceneCollada(const char*,IOSystem*, const aiScene*); +void ExportSceneObj(const char*,IOSystem*, const aiScene*); void ExportScene3DS(const char*, IOSystem*, const aiScene*) {} /// Function pointer type of a Export worker function @@ -95,6 +96,10 @@ ExportFormatEntry gExporters[] = ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada), #endif +#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER + ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj), +#endif + #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER ExportFormatEntry( "3ds", "Autodesk 3DS (legacy format)", "3ds" , &ExportScene3DS), #endif diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp new file mode 100644 index 000000000..e9eb201ed --- /dev/null +++ b/code/ObjExporter.cpp @@ -0,0 +1,294 @@ +/* +Open Asset Import Library (ASSIMP) +---------------------------------------------------------------------- + +Copyright (c) 2006-2010, ASSIMP Development Team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the ASSIMP team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the ASSIMP Development Team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "AssimpPCH.h" + +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER + +#include "ObjExporter.h" +#include "../include/aiVersion.h" + +using namespace Assimp; +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp +void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene) +{ + // invoke the exporter + ObjExporter exporter(pFile, pScene); + + // we're still here - export successfully completed. Write both the main OBJ file and the material script + { + boost::scoped_ptr outfile (pIOSystem->Open(pFile,"wt")); + outfile->Write( exporter.mOutput.str().c_str(), static_cast(exporter.mOutput.tellp()),1); + } + { + boost::scoped_ptr outfile (pIOSystem->Open(exporter.GetMaterialLibFileName(),"wt")); + outfile->Write( exporter.mOutputMat.str().c_str(), static_cast(exporter.mOutputMat.tellp()),1); + } +} + +} // end of namespace Assimp + + +// ------------------------------------------------------------------------------------------------ +ObjExporter :: ObjExporter(const char* _filename, const aiScene* pScene) +: filename(_filename) +, pScene(pScene) +, endl("\n") +{ + // make sure that all formatting happens using the standard, C locale and not the user's current locale + const std::locale& l = std::locale("C"); + mOutput.imbue(l); + mOutputMat.imbue(l); + + WriteGeometryFile(); + WriteMaterialFile(); +} + +// ------------------------------------------------------------------------------------------------ +std::string ObjExporter :: GetMaterialLibName() +{ + // within the Obj file, we use just the relative file name with the path stripped + const std::string& s = GetMaterialLibFileName(); + std::string::size_type il = s.find_last_of("/\\"); + if (il != std::string::npos) { + return s.substr(il + 1); + } + + return s; +} + +// ------------------------------------------------------------------------------------------------ +std::string ObjExporter :: GetMaterialLibFileName() +{ + return filename + ".mtl"; +} + +// ------------------------------------------------------------------------------------------------ +void ObjExporter :: WriteHeader(std::ostringstream& out) +{ + out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl; + out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl << endl; +} + +// ------------------------------------------------------------------------------------------------ +std::string ObjExporter :: GetMaterialName(unsigned int index) +{ + const aiMaterial* const mat = pScene->mMaterials[index]; + aiString s; + if(AI_SUCCESS == mat->Get(AI_MATKEY_NAME,s)) { + return std::string(s.data,s.length); + } + + char number[ sizeof(unsigned int) * 3 + 1 ]; + ASSIMP_itoa10(number,index); + return "$Material_" + std::string(number); +} + +// ------------------------------------------------------------------------------------------------ +void ObjExporter :: WriteMaterialFile() +{ + WriteHeader(mOutputMat); + + for(unsigned int i = 0; i < pScene->mNumMaterials; ++i) { + const aiMaterial* const mat = pScene->mMaterials[i]; + + int illum = 1; + mOutputMat << "newmtl " << GetMaterialName(i) << endl; + + aiColor4D c; + if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_DIFFUSE,c)) { + mOutputMat << "kd " << c.r << " " << c.g << " " << c.b << endl; + } + if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_AMBIENT,c)) { + mOutputMat << "ka " << c.r << " " << c.g << " " << c.b << endl; + } + if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_SPECULAR,c)) { + mOutputMat << "ks " << c.r << " " << c.g << " " << c.b << endl; + } + + float o; + if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) { + mOutputMat << "d " << o << endl; + } + + if(AI_SUCCESS == mat->Get(AI_MATKEY_SHININESS,o) && o) { + mOutputMat << "Ns " << o << endl; + illum = 2; + } + + mOutputMat << "illum " << illum << endl; + + aiString s; + if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_DIFFUSE(0),s)) { + mOutputMat << "map_kd " << s.data << endl; + } + if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_AMBIENT(0),s)) { + mOutputMat << "map_ka " << s.data << endl; + } + if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SPECULAR(0),s)) { + mOutputMat << "map_ks " << s.data << endl; + } + if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SHININESS(0),s)) { + mOutputMat << "map_ns " << s.data << endl; + } + if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_HEIGHT(0),s) || AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_NORMALS(0),s)) { + // implementations seem to vary here, so write both variants + mOutputMat << "bump " << s.data << endl; + mOutputMat << "map_bump " << s.data << endl; + } + + mOutputMat << endl; + } +} + +// ------------------------------------------------------------------------------------------------ +void ObjExporter :: WriteGeometryFile() +{ + WriteHeader(mOutput); + mOutput << "mtllib " << GetMaterialLibName() << endl << endl; + + // collect mesh geometry + aiMatrix4x4 mBase; + AddNode(pScene->mRootNode,mBase); + + // write vertex positions + mOutput << "# " << vp.size() << " vertex positions" << endl; + BOOST_FOREACH(const aiVector3D& v, vp) { + mOutput << "v " << v.x << " " << v.y << " " << v.z << endl; + } + mOutput << endl; + + // write uv coordinates + mOutput << "# " << vt.size() << " UV coordinates" << endl; + BOOST_FOREACH(const aiVector3D& v, vt) { + mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl; + } + mOutput << endl; + + // write vertex normals + mOutput << "# " << vn.size() << " vertex normals" << endl; + BOOST_FOREACH(const aiVector3D& v, vn) { + mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl; + } + mOutput << endl; + + // now write all mesh instances + BOOST_FOREACH(const MeshInstance& m, meshes) { + mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl; + mOutput << "g " << m.name << endl; + mOutput << "usemtl " << m.matname << endl; + + BOOST_FOREACH(const Face& f, m.faces) { + mOutput << "f "; + BOOST_FOREACH(const FaceVertex& fv, f.indices) { + mOutput << " " << fv.vp << "/"; + if (fv.vt) { + mOutput << fv.vt; + } + mOutput << "/"; + if (fv.vn) { + mOutput << fv.vn; + } + } + + mOutput << endl; + } + mOutput << endl; + } +} + +// ------------------------------------------------------------------------------------------------ +void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) +{ + meshes.push_back(MeshInstance()); + MeshInstance& mesh = meshes.back(); + + mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_"+std::string(m->mName.data,m->mName.length) : ""); + mesh.matname = GetMaterialName(m->mMaterialIndex); + + mesh.faces.resize(m->mNumFaces); + for(unsigned int i = 0; i < m->mNumFaces; ++i) { + const aiFace& f = m->mFaces[i]; + + Face& face = mesh.faces[i]; + face.indices.resize(f.mNumIndices); + + for(unsigned int a = 0; a < f.mNumIndices; ++a) { + const unsigned int idx = f.mIndices[a]; + + // XXX need a way to check if this is an unique vertex or if we had it already, + // in which case we should instead reference the previous occurrence. + ai_assert(m->mVertices); + vp.push_back( mat * m->mVertices[idx] ); + face.indices[a].vp = vp.size(); + + if (m->mNormals) { + vn.push_back( m->mNormals[idx] ); + } + face.indices[a].vn = vn.size(); + + if (m->mTextureCoords[0]) { + vt.push_back( m->mTextureCoords[0][idx] ); + } + face.indices[a].vt = vt.size(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ObjExporter :: AddNode(const aiNode* nd, const aiMatrix4x4& mParent) +{ + const aiMatrix4x4& mAbs = mParent * nd->mTransformation; + + for(unsigned int i = 0; i < nd->mNumMeshes; ++i) { + AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]],mAbs); + } + + for(unsigned int i = 0; i < nd->mNumChildren; ++i) { + AddNode(nd->mChildren[i],mAbs); + } +} + +#endif +#endif diff --git a/code/ObjExporter.h b/code/ObjExporter.h new file mode 100644 index 000000000..619e4e214 --- /dev/null +++ b/code/ObjExporter.h @@ -0,0 +1,122 @@ +/* +Open Asset Import Library (ASSIMP) +---------------------------------------------------------------------- + +Copyright (c) 2006-2010, ASSIMP Development Team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the ASSIMP team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the ASSIMP Development Team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ColladaExporter.h + * Declares the exporter class to write a scene to a Collada file + */ +#ifndef AI_OBJEXPORTER_H_INC +#define AI_OBJEXPORTER_H_INC + +#include + +struct aiScene; +struct aiNode; + +namespace Assimp +{ + +// ------------------------------------------------------------------------------------------------ +/** Helper class to export a given scene to an OBJ file. */ +// ------------------------------------------------------------------------------------------------ +class ASSIMP_API ObjExporter +{ +public: + /// Constructor for a specific scene to export + ObjExporter(const char* filename, const aiScene* pScene); + +public: + + std::string GetMaterialLibName(); + std::string GetMaterialLibFileName(); + +public: + + /// public stringstreams to write all output into + std::ostringstream mOutput, mOutputMat; + +private: + + // intermediate data structures + struct FaceVertex + { + FaceVertex() + : vp(),vn(),vt() + { + } + + // one-based, 0 means: 'does not exist' + unsigned int vp,vn,vt; + }; + + struct Face { + std::vector indices; + }; + + struct MeshInstance { + + std::string name, matname; + std::vector faces; + }; + + void WriteHeader(std::ostringstream& out); + + void WriteMaterialFile(); + void WriteGeometryFile(); + + std::string GetMaterialName(unsigned int index); + + void AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat); + void AddNode(const aiNode* nd, const aiMatrix4x4& mParent); + +private: + + const std::string filename; + const aiScene* const pScene; + + std::vector vp, vn, vt; + std::vector meshes; + + // this endl() doesn't flush() the stream + const std::string endl; +}; + +} + +#endif \ No newline at end of file diff --git a/workspaces/vc9/assimp.vcproj b/workspaces/vc9/assimp.vcproj index d32e3103c..0850076b6 100644 --- a/workspaces/vc9/assimp.vcproj +++ b/workspaces/vc9/assimp.vcproj @@ -4176,6 +4176,18 @@ Name="3ds" > + + + + + +