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
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
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
42#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
43
44#include "D3MFImporter.h"
45
46#include <assimp/scene.h>
47#include <assimp/IOSystem.hpp>
48#include <assimp/DefaultLogger.hpp>
49#include <assimp/importerdesc.h>
50#include "StringComparison.h"
51#include "StringUtils.h"
52
53#include <string>
54#include <vector>
55#include <map>
56#include <cassert>
57#include <memory>
58
59#include "D3MFOpcPackage.h"
60#include <contrib/unzip/unzip.h>
61#include "irrXMLWrapper.h"
62#include "3MFXmlTags.h"
63
64namespace Assimp {
65namespace D3MF {
66
67class XmlSerializer {
68public:
69 XmlSerializer(XmlReader* xmlReader)
70 : xmlReader(xmlReader) {
71 // empty
72 }
73
74 ~XmlSerializer() {
75 // empty
76 }
77
78 void ImportXml(aiScene* scene) {
79 scene->mRootNode = new aiNode();
80 std::vector<aiNode*> children;
81
82 while(ReadToEndElement(D3MF::XmlTag::model)) {
83 if(xmlReader->getNodeName() == D3MF::XmlTag::object) {
84 children.push_back(ReadObject(scene));
85 } else if(xmlReader->getNodeName() == D3MF::XmlTag::build) {
86
87 }
88 }
89
90 if ( scene->mRootNode->mName.length == 0 ) {
91 scene->mRootNode->mName.Set( "3MF" );
92 }
93
94 scene->mNumMeshes = static_cast<unsigned int>(meshes.size());
95 scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
96
97 std::copy(meshes.begin(), meshes.end(), scene->mMeshes);
98
99 scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
100 scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
101
102 std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
103 }
104
105private:
106 aiNode* ReadObject(aiScene* scene)
107 {
108 std::unique_ptr<aiNode> node(new aiNode());
109
110 std::vector<unsigned long> meshIds;
111
112 const char *attrib( nullptr );
113 std::string name, type;
114 attrib = xmlReader->getAttributeValue( D3MF::XmlTag::name.c_str() );
115 if ( nullptr != attrib ) {
116 name = attrib;
117 }
118 attrib = xmlReader->getAttributeValue( D3MF::XmlTag::name.c_str() );
119 if ( nullptr != attrib ) {
120 type = attrib;
121 }
122
123 node->mParent = scene->mRootNode;
124 node->mName.Set(name);
125
126 size_t meshIdx = meshes.size();
127
128 while(ReadToEndElement(D3MF::XmlTag::object))
129 {
130 if(xmlReader->getNodeName() == D3MF::XmlTag::mesh)
131 {
132 auto mesh = ReadMesh();
133
134 mesh->mName.Set(name);
135 meshes.push_back(mesh);
136 meshIds.push_back(static_cast<unsigned long>(meshIdx));
137 meshIdx++;
138
139 }
140 }
141
142 node->mNumMeshes = static_cast<unsigned int>(meshIds.size());
143
144 node->mMeshes = new unsigned int[node->mNumMeshes];
145
146 std::copy(meshIds.begin(), meshIds.end(), node->mMeshes);
147
148 return node.release();
149
150 }
151
152 aiMesh* ReadMesh() {
153 aiMesh* mesh = new aiMesh();
154 while(ReadToEndElement(D3MF::XmlTag::mesh))
155 {
156 if(xmlReader->getNodeName() == D3MF::XmlTag::vertices)
157 {
158 ImportVertices(mesh);
159 }
160 else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles)
161 {
162 ImportTriangles(mesh);
163 }
164 }
165
166 return mesh;
167 }
168
169 void ImportVertices(aiMesh* mesh)
170 {
171 std::vector<aiVector3D> vertices;
172
173 while(ReadToEndElement(D3MF::XmlTag::vertices))
174 {
175 if(xmlReader->getNodeName() == D3MF::XmlTag::vertex)
176 {
177 vertices.push_back(ReadVertex());
178 }
179 }
180 mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
181 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
182
183 std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
184
185 }
186
187 aiVector3D ReadVertex()
188 {
189 aiVector3D vertex;
190
191 vertex.x = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::x.c_str()), nullptr);
192 vertex.y = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::y.c_str()), nullptr);
193 vertex.z = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::z.c_str()), nullptr);
194
195 return vertex;
196 }
197
198 void ImportTriangles(aiMesh* mesh)
199 {
200 std::vector<aiFace> faces;
201
202
203 while(ReadToEndElement(D3MF::XmlTag::triangles))
204 {
205 if(xmlReader->getNodeName() == D3MF::XmlTag::triangle)
206 {
207 faces.push_back(ReadTriangle());
208 }
209 }
210
211 mesh->mNumFaces = static_cast<unsigned int>(faces.size());
212 mesh->mFaces = new aiFace[mesh->mNumFaces];
213 mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
214
215 std::copy(faces.begin(), faces.end(), mesh->mFaces);
216 }
217
218 aiFace ReadTriangle()
219 {
220 aiFace face;
221
222 face.mNumIndices = 3;
223 face.mIndices = new unsigned int[face.mNumIndices];
224 face.mIndices[0] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v1.c_str())));
225 face.mIndices[1] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v2.c_str())));
226 face.mIndices[2] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v3.c_str())));
227
228 return face;
229 }
230
231private:
232 bool ReadToStartElement(const std::string& startTag)
233 {
234 while(xmlReader->read())
235 {
236 if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && xmlReader->getNodeName() == startTag)
237 {
238 return true;
239 }
240 else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END &&
241 xmlReader->getNodeName() == startTag)
242 {
243 return false;
244 }
245 }
246 //DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag");
247 return false;
248 }
249
250 bool ReadToEndElement(const std::string& closeTag)
251 {
252 while(xmlReader->read())
253 {
254 if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) {
255 return true;
256 }
257 else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END
258 && xmlReader->getNodeName() == closeTag)
259 {
260 return false;
261 }
262 }
263 DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag");
264 return false;
265 }
266
267
268private:
269 std::vector<aiMesh*> meshes;
270 XmlReader* xmlReader;
271};
272
273} //namespace D3MF
274
275static const std::string Extension = "3mf";
276
277static const aiImporterDesc desc = {
278 "3mf Importer",
279 "",
280 "",
281 "http://3mf.io/",
282 aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour,
283 0,
284 0,
285 0,
286 0,
287 Extension.c_str()
288};
289
290
291D3MFImporter::D3MFImporter()
292: BaseImporter() {
293 // empty
294}
295
296D3MFImporter::~D3MFImporter() {
297 // empty
298}
299
300bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
301 const std::string extension = GetExtension(pFile);
302 if(extension == Extension ) {
303 return true;
304 } else if ( !extension.length() || checkSig ) {
305 if (nullptr == pIOHandler ) {
306 return true;
307 }
308 }
309
310 return false;
311}
312
313void D3MFImporter::SetupProperties(const Importer * /*pImp*/) {
314 // empty
315}
316
317const aiImporterDesc *D3MFImporter::GetInfo() const {
318 return &desc;
319}
320
321void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
322 D3MF::D3MFOpcPackage opcPackage(pIOHandler, pFile);
323
324 std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream()));
325 std::unique_ptr<D3MF::XmlReader> xmlReader(irr::io::createIrrXMLReader(xmlStream.get()));
326
327 D3MF::XmlSerializer xmlSerializer(xmlReader.get());
328
329 xmlSerializer.ImportXml(pScene);
330}
331
332} // Namespace Assimp
333
334#endif // ASSIMP_BUILD_NO_3MF_IMPORTER
335