1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2019, assimp team
7
8
9
10 All rights reserved.
11
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15
16 * Redistributions of source code must retain the above
17 copyright notice, this list of conditions and the
18 following disclaimer.
19
20 * Redistributions in binary form must reproduce the above
21 copyright notice, this list of conditions and the
22 following disclaimer in the documentation and/or other
23 materials provided with the distribution.
24
25 * Neither the name of the assimp team, nor the names of its
26 contributors may be used to endorse or promote products
27 derived from this software without specific prior
28 written permission of the assimp team.
29
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43
44 /// \file AMFImporter.cpp
45 /// \brief AMF-format files importer for Assimp: main algorithm implementation.
46 /// \date 2016
47 /// \author smal.root@gmail.com
48
49 #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
50
51 // Header files, Assimp.
52 #include "AMFImporter.hpp"
53 #include "AMFImporter_Macro.hpp"
54
55 #include <assimp/fast_atof.h>
56 #include <assimp/DefaultIOSystem.h>
57
58 // Header files, stdlib.
59 #include <memory>
60
61 namespace Assimp
62 {
63
64 /// \var aiImporterDesc AMFImporter::Description
65 /// Conastant which hold importer description
66 const aiImporterDesc AMFImporter::Description = {
67 "Additive manufacturing file format(AMF) Importer",
68 "smalcom",
69 "",
70 "See documentation in source code. Chapter: Limitations.",
71 aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
72 0,
73 0,
74 0,
75 0,
76 "amf"
77 };
78
Clear()79 void AMFImporter::Clear()
80 {
81 mNodeElement_Cur = nullptr;
82 mUnit.clear();
83 mMaterial_Converted.clear();
84 mTexture_Converted.clear();
85 // Delete all elements
86 if(!mNodeElement_List.empty())
87 {
88 for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
89
90 mNodeElement_List.clear();
91 }
92 }
93
~AMFImporter()94 AMFImporter::~AMFImporter()
95 {
96 if(mReader != nullptr) delete mReader;
97 // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
98 Clear();
99 }
100
101 /*********************************************************************************************************************************************/
102 /************************************************************ Functions: find set ************************************************************/
103 /*********************************************************************************************************************************************/
104
Find_NodeElement(const std::string & pID,const CAMFImporter_NodeElement::EType pType,CAMFImporter_NodeElement ** pNodeElement) const105 bool AMFImporter::Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const
106 {
107 for(CAMFImporter_NodeElement* ne: mNodeElement_List)
108 {
109 if((ne->ID == pID) && (ne->Type == pType))
110 {
111 if(pNodeElement != nullptr) *pNodeElement = ne;
112
113 return true;
114 }
115 }// for(CAMFImporter_NodeElement* ne: mNodeElement_List)
116
117 return false;
118 }
119
Find_ConvertedNode(const std::string & pID,std::list<aiNode * > & pNodeList,aiNode ** pNode) const120 bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const
121 {
122 aiString node_name(pID.c_str());
123
124 for(aiNode* node: pNodeList)
125 {
126 if(node->mName == node_name)
127 {
128 if(pNode != nullptr) *pNode = node;
129
130 return true;
131 }
132 }// for(aiNode* node: pNodeList)
133
134 return false;
135 }
136
Find_ConvertedMaterial(const std::string & pID,const SPP_Material ** pConvertedMaterial) const137 bool AMFImporter::Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
138 {
139 for(const SPP_Material& mat: mMaterial_Converted)
140 {
141 if(mat.ID == pID)
142 {
143 if(pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
144
145 return true;
146 }
147 }// for(const SPP_Material& mat: mMaterial_Converted)
148
149 return false;
150 }
151
152 /*********************************************************************************************************************************************/
153 /************************************************************ Functions: throw set ***********************************************************/
154 /*********************************************************************************************************************************************/
155
Throw_CloseNotFound(const std::string & pNode)156 void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
157 {
158 throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
159 }
160
Throw_IncorrectAttr(const std::string & pAttrName)161 void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
162 {
163 throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
164 }
165
Throw_IncorrectAttrValue(const std::string & pAttrName)166 void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
167 {
168 throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
169 }
170
Throw_MoreThanOnceDefined(const std::string & pNodeType,const std::string & pDescription)171 void AMFImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
172 {
173 throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
174 }
175
Throw_ID_NotFound(const std::string & pID) const176 void AMFImporter::Throw_ID_NotFound(const std::string& pID) const
177 {
178 throw DeadlyImportError("Not found node with name \"" + pID + "\".");
179 }
180
181 /*********************************************************************************************************************************************/
182 /************************************************************* Functions: XML set ************************************************************/
183 /*********************************************************************************************************************************************/
184
XML_CheckNode_MustHaveChildren()185 void AMFImporter::XML_CheckNode_MustHaveChildren()
186 {
187 if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
188 }
189
XML_CheckNode_SkipUnsupported(const std::string & pParentNodeName)190 void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
191 {
192 static const size_t Uns_Skip_Len = 3;
193 const char* Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
194
195 static bool skipped_before[Uns_Skip_Len] = { false, false, false };
196
197 std::string nn(mReader->getNodeName());
198 bool found = false;
199 bool close_found = false;
200 size_t sk_idx;
201
202 for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
203 {
204 if(nn != Uns_Skip[sk_idx]) continue;
205
206 found = true;
207 if(mReader->isEmptyElement())
208 {
209 close_found = true;
210
211 goto casu_cres;
212 }
213
214 while(mReader->read())
215 {
216 if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
217 {
218 close_found = true;
219
220 goto casu_cres;
221 }
222 }
223 }// for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
224
225 casu_cres:
226
227 if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
228 if(!close_found) Throw_CloseNotFound(nn);
229
230 if(!skipped_before[sk_idx])
231 {
232 skipped_before[sk_idx] = true;
233 ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
234 }
235 }
236
XML_SearchNode(const std::string & pNodeName)237 bool AMFImporter::XML_SearchNode(const std::string& pNodeName)
238 {
239 while(mReader->read())
240 {
241 if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
242 }
243
244 return false;
245 }
246
XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)247 bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
248 {
249 std::string val(mReader->getAttributeValue(pAttrIdx));
250
251 if((val == "false") || (val == "0"))
252 return false;
253 else if((val == "true") || (val == "1"))
254 return true;
255 else
256 throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
257 }
258
XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)259 float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
260 {
261 std::string val;
262 float tvalf;
263
264 ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
265 fast_atoreal_move(val.c_str(), tvalf, false);
266
267 return tvalf;
268 }
269
XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)270 uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
271 {
272 return strtoul10(mReader->getAttributeValue(pAttrIdx));
273 }
274
XML_ReadNode_GetVal_AsFloat()275 float AMFImporter::XML_ReadNode_GetVal_AsFloat()
276 {
277 std::string val;
278 float tvalf;
279
280 if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
281 if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
282
283 ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
284 fast_atoreal_move(val.c_str(), tvalf, false);
285
286 return tvalf;
287 }
288
XML_ReadNode_GetVal_AsU32()289 uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32()
290 {
291 if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
292 if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
293
294 return strtoul10(mReader->getNodeData());
295 }
296
XML_ReadNode_GetVal_AsString(std::string & pValue)297 void AMFImporter::XML_ReadNode_GetVal_AsString(std::string& pValue)
298 {
299 if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
300 if(mReader->getNodeType() != irr::io::EXN_TEXT)
301 throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
302
303 pValue = mReader->getNodeData();
304 }
305
306 /*********************************************************************************************************************************************/
307 /************************************************************ Functions: parse set ***********************************************************/
308 /*********************************************************************************************************************************************/
309
ParseHelper_Node_Enter(CAMFImporter_NodeElement * pNode)310 void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
311 {
312 mNodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
313 mNodeElement_Cur = pNode;// switch current element to new one.
314 }
315
ParseHelper_Node_Exit()316 void AMFImporter::ParseHelper_Node_Exit()
317 {
318 // check if we can walk up.
319 if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
320 }
321
ParseHelper_FixTruncatedFloatString(const char * pInStr,std::string & pOutString)322 void AMFImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
323 {
324 size_t instr_len;
325
326 pOutString.clear();
327 instr_len = strlen(pInStr);
328 if(!instr_len) return;
329
330 pOutString.reserve(instr_len * 3 / 2);
331 // check and correct floats in format ".x". Must be "x.y".
332 if(pInStr[0] == '.') pOutString.push_back('0');
333
334 pOutString.push_back(pInStr[0]);
335 for(size_t ci = 1; ci < instr_len; ci++)
336 {
337 if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
338 {
339 pOutString.push_back('0');
340 pOutString.push_back('.');
341 }
342 else
343 {
344 pOutString.push_back(pInStr[ci]);
345 }
346 }
347 }
348
ParseHelper_Decode_Base64_IsBase64(const char pChar)349 static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
350 {
351 return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
352 }
353
ParseHelper_Decode_Base64(const std::string & pInputBase64,std::vector<uint8_t> & pOutputData) const354 void AMFImporter::ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const
355 {
356 // With help from
357 // René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
358 const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
359
360 uint8_t tidx = 0;
361 uint8_t arr4[4], arr3[3];
362
363 // check input data
364 if(pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
365 // prepare output place
366 pOutputData.clear();
367 pOutputData.reserve(pInputBase64.size() / 4 * 3);
368
369 for(size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--)
370 {
371 if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
372 {
373 arr4[tidx++] = pInputBase64[in_idx++];
374 if(tidx == 4)
375 {
376 for(tidx = 0; tidx < 4; tidx++) arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
377
378 arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
379 arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
380 arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
381 for(tidx = 0; tidx < 3; tidx++) pOutputData.push_back(arr3[tidx]);
382
383 tidx = 0;
384 }// if(tidx == 4)
385 }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
386 else
387 {
388 in_idx++;
389 }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
390 }
391
392 if(tidx)
393 {
394 for(uint8_t i = tidx; i < 4; i++) arr4[i] = 0;
395 for(uint8_t i = 0; i < 4; i++) arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
396
397 arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
398 arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
399 arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
400 for(uint8_t i = 0; i < (tidx - 1); i++) pOutputData.push_back(arr3[i]);
401 }
402 }
403
ParseFile(const std::string & pFile,IOSystem * pIOHandler)404 void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
405 {
406 irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
407 std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
408
409 // Check whether we can read from the file
410 if(file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
411
412 // generate a XML reader for it
413 std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
414 mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
415 if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
416 //
417 // start reading
418 // search for root tag <amf>
419 if(XML_SearchNode("amf"))
420 ParseNode_Root();
421 else
422 throw DeadlyImportError("Root node \"amf\" not found.");
423
424 delete mReader;
425 // restore old XMLreader
426 mReader = OldReader;
427 }
428
429 // <amf
430 // unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
431 // version="" - Version of file format.
432 // >
433 // </amf>
434 // Root XML element.
435 // Multi elements - No.
ParseNode_Root()436 void AMFImporter::ParseNode_Root()
437 {
438 std::string unit, version;
439 CAMFImporter_NodeElement *ne( nullptr );
440
441 // Read attributes for node <amf>.
442 MACRO_ATTRREAD_LOOPBEG;
443 MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
444 MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
445 MACRO_ATTRREAD_LOOPEND_WSKIP;
446
447 // Check attributes
448 if(!mUnit.empty())
449 {
450 if((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
451 }
452
453 // create root node element.
454 ne = new CAMFImporter_NodeElement_Root(nullptr);
455 mNodeElement_Cur = ne;// set first "current" element
456 // and assign attribute's values
457 ((CAMFImporter_NodeElement_Root*)ne)->Unit = unit;
458 ((CAMFImporter_NodeElement_Root*)ne)->Version = version;
459
460 // Check for child nodes
461 if(!mReader->isEmptyElement())
462 {
463 MACRO_NODECHECK_LOOPBEGIN("amf");
464 if(XML_CheckNode_NameEqual("object")) { ParseNode_Object(); continue; }
465 if(XML_CheckNode_NameEqual("material")) { ParseNode_Material(); continue; }
466 if(XML_CheckNode_NameEqual("texture")) { ParseNode_Texture(); continue; }
467 if(XML_CheckNode_NameEqual("constellation")) { ParseNode_Constellation(); continue; }
468 if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
469 MACRO_NODECHECK_LOOPEND("amf");
470 mNodeElement_Cur = ne;// force restore "current" element
471 }// if(!mReader->isEmptyElement())
472
473 mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
474 }
475
476 // <constellation
477 // id="" - The Object ID of the new constellation being defined.
478 // >
479 // </constellation>
480 // A collection of objects or constellations with specific relative locations.
481 // Multi elements - Yes.
482 // Parent element - <amf>.
ParseNode_Constellation()483 void AMFImporter::ParseNode_Constellation()
484 {
485 std::string id;
486 CAMFImporter_NodeElement* ne( nullptr );
487
488 // Read attributes for node <constellation>.
489 MACRO_ATTRREAD_LOOPBEG;
490 MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
491 MACRO_ATTRREAD_LOOPEND;
492
493 // create and if needed - define new grouping object.
494 ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
495
496 CAMFImporter_NodeElement_Constellation& als = *((CAMFImporter_NodeElement_Constellation*)ne);// alias for convenience
497
498 if(!id.empty()) als.ID = id;
499 // Check for child nodes
500 if(!mReader->isEmptyElement())
501 {
502 ParseHelper_Node_Enter(ne);
503 MACRO_NODECHECK_LOOPBEGIN("constellation");
504 if(XML_CheckNode_NameEqual("instance")) { ParseNode_Instance(); continue; }
505 if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
506 MACRO_NODECHECK_LOOPEND("constellation");
507 ParseHelper_Node_Exit();
508 }// if(!mReader->isEmptyElement())
509 else
510 {
511 mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
512 }// if(!mReader->isEmptyElement()) else
513
514 mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
515 }
516
517 // <instance
518 // objectid="" - The Object ID of the new constellation being defined.
519 // >
520 // </instance>
521 // A collection of objects or constellations with specific relative locations.
522 // Multi elements - Yes.
523 // Parent element - <amf>.
ParseNode_Instance()524 void AMFImporter::ParseNode_Instance()
525 {
526 std::string objectid;
527 CAMFImporter_NodeElement* ne( nullptr );
528
529 // Read attributes for node <constellation>.
530 MACRO_ATTRREAD_LOOPBEG;
531 MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
532 MACRO_ATTRREAD_LOOPEND;
533
534 // used object id must be defined, check that.
535 if(objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
536 // create and define new grouping object.
537 ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
538
539 CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);// alias for convenience
540
541 als.ObjectID = objectid;
542 // Check for child nodes
543 if(!mReader->isEmptyElement())
544 {
545 bool read_flag[6] = { false, false, false, false, false, false };
546
547 als.Delta.Set(0, 0, 0);
548 als.Rotation.Set(0, 0, 0);
549 ParseHelper_Node_Enter(ne);
550 MACRO_NODECHECK_LOOPBEGIN("instance");
551 MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
552 MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
553 MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
554 MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
555 MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
556 MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
557 MACRO_NODECHECK_LOOPEND("instance");
558 ParseHelper_Node_Exit();
559 // also convert degrees to radians.
560 als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
561 als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
562 als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
563 }// if(!mReader->isEmptyElement())
564 else
565 {
566 mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
567 }// if(!mReader->isEmptyElement()) else
568
569 mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
570 }
571
572 // <object
573 // id="" - A unique ObjectID for the new object being defined.
574 // >
575 // </object>
576 // An object definition.
577 // Multi elements - Yes.
578 // Parent element - <amf>.
ParseNode_Object()579 void AMFImporter::ParseNode_Object()
580 {
581 std::string id;
582 CAMFImporter_NodeElement* ne( nullptr );
583
584 // Read attributes for node <object>.
585 MACRO_ATTRREAD_LOOPBEG;
586 MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
587 MACRO_ATTRREAD_LOOPEND;
588
589 // create and if needed - define new geometry object.
590 ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
591
592 CAMFImporter_NodeElement_Object& als = *((CAMFImporter_NodeElement_Object*)ne);// alias for convenience
593
594 if(!id.empty()) als.ID = id;
595 // Check for child nodes
596 if(!mReader->isEmptyElement())
597 {
598 bool col_read = false;
599
600 ParseHelper_Node_Enter(ne);
601 MACRO_NODECHECK_LOOPBEGIN("object");
602 if(XML_CheckNode_NameEqual("color"))
603 {
604 // Check if color already defined for object.
605 if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
606 // read data and set flag about it
607 ParseNode_Color();
608 col_read = true;
609
610 continue;
611 }
612
613 if(XML_CheckNode_NameEqual("mesh")) { ParseNode_Mesh(); continue; }
614 if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
615 MACRO_NODECHECK_LOOPEND("object");
616 ParseHelper_Node_Exit();
617 }// if(!mReader->isEmptyElement())
618 else
619 {
620 mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
621 }// if(!mReader->isEmptyElement()) else
622
623 mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
624 }
625
626 // <metadata
627 // type="" - The type of the attribute.
628 // >
629 // </metadata>
630 // Specify additional information about an entity.
631 // Multi elements - Yes.
632 // Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
633 //
634 // Reserved types are:
635 // "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
636 // "Description" - A description of the content of the entity
637 // "URL" - A link to an external resource relating to the entity
638 // "Author" - Specifies the name(s) of the author(s) of the entity
639 // "Company" - Specifying the company generating the entity
640 // "CAD" - specifies the name of the originating CAD software and version
641 // "Revision" - specifies the revision of the entity
642 // "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
643 // "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
ParseNode_Metadata()644 void AMFImporter::ParseNode_Metadata()
645 {
646 std::string type, value;
647 CAMFImporter_NodeElement* ne( nullptr );
648
649 // read attribute
650 MACRO_ATTRREAD_LOOPBEG;
651 MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
652 MACRO_ATTRREAD_LOOPEND;
653 // and value of node.
654 value = mReader->getNodeData();
655 // Create node element and assign read data.
656 ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
657 ((CAMFImporter_NodeElement_Metadata*)ne)->Type = type;
658 ((CAMFImporter_NodeElement_Metadata*)ne)->Value = value;
659 mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
660 mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
661 }
662
663 /*********************************************************************************************************************************************/
664 /******************************************************** Functions: BaseImporter set ********************************************************/
665 /*********************************************************************************************************************************************/
666
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool pCheckSig) const667 bool AMFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
668 {
669 const std::string extension = GetExtension(pFile);
670
671 if ( extension == "amf" ) {
672 return true;
673 }
674
675 if(!extension.length() || pCheckSig)
676 {
677 const char* tokens[] = { "<amf" };
678
679 return SearchFileHeaderForToken( pIOHandler, pFile, tokens, 1 );
680 }
681
682 return false;
683 }
684
GetExtensionList(std::set<std::string> & pExtensionList)685 void AMFImporter::GetExtensionList(std::set<std::string>& pExtensionList)
686 {
687 pExtensionList.insert("amf");
688 }
689
GetInfo() const690 const aiImporterDesc* AMFImporter::GetInfo () const
691 {
692 return &Description;
693 }
694
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)695 void AMFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
696 {
697 Clear();// delete old graph.
698 ParseFile(pFile, pIOHandler);
699 Postprocess_BuildScene(pScene);
700 // scene graph is ready, exit.
701 }
702
703 }// namespace Assimp
704
705 #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER
706