1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2019, 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 ----------------------------------------------------------------------
41 */
42 #ifndef ASSIMP_BUILD_NO_EXPORT
43 #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
44 
45 #include "D3MFExporter.h"
46 
47 #include <assimp/scene.h>
48 #include <assimp/IOSystem.hpp>
49 #include <assimp/IOStream.hpp>
50 #include <assimp/Exporter.hpp>
51 #include <assimp/DefaultLogger.hpp>
52 #include <assimp/StringUtils.h>
53 #include <assimp/Exceptional.h>
54 
55 #include "3MFXmlTags.h"
56 #include "D3MFOpcPackage.h"
57 
58 #ifdef ASSIMP_USE_HUNTER
59 #  include <zip/zip.h>
60 #else
61 #  include <contrib/zip/src/zip.h>
62 #endif
63 
64 namespace Assimp {
65 
ExportScene3MF(const char * pFile,IOSystem * pIOSystem,const aiScene * pScene,const ExportProperties *)66 void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
67     if ( nullptr == pIOSystem ) {
68         throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
69     }
70     D3MF::D3MFExporter myExporter( pFile, pScene );
71     if ( myExporter.validate() ) {
72         if ( pIOSystem->Exists( pFile ) ) {
73             if ( !pIOSystem->DeleteFile( pFile ) ) {
74                 throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) );
75             }
76         }
77         bool ok = myExporter.exportArchive(pFile);
78         if ( !ok ) {
79             throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
80         }
81     }
82 }
83 
84 namespace D3MF {
85 
D3MFExporter(const char * pFile,const aiScene * pScene)86 D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene )
87 : mArchiveName( pFile )
88 , m_zipArchive( nullptr )
89 , mScene( pScene )
90 , mModelOutput()
91 , mRelOutput()
92 , mContentOutput()
93 , mBuildItems()
94 , mRelations() {
95     // empty
96 }
97 
~D3MFExporter()98 D3MFExporter::~D3MFExporter() {
99     for ( size_t i = 0; i < mRelations.size(); ++i ) {
100         delete mRelations[ i ];
101     }
102     mRelations.clear();
103 }
104 
validate()105 bool D3MFExporter::validate() {
106     if ( mArchiveName.empty() ) {
107         return false;
108     }
109 
110     if ( nullptr == mScene ) {
111         return false;
112     }
113 
114     return true;
115 }
116 
exportArchive(const char * file)117 bool D3MFExporter::exportArchive( const char *file ) {
118     bool ok( true );
119 
120     m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
121     if ( nullptr == m_zipArchive ) {
122         return false;
123     }
124 
125     ok |= exportContentTypes();
126     ok |= export3DModel();
127     ok |= exportRelations();
128 
129     zip_close( m_zipArchive );
130     m_zipArchive = nullptr;
131 
132     return ok;
133 }
134 
exportContentTypes()135 bool D3MFExporter::exportContentTypes() {
136     mContentOutput.clear();
137 
138     mContentOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
139     mContentOutput << std::endl;
140     mContentOutput << "<Types xmlns = \"http://schemas.openxmlformats.org/package/2006/content-types\">";
141     mContentOutput << std::endl;
142     mContentOutput << "<Default Extension = \"rels\" ContentType = \"application/vnd.openxmlformats-package.relationships+xml\" />";
143     mContentOutput << std::endl;
144     mContentOutput << "<Default Extension = \"model\" ContentType = \"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" />";
145     mContentOutput << std::endl;
146     mContentOutput << "</Types>";
147     mContentOutput << std::endl;
148     exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
149 
150     return true;
151 }
152 
exportRelations()153 bool D3MFExporter::exportRelations() {
154     mRelOutput.clear();
155 
156     mRelOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
157     mRelOutput << std::endl;
158     mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
159 
160     for ( size_t i = 0; i < mRelations.size(); ++i ) {
161         if ( mRelations[ i ]->target[ 0 ] == '/' ) {
162             mRelOutput << "<Relationship Target=\"" << mRelations[ i ]->target << "\" ";
163         } else {
164             mRelOutput << "<Relationship Target=\"/" << mRelations[ i ]->target << "\" ";
165         }
166         mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
167         mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />";
168         mRelOutput << std::endl;
169     }
170     mRelOutput << "</Relationships>";
171     mRelOutput << std::endl;
172 
173     writeRelInfoToFile( "_rels", ".rels" );
174     mRelOutput.flush();
175 
176     return true;
177 }
178 
export3DModel()179 bool D3MFExporter::export3DModel() {
180     mModelOutput.clear();
181 
182     writeHeader();
183     mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
184             << "xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
185             << std::endl;
186     mModelOutput << "<" << XmlTag::resources << ">";
187     mModelOutput << std::endl;
188 
189     writeMetaData();
190 
191     writeBaseMaterials();
192 
193     writeObjects();
194 
195 
196     mModelOutput << "</" << XmlTag::resources << ">";
197     mModelOutput << std::endl;
198     writeBuild();
199 
200     mModelOutput << "</" << XmlTag::model << ">\n";
201 
202     OpcPackageRelationship *info = new OpcPackageRelationship;
203     info->id = "rel0";
204     info->target = "/3D/3DModel.model";
205     info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
206     mRelations.push_back( info );
207 
208     writeModelToArchive( "3D", "3DModel.model" );
209     mModelOutput.flush();
210 
211     return true;
212 }
213 
writeHeader()214 void D3MFExporter::writeHeader() {
215     mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF - 8\"?>";
216     mModelOutput << std::endl;
217 }
218 
writeMetaData()219 void D3MFExporter::writeMetaData() {
220     if ( nullptr == mScene->mMetaData ) {
221         return;
222     }
223 
224     const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties );
225     if ( 0 == numMetaEntries ) {
226         return;
227     }
228 
229 	const aiString *key = nullptr;
230     const aiMetadataEntry *entry(nullptr);
231     for ( size_t i = 0; i < numMetaEntries; ++i ) {
232         mScene->mMetaData->Get( i, key, entry );
233         std::string k( key->C_Str() );
234         aiString value;
235         mScene->mMetaData->Get(  k, value );
236         mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
237         mModelOutput << value.C_Str();
238         mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
239     }
240 }
241 
writeBaseMaterials()242 void D3MFExporter::writeBaseMaterials() {
243     mModelOutput << "<basematerials id=\"1\">\n";
244     std::string strName, hexDiffuseColor , tmp;
245     for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) {
246         aiMaterial *mat = mScene->mMaterials[ i ];
247         aiString name;
248         if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) {
249             strName = "basemat_" + to_string( i );
250         } else {
251             strName = name.C_Str();
252         }
253         aiColor4D color;
254         if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) {
255             hexDiffuseColor.clear();
256             tmp.clear();
257             hexDiffuseColor = "#";
258 
259             tmp = DecimalToHexa( color.r );
260             hexDiffuseColor += tmp;
261             tmp = DecimalToHexa( color.g );
262             hexDiffuseColor += tmp;
263             tmp = DecimalToHexa( color.b );
264             hexDiffuseColor += tmp;
265             tmp = DecimalToHexa( color.a );
266             hexDiffuseColor += tmp;
267         } else {
268             hexDiffuseColor = "#FFFFFFFF";
269         }
270 
271         mModelOutput << "<base name=\""+strName+"\" "+" displaycolor=\""+hexDiffuseColor+"\" />\n";
272     }
273     mModelOutput << "</basematerials>\n";
274 }
275 
writeObjects()276 void D3MFExporter::writeObjects() {
277     if ( nullptr == mScene->mRootNode ) {
278         return;
279     }
280 
281     aiNode *root = mScene->mRootNode;
282     for ( unsigned int i = 0; i < root->mNumChildren; ++i ) {
283         aiNode *currentNode( root->mChildren[ i ] );
284         if ( nullptr == currentNode ) {
285             continue;
286         }
287         mModelOutput << "<" << XmlTag::object << " id=\"" << currentNode->mName.C_Str() << "\" type=\"model\">";
288         mModelOutput << std::endl;
289         for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) {
290             aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ];
291             if ( nullptr == currentMesh ) {
292                 continue;
293             }
294             writeMesh( currentMesh );
295         }
296         mBuildItems.push_back( i );
297 
298         mModelOutput << "</" << XmlTag::object << ">";
299         mModelOutput << std::endl;
300     }
301 }
302 
writeMesh(aiMesh * mesh)303 void D3MFExporter::writeMesh( aiMesh *mesh ) {
304     if ( nullptr == mesh ) {
305         return;
306     }
307 
308     mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
309     mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
310     for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) {
311         writeVertex( mesh->mVertices[ i ] );
312     }
313     mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
314 
315     const unsigned int matIdx( mesh->mMaterialIndex );
316 
317     writeFaces( mesh, matIdx );
318 
319     mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
320 }
321 
writeVertex(const aiVector3D & pos)322 void D3MFExporter::writeVertex( const aiVector3D &pos ) {
323     mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
324     mModelOutput << std::endl;
325 }
326 
writeFaces(aiMesh * mesh,unsigned int matIdx)327 void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
328     if ( nullptr == mesh ) {
329         return;
330     }
331 
332     if ( !mesh->HasFaces() ) {
333         return;
334     }
335     mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
336     for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) {
337         aiFace &currentFace = mesh->mFaces[ i ];
338         mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\""
339                 << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ]
340                 << "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />";
341         mModelOutput << std::endl;
342     }
343     mModelOutput << "</" << XmlTag::triangles << ">";
344     mModelOutput << std::endl;
345 }
346 
writeBuild()347 void D3MFExporter::writeBuild() {
348     mModelOutput << "<" << XmlTag::build << ">" << std::endl;
349 
350     for ( size_t i = 0; i < mBuildItems.size(); ++i ) {
351         mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>";
352         mModelOutput << std::endl;
353     }
354     mModelOutput << "</" << XmlTag::build << ">";
355     mModelOutput << std::endl;
356 }
357 
exportContentTyp(const std::string & filename)358 void D3MFExporter::exportContentTyp( const std::string &filename ) {
359     if ( nullptr == m_zipArchive ) {
360         throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
361     }
362     const std::string entry = filename;
363     zip_entry_open( m_zipArchive, entry.c_str() );
364 
365     const std::string &exportTxt( mContentOutput.str() );
366     zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
367 
368     zip_entry_close( m_zipArchive );
369 }
370 
writeModelToArchive(const std::string & folder,const std::string & modelName)371 void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) {
372     if ( nullptr == m_zipArchive ) {
373         throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
374     }
375     const std::string entry = folder + "/" + modelName;
376     zip_entry_open( m_zipArchive, entry.c_str() );
377 
378     const std::string &exportTxt( mModelOutput.str() );
379     zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
380 
381     zip_entry_close( m_zipArchive );
382 }
383 
writeRelInfoToFile(const std::string & folder,const std::string & relName)384 void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) {
385     if ( nullptr == m_zipArchive ) {
386         throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
387     }
388     const std::string entry = folder + "/" + relName;
389     zip_entry_open( m_zipArchive, entry.c_str() );
390 
391     const std::string &exportTxt( mRelOutput.str() );
392     zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
393 
394     zip_entry_close( m_zipArchive );
395 }
396 
397 
398 } // Namespace D3MF
399 } // Namespace Assimp
400 
401 #endif // ASSIMP_BUILD_NO_3MF_EXPORTER
402 #endif // ASSIMP_BUILD_NO_EXPORT
403