1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16 copyright notice, this list of conditions and the
17 following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the
21 following disclaimer in the documentation and/or other
22 materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25 contributors may be used to endorse or promote products
26 derived from this software without specific prior
27 written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF 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 
60 namespace Assimp
61 {
62 
63 /// \var aiImporterDesc AMFImporter::Description
64 /// Conastant which hold importer description
65 const 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 
Clear()78 void 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 
~AMFImporter()93 AMFImporter::~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 
Find_NodeElement(const std::string & pID,const CAMFImporter_NodeElement::EType pType,CAMFImporter_NodeElement ** pNodeElement) const104 bool 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 
Find_ConvertedNode(const std::string & pID,std::list<aiNode * > & pNodeList,aiNode ** pNode) const119 bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const
120 {
121 aiString 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 
Find_ConvertedMaterial(const std::string & pID,const SPP_Material ** pConvertedMaterial) const136 bool 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 
Throw_CloseNotFound(const std::string & pNode)155 void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
156 {
157 	throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
158 }
159 
Throw_IncorrectAttr(const std::string & pAttrName)160 void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
161 {
162 	throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
163 }
164 
Throw_IncorrectAttrValue(const std::string & pAttrName)165 void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
166 {
167 	throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
168 }
169 
Throw_MoreThanOnceDefined(const std::string & pNodeType,const std::string & pDescription)170 void 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 
Throw_ID_NotFound(const std::string & pID) const175 void 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 
XML_CheckNode_MustHaveChildren()184 void AMFImporter::XML_CheckNode_MustHaveChildren()
185 {
186 	if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
187 }
188 
XML_CheckNode_SkipUnsupported(const std::string & pParentNodeName)189 void 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 
224 casu_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 
XML_SearchNode(const std::string & pNodeName)236 bool 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 
XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)246 bool 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 
XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)258 float 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 
XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)269 uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
270 {
271 	return strtoul10(mReader->getAttributeValue(pAttrIdx));
272 }
273 
XML_ReadNode_GetVal_AsFloat()274 float 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 
XML_ReadNode_GetVal_AsU32()288 uint32_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 
XML_ReadNode_GetVal_AsString(std::string & pValue)296 void 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 
ParseHelper_Node_Enter(CAMFImporter_NodeElement * pNode)309 void 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 
ParseHelper_Node_Exit()315 void AMFImporter::ParseHelper_Node_Exit()
316 {
317 	// check if we can walk up.
318 	if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
319 }
320 
ParseHelper_FixTruncatedFloatString(const char * pInStr,std::string & pOutString)321 void 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 
ParseHelper_Decode_Base64_IsBase64(const char pChar)348 static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
349 {
350 	return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
351 }
352 
ParseHelper_Decode_Base64(const std::string & pInputBase64,std::vector<uint8_t> & pOutputData) const353 void 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 
ParseFile(const std::string & pFile,IOSystem * pIOHandler)403 void 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.
ParseNode_Root()435 void 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>.
ParseNode_Constellation()482 void 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>.
ParseNode_Instance()523 void 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>.
ParseNode_Object()578 void 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)
ParseNode_Metadata()643 void 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 
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool pCheckSig) const666 bool 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 
GetExtensionList(std::set<std::string> & pExtensionList)684 void AMFImporter::GetExtensionList(std::set<std::string>& pExtensionList)
685 {
686 	pExtensionList.insert("amf");
687 }
688 
GetInfo() const689 const aiImporterDesc* AMFImporter::GetInfo () const
690 {
691 	return &Description;
692 }
693 
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)694 void 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