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