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