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 ¤tFace = 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