rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 Open Asset Import Library (assimp)
|
nuclear@0
|
3 ----------------------------------------------------------------------
|
nuclear@0
|
4
|
nuclear@0
|
5 Copyright (c) 2006-2012, assimp team
|
nuclear@0
|
6 All rights reserved.
|
nuclear@0
|
7
|
nuclear@0
|
8 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
9 with or without modification, are permitted provided that the
|
nuclear@0
|
10 following conditions are met:
|
nuclear@0
|
11
|
nuclear@0
|
12 * Redistributions of source code must retain the above
|
nuclear@0
|
13 copyright notice, this list of conditions and the
|
nuclear@0
|
14 following disclaimer.
|
nuclear@0
|
15
|
nuclear@0
|
16 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
17 copyright notice, this list of conditions and the
|
nuclear@0
|
18 following disclaimer in the documentation and/or other
|
nuclear@0
|
19 materials provided with the distribution.
|
nuclear@0
|
20
|
nuclear@0
|
21 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
22 contributors may be used to endorse or promote products
|
nuclear@0
|
23 derived from this software without specific prior
|
nuclear@0
|
24 written permission of the assimp team.
|
nuclear@0
|
25
|
nuclear@0
|
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
37
|
nuclear@0
|
38 ----------------------------------------------------------------------
|
nuclear@0
|
39 */
|
nuclear@0
|
40
|
nuclear@0
|
41 #include "AssimpPCH.h"
|
nuclear@0
|
42
|
nuclear@0
|
43 #ifndef ASSIMP_BUILD_NO_EXPORT
|
nuclear@0
|
44 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
|
nuclear@0
|
45
|
nuclear@0
|
46 #include "ObjExporter.h"
|
nuclear@0
|
47 #include "assimp/version.h"
|
nuclear@0
|
48
|
nuclear@0
|
49 using namespace Assimp;
|
nuclear@0
|
50 namespace Assimp {
|
nuclear@0
|
51
|
nuclear@0
|
52 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
53 // Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp
|
nuclear@0
|
54 void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
|
nuclear@0
|
55 {
|
nuclear@0
|
56 // invoke the exporter
|
nuclear@0
|
57 ObjExporter exporter(pFile, pScene);
|
nuclear@0
|
58
|
nuclear@0
|
59 // we're still here - export successfully completed. Write both the main OBJ file and the material script
|
nuclear@0
|
60 {
|
nuclear@0
|
61 boost::scoped_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
|
nuclear@0
|
62 if(outfile == NULL) {
|
nuclear@0
|
63 throw DeadlyExportError("could not open output .obj file: " + std::string(pFile));
|
nuclear@0
|
64 }
|
nuclear@0
|
65 outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
|
nuclear@0
|
66 }
|
nuclear@0
|
67 {
|
nuclear@0
|
68 boost::scoped_ptr<IOStream> outfile (pIOSystem->Open(exporter.GetMaterialLibFileName(),"wt"));
|
nuclear@0
|
69 if(outfile == NULL) {
|
nuclear@0
|
70 throw DeadlyExportError("could not open output .mtl file: " + std::string(exporter.GetMaterialLibFileName()));
|
nuclear@0
|
71 }
|
nuclear@0
|
72 outfile->Write( exporter.mOutputMat.str().c_str(), static_cast<size_t>(exporter.mOutputMat.tellp()),1);
|
nuclear@0
|
73 }
|
nuclear@0
|
74 }
|
nuclear@0
|
75
|
nuclear@0
|
76 } // end of namespace Assimp
|
nuclear@0
|
77
|
nuclear@0
|
78
|
nuclear@0
|
79 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
80 ObjExporter :: ObjExporter(const char* _filename, const aiScene* pScene)
|
nuclear@0
|
81 : filename(_filename)
|
nuclear@0
|
82 , pScene(pScene)
|
nuclear@0
|
83 , endl("\n")
|
nuclear@0
|
84 {
|
nuclear@0
|
85 // make sure that all formatting happens using the standard, C locale and not the user's current locale
|
nuclear@0
|
86 const std::locale& l = std::locale("C");
|
nuclear@0
|
87 mOutput.imbue(l);
|
nuclear@0
|
88 mOutputMat.imbue(l);
|
nuclear@0
|
89
|
nuclear@0
|
90 WriteGeometryFile();
|
nuclear@0
|
91 WriteMaterialFile();
|
nuclear@0
|
92 }
|
nuclear@0
|
93
|
nuclear@0
|
94 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
95 std::string ObjExporter :: GetMaterialLibName()
|
nuclear@0
|
96 {
|
nuclear@0
|
97 // within the Obj file, we use just the relative file name with the path stripped
|
nuclear@0
|
98 const std::string& s = GetMaterialLibFileName();
|
nuclear@0
|
99 std::string::size_type il = s.find_last_of("/\\");
|
nuclear@0
|
100 if (il != std::string::npos) {
|
nuclear@0
|
101 return s.substr(il + 1);
|
nuclear@0
|
102 }
|
nuclear@0
|
103
|
nuclear@0
|
104 return s;
|
nuclear@0
|
105 }
|
nuclear@0
|
106
|
nuclear@0
|
107 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
108 std::string ObjExporter :: GetMaterialLibFileName()
|
nuclear@0
|
109 {
|
nuclear@0
|
110 return filename + ".mtl";
|
nuclear@0
|
111 }
|
nuclear@0
|
112
|
nuclear@0
|
113 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
114 void ObjExporter :: WriteHeader(std::ostringstream& out)
|
nuclear@0
|
115 {
|
nuclear@0
|
116 out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl;
|
nuclear@0
|
117 out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl << endl;
|
nuclear@0
|
118 }
|
nuclear@0
|
119
|
nuclear@0
|
120 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
121 std::string ObjExporter :: GetMaterialName(unsigned int index)
|
nuclear@0
|
122 {
|
nuclear@0
|
123 const aiMaterial* const mat = pScene->mMaterials[index];
|
nuclear@0
|
124 aiString s;
|
nuclear@0
|
125 if(AI_SUCCESS == mat->Get(AI_MATKEY_NAME,s)) {
|
nuclear@0
|
126 return std::string(s.data,s.length);
|
nuclear@0
|
127 }
|
nuclear@0
|
128
|
nuclear@0
|
129 char number[ sizeof(unsigned int) * 3 + 1 ];
|
nuclear@0
|
130 ASSIMP_itoa10(number,index);
|
nuclear@0
|
131 return "$Material_" + std::string(number);
|
nuclear@0
|
132 }
|
nuclear@0
|
133
|
nuclear@0
|
134 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
135 void ObjExporter :: WriteMaterialFile()
|
nuclear@0
|
136 {
|
nuclear@0
|
137 WriteHeader(mOutputMat);
|
nuclear@0
|
138
|
nuclear@0
|
139 for(unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
|
nuclear@0
|
140 const aiMaterial* const mat = pScene->mMaterials[i];
|
nuclear@0
|
141
|
nuclear@0
|
142 int illum = 1;
|
nuclear@0
|
143 mOutputMat << "newmtl " << GetMaterialName(i) << endl;
|
nuclear@0
|
144
|
nuclear@0
|
145 aiColor4D c;
|
nuclear@0
|
146 if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_DIFFUSE,c)) {
|
nuclear@0
|
147 mOutputMat << "kd " << c.r << " " << c.g << " " << c.b << endl;
|
nuclear@0
|
148 }
|
nuclear@0
|
149 if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_AMBIENT,c)) {
|
nuclear@0
|
150 mOutputMat << "ka " << c.r << " " << c.g << " " << c.b << endl;
|
nuclear@0
|
151 }
|
nuclear@0
|
152 if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_SPECULAR,c)) {
|
nuclear@0
|
153 mOutputMat << "ks " << c.r << " " << c.g << " " << c.b << endl;
|
nuclear@0
|
154 }
|
nuclear@0
|
155
|
nuclear@0
|
156 float o;
|
nuclear@0
|
157 if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) {
|
nuclear@0
|
158 mOutputMat << "d " << o << endl;
|
nuclear@0
|
159 }
|
nuclear@0
|
160
|
nuclear@0
|
161 if(AI_SUCCESS == mat->Get(AI_MATKEY_SHININESS,o) && o) {
|
nuclear@0
|
162 mOutputMat << "Ns " << o << endl;
|
nuclear@0
|
163 illum = 2;
|
nuclear@0
|
164 }
|
nuclear@0
|
165
|
nuclear@0
|
166 mOutputMat << "illum " << illum << endl;
|
nuclear@0
|
167
|
nuclear@0
|
168 aiString s;
|
nuclear@0
|
169 if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_DIFFUSE(0),s)) {
|
nuclear@0
|
170 mOutputMat << "map_kd " << s.data << endl;
|
nuclear@0
|
171 }
|
nuclear@0
|
172 if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_AMBIENT(0),s)) {
|
nuclear@0
|
173 mOutputMat << "map_ka " << s.data << endl;
|
nuclear@0
|
174 }
|
nuclear@0
|
175 if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SPECULAR(0),s)) {
|
nuclear@0
|
176 mOutputMat << "map_ks " << s.data << endl;
|
nuclear@0
|
177 }
|
nuclear@0
|
178 if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SHININESS(0),s)) {
|
nuclear@0
|
179 mOutputMat << "map_ns " << s.data << endl;
|
nuclear@0
|
180 }
|
nuclear@0
|
181 if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_HEIGHT(0),s) || AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_NORMALS(0),s)) {
|
nuclear@0
|
182 // implementations seem to vary here, so write both variants
|
nuclear@0
|
183 mOutputMat << "bump " << s.data << endl;
|
nuclear@0
|
184 mOutputMat << "map_bump " << s.data << endl;
|
nuclear@0
|
185 }
|
nuclear@0
|
186
|
nuclear@0
|
187 mOutputMat << endl;
|
nuclear@0
|
188 }
|
nuclear@0
|
189 }
|
nuclear@0
|
190
|
nuclear@0
|
191 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
192 void ObjExporter :: WriteGeometryFile()
|
nuclear@0
|
193 {
|
nuclear@0
|
194 WriteHeader(mOutput);
|
nuclear@0
|
195 mOutput << "mtllib " << GetMaterialLibName() << endl << endl;
|
nuclear@0
|
196
|
nuclear@0
|
197 // collect mesh geometry
|
nuclear@0
|
198 aiMatrix4x4 mBase;
|
nuclear@0
|
199 AddNode(pScene->mRootNode,mBase);
|
nuclear@0
|
200
|
nuclear@0
|
201 // write vertex positions
|
nuclear@0
|
202 mOutput << "# " << vp.size() << " vertex positions" << endl;
|
nuclear@0
|
203 BOOST_FOREACH(const aiVector3D& v, vp) {
|
nuclear@0
|
204 mOutput << "v " << v.x << " " << v.y << " " << v.z << endl;
|
nuclear@0
|
205 }
|
nuclear@0
|
206 mOutput << endl;
|
nuclear@0
|
207
|
nuclear@0
|
208 // write uv coordinates
|
nuclear@0
|
209 mOutput << "# " << vt.size() << " UV coordinates" << endl;
|
nuclear@0
|
210 BOOST_FOREACH(const aiVector3D& v, vt) {
|
nuclear@0
|
211 mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
|
nuclear@0
|
212 }
|
nuclear@0
|
213 mOutput << endl;
|
nuclear@0
|
214
|
nuclear@0
|
215 // write vertex normals
|
nuclear@0
|
216 mOutput << "# " << vn.size() << " vertex normals" << endl;
|
nuclear@0
|
217 BOOST_FOREACH(const aiVector3D& v, vn) {
|
nuclear@0
|
218 mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
|
nuclear@0
|
219 }
|
nuclear@0
|
220 mOutput << endl;
|
nuclear@0
|
221
|
nuclear@0
|
222 // now write all mesh instances
|
nuclear@0
|
223 BOOST_FOREACH(const MeshInstance& m, meshes) {
|
nuclear@0
|
224 mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl;
|
nuclear@0
|
225 mOutput << "g " << m.name << endl;
|
nuclear@0
|
226 mOutput << "usemtl " << m.matname << endl;
|
nuclear@0
|
227
|
nuclear@0
|
228 BOOST_FOREACH(const Face& f, m.faces) {
|
nuclear@0
|
229 mOutput << f.kind << ' ';
|
nuclear@0
|
230 BOOST_FOREACH(const FaceVertex& fv, f.indices) {
|
nuclear@0
|
231 mOutput << ' ' << fv.vp;
|
nuclear@0
|
232
|
nuclear@0
|
233 if (f.kind != 'p') {
|
nuclear@0
|
234 if (fv.vt || f.kind == 'f') {
|
nuclear@0
|
235 mOutput << '/';
|
nuclear@0
|
236 }
|
nuclear@0
|
237 if (fv.vt) {
|
nuclear@0
|
238 mOutput << fv.vt;
|
nuclear@0
|
239 }
|
nuclear@0
|
240 if (f.kind == 'f') {
|
nuclear@0
|
241 mOutput << '/';
|
nuclear@0
|
242 if (fv.vn) {
|
nuclear@0
|
243 mOutput << fv.vn;
|
nuclear@0
|
244 }
|
nuclear@0
|
245 }
|
nuclear@0
|
246 }
|
nuclear@0
|
247 }
|
nuclear@0
|
248
|
nuclear@0
|
249 mOutput << endl;
|
nuclear@0
|
250 }
|
nuclear@0
|
251 mOutput << endl;
|
nuclear@0
|
252 }
|
nuclear@0
|
253 }
|
nuclear@0
|
254
|
nuclear@0
|
255 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
256 void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat)
|
nuclear@0
|
257 {
|
nuclear@0
|
258 meshes.push_back(MeshInstance());
|
nuclear@0
|
259 MeshInstance& mesh = meshes.back();
|
nuclear@0
|
260
|
nuclear@0
|
261 mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_"+std::string(m->mName.data,m->mName.length) : "");
|
nuclear@0
|
262 mesh.matname = GetMaterialName(m->mMaterialIndex);
|
nuclear@0
|
263
|
nuclear@0
|
264 mesh.faces.resize(m->mNumFaces);
|
nuclear@0
|
265 for(unsigned int i = 0; i < m->mNumFaces; ++i) {
|
nuclear@0
|
266 const aiFace& f = m->mFaces[i];
|
nuclear@0
|
267
|
nuclear@0
|
268 Face& face = mesh.faces[i];
|
nuclear@0
|
269 switch (f.mNumIndices) {
|
nuclear@0
|
270 case 1:
|
nuclear@0
|
271 face.kind = 'p';
|
nuclear@0
|
272 break;
|
nuclear@0
|
273 case 2:
|
nuclear@0
|
274 face.kind = 'l';
|
nuclear@0
|
275 break;
|
nuclear@0
|
276 default:
|
nuclear@0
|
277 face.kind = 'f';
|
nuclear@0
|
278 }
|
nuclear@0
|
279 face.indices.resize(f.mNumIndices);
|
nuclear@0
|
280
|
nuclear@0
|
281 for(unsigned int a = 0; a < f.mNumIndices; ++a) {
|
nuclear@0
|
282 const unsigned int idx = f.mIndices[a];
|
nuclear@0
|
283
|
nuclear@0
|
284 // XXX need a way to check if this is an unique vertex or if we had it already,
|
nuclear@0
|
285 // in which case we should instead reference the previous occurrence.
|
nuclear@0
|
286 ai_assert(m->mVertices);
|
nuclear@0
|
287 vp.push_back( mat * m->mVertices[idx] );
|
nuclear@0
|
288 face.indices[a].vp = vp.size();
|
nuclear@0
|
289
|
nuclear@0
|
290 if (m->mNormals) {
|
nuclear@0
|
291 vn.push_back( m->mNormals[idx] );
|
nuclear@0
|
292 }
|
nuclear@0
|
293 face.indices[a].vn = vn.size();
|
nuclear@0
|
294
|
nuclear@0
|
295 if (m->mTextureCoords[0]) {
|
nuclear@0
|
296 vt.push_back( m->mTextureCoords[0][idx] );
|
nuclear@0
|
297 }
|
nuclear@0
|
298 face.indices[a].vt = vt.size();
|
nuclear@0
|
299 }
|
nuclear@0
|
300 }
|
nuclear@0
|
301 }
|
nuclear@0
|
302
|
nuclear@0
|
303 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
304 void ObjExporter :: AddNode(const aiNode* nd, const aiMatrix4x4& mParent)
|
nuclear@0
|
305 {
|
nuclear@0
|
306 const aiMatrix4x4& mAbs = mParent * nd->mTransformation;
|
nuclear@0
|
307
|
nuclear@0
|
308 for(unsigned int i = 0; i < nd->mNumMeshes; ++i) {
|
nuclear@0
|
309 AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]],mAbs);
|
nuclear@0
|
310 }
|
nuclear@0
|
311
|
nuclear@0
|
312 for(unsigned int i = 0; i < nd->mNumChildren; ++i) {
|
nuclear@0
|
313 AddNode(nd->mChildren[i],mAbs);
|
nuclear@0
|
314 }
|
nuclear@0
|
315 }
|
nuclear@0
|
316
|
nuclear@0
|
317 #endif
|
nuclear@0
|
318 #endif
|