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