1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39@author: Richard Steffen, 2014
40----------------------------------------------------------------------
41*/
42
43
44#ifndef ASSIMP_BUILD_NO_EXPORT
45#ifndef ASSIMP_BUILD_NO_X_EXPORTER
46
47#include "XFileExporter.h"
48#include "ConvertToLHProcess.h"
49#include "Bitmap.h"
50#include "BaseImporter.h"
51#include "fast_atof.h"
52#include <assimp/SceneCombiner.h>
53#include <assimp/DefaultIOSystem.h>
54#include <ctime>
55#include <set>
56#include <memory>
57#include "Exceptional.h"
58#include <assimp/IOSystem.hpp>
59#include <assimp/scene.h>
60#include <assimp/light.h>
61
62using namespace Assimp;
63
64namespace Assimp
65{
66
67// ------------------------------------------------------------------------------------------------
68// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
69void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
70{
71 std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
72 std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
73
74 // create/copy Properties
75 ExportProperties props(*pProperties);
76
77 // set standard properties if not set
78 if (!props.HasPropertyBool(AI_CONFIG_EXPORT_XFILE_64BIT)) props.SetPropertyBool(AI_CONFIG_EXPORT_XFILE_64BIT, false);
79
80 // invoke the exporter
81 XFileExporter iDoTheExportThing( pScene, pIOSystem, path, file, &props);
82
83 if (iDoTheExportThing.mOutput.fail()) {
84 throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
85 }
86
87 // we're still here - export successfully completed. Write result to the given IOSYstem
88 std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
89 if(outfile == NULL) {
90 throw DeadlyExportError("could not open output .x file: " + std::string(pFile));
91 }
92
93 // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy.
94 outfile->Write( iDoTheExportThing.mOutput.str().c_str(), static_cast<size_t>(iDoTheExportThing.mOutput.tellp()),1);
95}
96
97} // end of namespace Assimp
98
99
100// ------------------------------------------------------------------------------------------------
101// Constructor for a specific scene to export
102XFileExporter::XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file, const ExportProperties* pProperties)
103 : mProperties(pProperties),
104 mIOSystem(pIOSystem),
105 mPath(path),
106 mFile(file),
107 mScene(pScene),
108 mSceneOwned(false),
109 endstr("\n")
110{
111 // make sure that all formatting happens using the standard, C locale and not the user's current locale
112 mOutput.imbue( std::locale("C") );
113 mOutput.precision(16);
114
115 // start writing
116 WriteFile();
117}
118
119// ------------------------------------------------------------------------------------------------
120// Destructor
121XFileExporter::~XFileExporter()
122{
123 if(mSceneOwned) {
124 delete mScene;
125 }
126}
127
128// ------------------------------------------------------------------------------------------------
129// Starts writing the contents
130void XFileExporter::WriteFile()
131{
132 // note, that all realnumber values must be comma separated in x files
133 mOutput.setf(std::ios::fixed);
134 mOutput.precision(16); // precission for double
135
136 // entry of writing the file
137 WriteHeader();
138
139 mOutput << startstr << "Frame DXCC_ROOT {" << endstr;
140 PushTag();
141
142 aiMatrix4x4 I; // identity
143 WriteFrameTransform(I);
144
145 WriteNode(mScene->mRootNode);
146 PopTag();
147
148 mOutput << startstr << "}" << endstr;
149
150}
151
152// ------------------------------------------------------------------------------------------------
153// Writes the asset header
154void XFileExporter::WriteHeader()
155{
156 if (mProperties->GetPropertyBool(AI_CONFIG_EXPORT_XFILE_64BIT) == true)
157 mOutput << startstr << "xof 0303txt 0064" << endstr;
158 else
159 mOutput << startstr << "xof 0303txt 0032" << endstr;
160 mOutput << endstr;
161 mOutput << startstr << "template Frame {" << endstr;
162 PushTag();
163 mOutput << startstr << "<3d82ab46-62da-11cf-ab39-0020af71e433>" << endstr;
164 mOutput << startstr << "[...]" << endstr;
165 PopTag();
166 mOutput << startstr << "}" << endstr;
167 mOutput << endstr;
168 mOutput << startstr << "template Matrix4x4 {" << endstr;
169 PushTag();
170 mOutput << startstr << "<f6f23f45-7686-11cf-8f52-0040333594a3>" << endstr;
171 mOutput << startstr << "array FLOAT matrix[16];" << endstr;
172 PopTag();
173 mOutput << startstr << "}" << endstr;
174 mOutput << endstr;
175 mOutput << startstr << "template FrameTransformMatrix {" << endstr;
176 PushTag();
177 mOutput << startstr << "<f6f23f41-7686-11cf-8f52-0040333594a3>" << endstr;
178 mOutput << startstr << "Matrix4x4 frameMatrix;" << endstr;
179 PopTag();
180 mOutput << startstr << "}" << endstr;
181 mOutput << endstr;
182 mOutput << startstr << "template Vector {" << endstr;
183 PushTag();
184 mOutput << startstr << "<3d82ab5e-62da-11cf-ab39-0020af71e433>" << endstr;
185 mOutput << startstr << "FLOAT x;" << endstr;
186 mOutput << startstr << "FLOAT y;" << endstr;
187 mOutput << startstr << "FLOAT z;" << endstr;
188 PopTag();
189 mOutput << startstr << "}" << endstr;
190 mOutput << endstr;
191 mOutput << startstr << "template MeshFace {" << endstr;
192 PushTag();
193 mOutput << startstr << "<3d82ab5f-62da-11cf-ab39-0020af71e433>" << endstr;
194 mOutput << startstr << "DWORD nFaceVertexIndices;" << endstr;
195 mOutput << startstr << "array DWORD faceVertexIndices[nFaceVertexIndices];" << endstr;
196 PopTag();
197 mOutput << startstr << "}" << endstr;
198 mOutput << endstr;
199 mOutput << startstr << "template Mesh {" << endstr;
200 PushTag();
201 mOutput << startstr << "<3d82ab44-62da-11cf-ab39-0020af71e433>" << endstr;
202 mOutput << startstr << "DWORD nVertices;" << endstr;
203 mOutput << startstr << "array Vector vertices[nVertices];" << endstr;
204 mOutput << startstr << "DWORD nFaces;" << endstr;
205 mOutput << startstr << "array MeshFace faces[nFaces];" << endstr;
206 mOutput << startstr << "[...]" << endstr;
207 PopTag();
208 mOutput << startstr << "}" << endstr;
209 mOutput << endstr;
210 mOutput << startstr << "template MeshNormals {" << endstr;
211 PushTag();
212 mOutput << startstr << "<f6f23f43-7686-11cf-8f52-0040333594a3>" << endstr;
213 mOutput << startstr << "DWORD nNormals;" << endstr;
214 mOutput << startstr << "array Vector normals[nNormals];" << endstr;
215 mOutput << startstr << "DWORD nFaceNormals;" << endstr;
216 mOutput << startstr << "array MeshFace faceNormals[nFaceNormals];" << endstr;
217 PopTag();
218 mOutput << startstr << "}" << endstr;
219 mOutput << endstr;
220 mOutput << startstr << "template Coords2d {" << endstr;
221 PushTag();
222 mOutput << startstr << "<f6f23f44-7686-11cf-8f52-0040333594a3>" << endstr;
223 mOutput << startstr << "FLOAT u;" << endstr;
224 mOutput << startstr << "FLOAT v;" << endstr;
225 PopTag();
226 mOutput << startstr << "}" << endstr;
227 mOutput << endstr;
228 mOutput << startstr << "template MeshTextureCoords {" << endstr;
229 PushTag();
230 mOutput << startstr << "<f6f23f40-7686-11cf-8f52-0040333594a3>" << endstr;
231 mOutput << startstr << "DWORD nTextureCoords;" << endstr;
232 mOutput << startstr << "array Coords2d textureCoords[nTextureCoords];" << endstr;
233 PopTag();
234 mOutput << startstr << "}" << endstr;
235 mOutput << endstr;
236 mOutput << startstr << "template ColorRGBA {" << endstr;
237 PushTag();
238 mOutput << startstr << "<35ff44e0-6c7c-11cf-8f52-0040333594a3>" << endstr;
239 mOutput << startstr << "FLOAT red;" << endstr;
240 mOutput << startstr << "FLOAT green;" << endstr;
241 mOutput << startstr << "FLOAT blue;" << endstr;
242 mOutput << startstr << "FLOAT alpha;" << endstr;
243 PopTag();
244 mOutput << startstr << "}" << endstr;
245 mOutput << endstr;
246 mOutput << startstr << "template IndexedColor {" << endstr;
247 PushTag();
248 mOutput << startstr << "<1630b820-7842-11cf-8f52-0040333594a3>" << endstr;
249 mOutput << startstr << "DWORD index;" << endstr;
250 mOutput << startstr << "ColorRGBA indexColor;" << endstr;
251 PopTag();
252 mOutput << startstr << "}" << endstr;
253 mOutput << endstr;
254 mOutput << startstr << "template MeshVertexColors {" << endstr;
255 PushTag();
256 mOutput << startstr << "<1630b821-7842-11cf-8f52-0040333594a3>" << endstr;
257 mOutput << startstr << "DWORD nVertexColors;" << endstr;
258 mOutput << startstr << "array IndexedColor vertexColors[nVertexColors];" << endstr;
259 PopTag();
260 mOutput << startstr << "}" << endstr;
261 mOutput << endstr;
262 mOutput << startstr << "template VertexElement {" << endstr;
263 PushTag();
264 mOutput << startstr << "<f752461c-1e23-48f6-b9f8-8350850f336f>" << endstr;
265 mOutput << startstr << "DWORD Type;" << endstr;
266 mOutput << startstr << "DWORD Method;" << endstr;
267 mOutput << startstr << "DWORD Usage;" << endstr;
268 mOutput << startstr << "DWORD UsageIndex;" << endstr;
269 PopTag();
270 mOutput << startstr << "}" << endstr;
271 mOutput << endstr;
272 mOutput << startstr << "template DeclData {" << endstr;
273 PushTag();
274 mOutput << startstr << "<bf22e553-292c-4781-9fea-62bd554bdd93>" << endstr;
275 mOutput << startstr << "DWORD nElements;" << endstr;
276 mOutput << startstr << "array VertexElement Elements[nElements];" << endstr;
277 mOutput << startstr << "DWORD nDWords;" << endstr;
278 mOutput << startstr << "array DWORD data[nDWords];" << endstr;
279 PopTag();
280 mOutput << startstr << "}" << endstr;
281 mOutput << endstr;
282}
283
284
285// Writes the material setup
286void XFileExporter::WriteFrameTransform(aiMatrix4x4& m)
287{
288 mOutput << startstr << "FrameTransformMatrix {" << endstr << " ";
289 PushTag();
290 mOutput << startstr << m.a1 << ", " << m.b1 << ", " << m.c1 << ", " << m.d1 << "," << endstr;
291 mOutput << startstr << m.a2 << ", " << m.b2 << ", " << m.c2 << ", " << m.d2 << "," << endstr;
292 mOutput << startstr << m.a3 << ", " << m.b3 << ", " << m.c3 << ", " << m.d3 << "," << endstr;
293 mOutput << startstr << m.a4 << ", " << m.b4 << ", " << m.c4 << ", " << m.d4 << ";;" << endstr;
294 PopTag();
295 mOutput << startstr << "}" << endstr << endstr;
296}
297
298
299// ------------------------------------------------------------------------------------------------
300// Recursively writes the given node
301void XFileExporter::WriteNode( aiNode* pNode)
302{
303 if (pNode->mName.length==0)
304 {
305 std::stringstream ss;
306 ss << "Node_" << pNode;
307 pNode->mName.Set(ss.str());
308 }
309 mOutput << startstr << "Frame " << toXFileString(pNode->mName) << " {" << endstr;
310
311 PushTag();
312
313 aiMatrix4x4 m = pNode->mTransformation;
314
315 WriteFrameTransform(m);
316
317 for (size_t i = 0; i < pNode->mNumMeshes; ++i)
318 WriteMesh(mScene->mMeshes[pNode->mMeshes[i]]);
319
320 // recursive call the Nodes
321 for (size_t i = 0; i < pNode->mNumChildren; ++i)
322 WriteNode(pNode->mChildren[i]);
323
324 PopTag();
325
326 mOutput << startstr << "}" << endstr << endstr;
327}
328
329void XFileExporter::WriteMesh(aiMesh* mesh)
330{
331 mOutput << startstr << "Mesh " << toXFileString(mesh->mName) << "_mShape" << " {" << endstr;
332
333 PushTag();
334
335 // write all the vertices
336 mOutput << startstr << mesh->mNumVertices << ";" << endstr;
337 for (size_t a = 0; a < mesh->mNumVertices; a++)
338 {
339 aiVector3D &v = mesh->mVertices[a];
340 mOutput << startstr << v[0] << ";"<< v[1] << ";" << v[2] << ";";
341 if (a < mesh->mNumVertices - 1)
342 mOutput << "," << endstr;
343 else
344 mOutput << ";" << endstr;
345 }
346
347 // write all the faces
348 mOutput << startstr << mesh->mNumFaces << ";" << endstr;
349 for( size_t a = 0; a < mesh->mNumFaces; ++a )
350 {
351 const aiFace& face = mesh->mFaces[a];
352 mOutput << startstr << face.mNumIndices << ";";
353 // must be counter clockwise triangle
354 //for(int b = face.mNumIndices - 1; b >= 0 ; --b)
355 for(size_t b = 0; b < face.mNumIndices ; ++b)
356 {
357 mOutput << face.mIndices[b];
358 //if (b > 0)
359 if (b<face.mNumIndices-1)
360 mOutput << ",";
361 else
362 mOutput << ";";
363 }
364
365 if (a < mesh->mNumFaces - 1)
366 mOutput << "," << endstr;
367 else
368 mOutput << ";" << endstr;
369 }
370
371 mOutput << endstr;
372
373 if (mesh->HasTextureCoords(0))
374 {
375 const aiMaterial* mat = mScene->mMaterials[mesh->mMaterialIndex];
376 aiString relpath;
377 mat->Get(_AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE, 0, relpath);
378
379 mOutput << startstr << "MeshMaterialList {" << endstr;
380 PushTag();
381 mOutput << startstr << "1;" << endstr; // number of materials
382 mOutput << startstr << mesh->mNumFaces << ";" << endstr; // number of faces
383 mOutput << startstr;
384 for( size_t a = 0; a < mesh->mNumFaces; ++a )
385 {
386 mOutput << "0"; // the material index
387 if (a < mesh->mNumFaces - 1)
388 mOutput << ", ";
389 else
390 mOutput << ";" << endstr;
391 }
392 mOutput << startstr << "Material {" << endstr;
393 PushTag();
394 mOutput << startstr << "1.0; 1.0; 1.0; 1.000000;;" << endstr;
395 mOutput << startstr << "1.000000;" << endstr; // power
396 mOutput << startstr << "0.000000; 0.000000; 0.000000;;" << endstr; // specularity
397 mOutput << startstr << "0.000000; 0.000000; 0.000000;;" << endstr; // emission
398 mOutput << startstr << "TextureFilename { \"";
399
400 writePath(relpath);
401
402 mOutput << "\"; }" << endstr;
403 PopTag();
404 mOutput << startstr << "}" << endstr;
405 PopTag();
406 mOutput << startstr << "}" << endstr;
407 }
408
409 // write normals (every vertex has one)
410 if (mesh->HasNormals())
411 {
412 mOutput << endstr << startstr << "MeshNormals {" << endstr;
413 mOutput << startstr << mesh->mNumVertices << ";" << endstr;
414 for (size_t a = 0; a < mesh->mNumVertices; a++)
415 {
416 aiVector3D &v = mesh->mNormals[a];
417 // because we have a LHS and also changed wth winding, we need to invert the normals again
418 mOutput << startstr << -v[0] << ";"<< -v[1] << ";" << -v[2] << ";";
419 if (a < mesh->mNumVertices - 1)
420 mOutput << "," << endstr;
421 else
422 mOutput << ";" << endstr;
423 }
424
425 mOutput << startstr << mesh->mNumFaces << ";" << endstr;
426 for (size_t a = 0; a < mesh->mNumFaces; a++)
427 {
428 const aiFace& face = mesh->mFaces[a];
429 mOutput << startstr << face.mNumIndices << ";";
430
431 //for(int b = face.mNumIndices-1; b >= 0 ; --b)
432 for(size_t b = 0; b < face.mNumIndices ; ++b)
433 {
434 mOutput << face.mIndices[b];
435 //if (b > 0)
436 if (b<face.mNumIndices-1)
437 mOutput << ",";
438 else
439 mOutput << ";";
440 }
441
442 if (a < mesh->mNumFaces-1)
443 mOutput << "," << endstr;
444 else
445 mOutput << ";" << endstr;
446 }
447 mOutput << startstr << "}" << endstr;
448 }
449
450 // write texture UVs if available
451 if (mesh->HasTextureCoords(0))
452 {
453 mOutput << endstr << startstr << "MeshTextureCoords {" << endstr;
454 mOutput << startstr << mesh->mNumVertices << ";" << endstr;
455 for (size_t a = 0; a < mesh->mNumVertices; a++)
456 //for (int a = (int)mesh->mNumVertices-1; a >=0 ; a--)
457 {
458 aiVector3D& uv = mesh->mTextureCoords[0][a]; // uv of first uv layer for the vertex
459 mOutput << startstr << uv.x << ";" << uv.y;
460 if (a < mesh->mNumVertices-1)
461 //if (a >0 )
462 mOutput << ";," << endstr;
463 else
464 mOutput << ";;" << endstr;
465 }
466 mOutput << startstr << "}" << endstr;
467 }
468
469 // write color channel if available
470 if (mesh->HasVertexColors(0))
471 {
472 mOutput << endstr << startstr << "MeshVertexColors {" << endstr;
473 mOutput << startstr << mesh->mNumVertices << ";" << endstr;
474 for (size_t a = 0; a < mesh->mNumVertices; a++)
475 {
476 aiColor4D& mColors = mesh->mColors[0][a]; // color of first vertex color set for the vertex
477 mOutput << startstr << a << ";" << mColors.r << ";" << mColors.g << ";" << mColors.b << ";" << mColors.a << ";;";
478 if (a < mesh->mNumVertices-1)
479 mOutput << "," << endstr;
480 else
481 mOutput << ";" << endstr;
482 }
483 mOutput << startstr << "}" << endstr;
484 }
485 /*
486 else
487 {
488 mOutput << endstr << startstr << "MeshVertexColors {" << endstr;
489 mOutput << startstr << mesh->mNumVertices << ";" << endstr;
490 for (size_t a = 0; a < mesh->mNumVertices; a++)
491 {
492 aiColor4D* mColors = mesh->mColors[a];
493 mOutput << startstr << a << ";0.500000;0.000000;0.000000;0.500000;;";
494 if (a < mesh->mNumVertices-1)
495 mOutput << "," << endstr;
496 else
497 mOutput << ";" << endstr;
498 }
499 mOutput << startstr << "}" << endstr;
500 }
501 */
502 PopTag();
503 mOutput << startstr << "}" << endstr << endstr;
504
505}
506
507std::string XFileExporter::toXFileString(aiString &name)
508{
509 std::string pref = ""; // node name prefix to prevent unexpected start of string
510 std::string str = pref + std::string(name.C_Str());
511 for (int i=0; i < (int) str.length(); ++i)
512 {
513 if ((str[i] >= '0' && str[i] <= '9') || // 0-9
514 (str[i] >= 'A' && str[i] <= 'Z') || // A-Z
515 (str[i] >= 'a' && str[i] <= 'z')) // a-z
516 continue;
517 str[i] = '_';
518 }
519 return str;
520}
521
522void XFileExporter::writePath(const aiString &path)
523{
524 std::string str = std::string(path.C_Str());
525 BaseImporter::ConvertUTF8toISO8859_1(str);
526
527 while( str.find( "\\\\") != std::string::npos)
528 str.replace( str.find( "\\\\"), 2, "\\");
529
530 while( str.find( "\\") != std::string::npos)
531 str.replace( str.find( "\\"), 1, "/");
532
533 mOutput << str;
534
535}
536
537#endif
538#endif
539