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