1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2017, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following 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 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF 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 
62 using namespace Assimp;
63 
64 namespace Assimp
65 {
66 
67 // ------------------------------------------------------------------------------------------------
68 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
ExportSceneXFile(const char * pFile,IOSystem * pIOSystem,const aiScene * pScene,const ExportProperties * pProperties)69 void 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
XFileExporter(const aiScene * pScene,IOSystem * pIOSystem,const std::string & path,const std::string & file,const ExportProperties * pProperties)102 XFileExporter::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
~XFileExporter()121 XFileExporter::~XFileExporter()
122 {
123     if(mSceneOwned) {
124         delete mScene;
125     }
126 }
127 
128 // ------------------------------------------------------------------------------------------------
129 // Starts writing the contents
WriteFile()130 void 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
WriteHeader()154 void 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
WriteFrameTransform(aiMatrix4x4 & m)286 void 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
WriteNode(aiNode * pNode)301 void 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 
WriteMesh(aiMesh * mesh)329 void 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 
toXFileString(aiString & name)507 std::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 
writePath(const aiString & path)522 void 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