1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF 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
58namespace Assimp {
59
60void 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
78namespace D3MF {
79
80D3MFExporter::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
92D3MFExporter::~D3MFExporter() {
93 for ( size_t i = 0; i < mRelations.size(); ++i ) {
94 delete mRelations[ i ];
95 }
96 mRelations.clear();
97}
98
99bool 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
111bool 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
129bool 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
147bool 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
169bool 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
200void D3MFExporter::writeHeader() {
201 mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF - 8\"?>";
202 mModelOutput << std::endl;
203}
204
205void 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
232void 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
249void D3MFExporter::writeVertex( const aiVector3D &pos ) {
250 mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
251 mModelOutput << std::endl;
252}
253
254void 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
273void 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
284void 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
297void 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
310void 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