/src/assimp/code/AssetLib/AMF/AMFImporter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2024, assimp team |
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 following |
12 | | 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_AMF_IMPORTER |
43 | | |
44 | | // Header files, Assimp. |
45 | | #include "AMFImporter.hpp" |
46 | | |
47 | | #include <assimp/DefaultIOSystem.h> |
48 | | #include <assimp/fast_atof.h> |
49 | | #include <assimp/StringUtils.h> |
50 | | |
51 | | // Header files, stdlib. |
52 | | #include <memory> |
53 | | |
54 | | namespace Assimp { |
55 | | |
56 | | static constexpr aiImporterDesc Description = { |
57 | | "Additive manufacturing file format(AMF) Importer", |
58 | | "smalcom", |
59 | | "", |
60 | | "See documentation in source code. Chapter: Limitations.", |
61 | | aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, |
62 | | 0, |
63 | | 0, |
64 | | 0, |
65 | | 0, |
66 | | "amf" |
67 | | }; |
68 | | |
69 | 33 | void AMFImporter::Clear() { |
70 | 33 | mNodeElement_Cur = nullptr; |
71 | 33 | mUnit.clear(); |
72 | 33 | mMaterial_Converted.clear(); |
73 | 33 | mTexture_Converted.clear(); |
74 | | // Delete all elements |
75 | 33 | if (!mNodeElement_List.empty()) { |
76 | 0 | for (AMFNodeElementBase *ne : mNodeElement_List) { |
77 | 0 | delete ne; |
78 | 0 | } |
79 | |
|
80 | 0 | mNodeElement_List.clear(); |
81 | 0 | } |
82 | 33 | } |
83 | | |
84 | | AMFImporter::AMFImporter() AI_NO_EXCEPT : |
85 | | mNodeElement_Cur(nullptr), |
86 | 33 | mXmlParser(nullptr) { |
87 | | // empty |
88 | 33 | } |
89 | | |
90 | 33 | AMFImporter::~AMFImporter() { |
91 | 33 | delete mXmlParser; |
92 | | // Clear() is accounting if data already is deleted. So, just check again if all data is deleted. |
93 | 33 | Clear(); |
94 | 33 | } |
95 | | |
96 | | /*********************************************************************************************************************************************/ |
97 | | /************************************************************ Functions: find set ************************************************************/ |
98 | | /*********************************************************************************************************************************************/ |
99 | | |
100 | 0 | bool AMFImporter::Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const { |
101 | 0 | for (AMFNodeElementBase *ne : mNodeElement_List) { |
102 | 0 | if ((ne->ID == pID) && (ne->Type == pType)) { |
103 | 0 | if (pNodeElement != nullptr) { |
104 | 0 | *pNodeElement = ne; |
105 | 0 | } |
106 | |
|
107 | 0 | return true; |
108 | 0 | } |
109 | 0 | } // for(CAMFImporter_NodeElement* ne: mNodeElement_List) |
110 | | |
111 | 0 | return false; |
112 | 0 | } |
113 | | |
114 | 0 | bool AMFImporter::Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const { |
115 | 0 | aiString node_name(pID.c_str()); |
116 | 0 | for (aiNode *node : nodeArray) { |
117 | 0 | if (node->mName == node_name) { |
118 | 0 | if (pNode != nullptr) { |
119 | 0 | *pNode = node; |
120 | 0 | } |
121 | |
|
122 | 0 | return true; |
123 | 0 | } |
124 | 0 | } // for(aiNode* node: pNodeList) |
125 | | |
126 | 0 | return false; |
127 | 0 | } |
128 | | |
129 | 0 | bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const { |
130 | 0 | for (const SPP_Material &mat : mMaterial_Converted) { |
131 | 0 | if (mat.ID == pID) { |
132 | 0 | if (pConvertedMaterial != nullptr) { |
133 | 0 | *pConvertedMaterial = &mat; |
134 | 0 | } |
135 | |
|
136 | 0 | return true; |
137 | 0 | } |
138 | 0 | } // for(const SPP_Material& mat: mMaterial_Converted) |
139 | | |
140 | 0 | return false; |
141 | 0 | } |
142 | | |
143 | | /*********************************************************************************************************************************************/ |
144 | | /************************************************************ Functions: throw set ***********************************************************/ |
145 | | /*********************************************************************************************************************************************/ |
146 | | |
147 | 0 | void AMFImporter::Throw_CloseNotFound(const std::string &nodeName) { |
148 | 0 | throw DeadlyImportError("Close tag for node <" + nodeName + "> not found. Seems file is corrupt."); |
149 | 0 | } |
150 | | |
151 | 0 | void AMFImporter::Throw_IncorrectAttr(const std::string &nodeName, const std::string &attrName) { |
152 | 0 | throw DeadlyImportError("Node <" + nodeName + "> has incorrect attribute \"" + attrName + "\"."); |
153 | 0 | } |
154 | | |
155 | 0 | void AMFImporter::Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &attrName) { |
156 | 0 | throw DeadlyImportError("Attribute \"" + attrName + "\" in node <" + nodeName + "> has incorrect value."); |
157 | 0 | } |
158 | | |
159 | 0 | void AMFImporter::Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) { |
160 | 0 | throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + nodeName + ". Description: " + pDescription); |
161 | 0 | } |
162 | | |
163 | 0 | void AMFImporter::Throw_ID_NotFound(const std::string &pID) const { |
164 | 0 | throw DeadlyImportError("Not found node with name \"", pID, "\"."); |
165 | 0 | } |
166 | | |
167 | | /*********************************************************************************************************************************************/ |
168 | | /************************************************************* Functions: XML set ************************************************************/ |
169 | | /*********************************************************************************************************************************************/ |
170 | | |
171 | 0 | void AMFImporter::XML_CheckNode_MustHaveChildren(pugi::xml_node &node) { |
172 | 0 | if (node.children().begin() == node.children().end()) { |
173 | 0 | throw DeadlyImportError(std::string("Node <") + node.name() + "> must have children."); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 0 | bool AMFImporter::XML_SearchNode(const std::string &nodeName) { |
178 | 0 | return nullptr != mXmlParser->findNode(nodeName); |
179 | 0 | } |
180 | | |
181 | 0 | static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) { |
182 | 0 | return (isalnum((unsigned char)pChar) || (pChar == '+') || (pChar == '/')); |
183 | 0 | } |
184 | | |
185 | 0 | void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const { |
186 | | // With help from |
187 | | // René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html |
188 | 0 | const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
189 | |
|
190 | 0 | uint8_t tidx = 0; |
191 | 0 | uint8_t arr4[4], arr3[3]; |
192 | | |
193 | | // check input data |
194 | 0 | if (pInputBase64.size() % 4) { |
195 | 0 | throw DeadlyImportError("Base64-encoded data must have size multiply of four."); |
196 | 0 | } |
197 | | |
198 | | // prepare output place |
199 | 0 | pOutputData.clear(); |
200 | 0 | pOutputData.reserve(pInputBase64.size() / 4 * 3); |
201 | |
|
202 | 0 | for (size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--) { |
203 | 0 | if (ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) { |
204 | 0 | arr4[tidx++] = pInputBase64[in_idx++]; |
205 | 0 | if (tidx == 4) { |
206 | 0 | for (tidx = 0; tidx < 4; tidx++) |
207 | 0 | arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]); |
208 | |
|
209 | 0 | arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4); |
210 | 0 | arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2); |
211 | 0 | arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3]; |
212 | 0 | for (tidx = 0; tidx < 3; tidx++) |
213 | 0 | pOutputData.push_back(arr3[tidx]); |
214 | |
|
215 | 0 | tidx = 0; |
216 | 0 | } // if(tidx == 4) |
217 | 0 | } // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) |
218 | 0 | else { |
219 | 0 | in_idx++; |
220 | 0 | } // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else |
221 | 0 | } |
222 | |
|
223 | 0 | if (tidx) { |
224 | 0 | for (uint8_t i = tidx; i < 4; i++) |
225 | 0 | arr4[i] = 0; |
226 | 0 | for (uint8_t i = 0; i < 4; i++) |
227 | 0 | arr4[i] = (uint8_t)(base64_chars.find(arr4[i])); |
228 | |
|
229 | 0 | arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4); |
230 | 0 | arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2); |
231 | 0 | arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3]; |
232 | 0 | for (uint8_t i = 0; i < (tidx - 1); i++) |
233 | 0 | pOutputData.push_back(arr3[i]); |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | 0 | void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) { |
238 | 0 | std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb")); |
239 | | |
240 | | // Check whether we can read from the file |
241 | 0 | if (file == nullptr) { |
242 | 0 | throw DeadlyImportError("Failed to open AMF file ", pFile, "."); |
243 | 0 | } |
244 | | |
245 | 0 | mXmlParser = new XmlParser(); |
246 | 0 | if (!mXmlParser->parse(file.get())) { |
247 | 0 | delete mXmlParser; |
248 | 0 | mXmlParser = nullptr; |
249 | 0 | throw DeadlyImportError("Failed to create XML reader for file ", pFile, "."); |
250 | 0 | } |
251 | | |
252 | | // Start reading, search for root tag <amf> |
253 | 0 | if (!mXmlParser->hasNode("amf")) { |
254 | 0 | throw DeadlyImportError("Root node \"amf\" not found."); |
255 | 0 | } |
256 | 0 | ParseNode_Root(); |
257 | 0 | } // namespace Assimp |
258 | | |
259 | 0 | void AMFImporter::ParseHelper_Node_Enter(AMFNodeElementBase *node) { |
260 | 0 | mNodeElement_Cur->Child.push_back(node); // add new element to current element child list. |
261 | 0 | mNodeElement_Cur = node; |
262 | 0 | } |
263 | | |
264 | 0 | void AMFImporter::ParseHelper_Node_Exit() { |
265 | 0 | if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent; |
266 | 0 | } |
267 | | |
268 | | // <amf |
269 | | // unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron". |
270 | | // version="" - Version of file format. |
271 | | // > |
272 | | // </amf> |
273 | | // Root XML element. |
274 | | // Multi elements - No. |
275 | 0 | void AMFImporter::ParseNode_Root() { |
276 | 0 | AMFNodeElementBase *ne = nullptr; |
277 | 0 | XmlNode *root = mXmlParser->findNode("amf"); |
278 | 0 | if (nullptr == root) { |
279 | 0 | throw DeadlyImportError("Root node \"amf\" not found."); |
280 | 0 | } |
281 | 0 | XmlNode node = *root; |
282 | 0 | mUnit = ai_tolower(std::string(node.attribute("unit").as_string())); |
283 | |
|
284 | 0 | mVersion = node.attribute("version").as_string(); |
285 | | |
286 | | // Read attributes for node <amf>. |
287 | | // Check attributes |
288 | 0 | if (!mUnit.empty()) { |
289 | 0 | if ((mUnit != "inch") && (mUnit != "millimeters") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) { |
290 | 0 | Throw_IncorrectAttrValue("unit", mUnit); |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | // create root node element. |
295 | 0 | ne = new AMFRoot(nullptr); |
296 | |
|
297 | 0 | mNodeElement_Cur = ne; // set first "current" element |
298 | | // and assign attribute's values |
299 | 0 | ((AMFRoot *)ne)->Unit = mUnit; |
300 | 0 | ((AMFRoot *)ne)->Version = mVersion; |
301 | | |
302 | | // Check for child nodes |
303 | 0 | for (XmlNode ¤tNode : node.children() ) { |
304 | 0 | const std::string currentName = currentNode.name(); |
305 | 0 | if (currentName == "object") { |
306 | 0 | ParseNode_Object(currentNode); |
307 | 0 | } else if (currentName == "material") { |
308 | 0 | ParseNode_Material(currentNode); |
309 | 0 | } else if (currentName == "texture") { |
310 | 0 | ParseNode_Texture(currentNode); |
311 | 0 | } else if (currentName == "constellation") { |
312 | 0 | ParseNode_Constellation(currentNode); |
313 | 0 | } else if (currentName == "metadata") { |
314 | 0 | ParseNode_Metadata(currentNode); |
315 | 0 | } |
316 | 0 | mNodeElement_Cur = ne; |
317 | 0 | } |
318 | 0 | mNodeElement_Cur = ne; // force restore "current" element |
319 | 0 | mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph. |
320 | 0 | } |
321 | | |
322 | | // <constellation |
323 | | // id="" - The Object ID of the new constellation being defined. |
324 | | // > |
325 | | // </constellation> |
326 | | // A collection of objects or constellations with specific relative locations. |
327 | | // Multi elements - Yes. |
328 | | // Parent element - <amf>. |
329 | 0 | void AMFImporter::ParseNode_Constellation(XmlNode &node) { |
330 | 0 | std::string id; |
331 | 0 | id = node.attribute("id").as_string(); |
332 | | |
333 | | // create and if needed - define new grouping object. |
334 | 0 | AMFNodeElementBase *ne = new AMFConstellation(mNodeElement_Cur); |
335 | |
|
336 | 0 | AMFConstellation &als = *((AMFConstellation *)ne); // alias for convenience |
337 | |
|
338 | 0 | if (!id.empty()) { |
339 | 0 | als.ID = id; |
340 | 0 | } |
341 | | |
342 | | // Check for child nodes |
343 | 0 | if (!node.empty()) { |
344 | 0 | ParseHelper_Node_Enter(ne); |
345 | 0 | for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { |
346 | 0 | std::string name = currentNode.name(); |
347 | 0 | if (name == "instance") { |
348 | 0 | ParseNode_Instance(currentNode); |
349 | 0 | } else if (name == "metadata") { |
350 | 0 | ParseNode_Metadata(currentNode); |
351 | 0 | } |
352 | 0 | } |
353 | 0 | ParseHelper_Node_Exit(); |
354 | 0 | } else { |
355 | 0 | mNodeElement_Cur->Child.push_back(ne); |
356 | 0 | } |
357 | 0 | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
358 | 0 | } |
359 | | |
360 | | // <instance |
361 | | // objectid="" - The Object ID of the new constellation being defined. |
362 | | // > |
363 | | // </instance> |
364 | | // A collection of objects or constellations with specific relative locations. |
365 | | // Multi elements - Yes. |
366 | | // Parent element - <amf>. |
367 | 0 | void AMFImporter::ParseNode_Instance(XmlNode &node) { |
368 | 0 | AMFNodeElementBase *ne(nullptr); |
369 | | |
370 | | // Read attributes for node <constellation>. |
371 | 0 | std::string objectid = node.attribute("objectid").as_string(); |
372 | | |
373 | | // used object id must be defined, check that. |
374 | 0 | if (objectid.empty()) { |
375 | 0 | throw DeadlyImportError("\"objectid\" in <instance> must be defined."); |
376 | 0 | } |
377 | | // create and define new grouping object. |
378 | 0 | ne = new AMFInstance(mNodeElement_Cur); |
379 | 0 | AMFInstance &als = *((AMFInstance *)ne); |
380 | 0 | als.ObjectID = objectid; |
381 | |
|
382 | 0 | if (!node.empty()) { |
383 | 0 | ParseHelper_Node_Enter(ne); |
384 | 0 | for (auto ¤tNode : node.children()) { |
385 | 0 | const std::string ¤tName = currentNode.name(); |
386 | 0 | if (currentName == "deltax") { |
387 | 0 | XmlParser::getValueAsReal(currentNode, als.Delta.x); |
388 | 0 | } else if (currentName == "deltay") { |
389 | 0 | XmlParser::getValueAsReal(currentNode, als.Delta.y); |
390 | 0 | } else if (currentName == "deltaz") { |
391 | 0 | XmlParser::getValueAsReal(currentNode, als.Delta.z); |
392 | 0 | } else if (currentName == "rx") { |
393 | 0 | XmlParser::getValueAsReal(currentNode, als.Delta.x); |
394 | 0 | } else if (currentName == "ry") { |
395 | 0 | XmlParser::getValueAsReal(currentNode, als.Delta.y); |
396 | 0 | } else if (currentName == "rz") { |
397 | 0 | XmlParser::getValueAsReal(currentNode, als.Delta.z); |
398 | 0 | } |
399 | 0 | } |
400 | 0 | ParseHelper_Node_Exit(); |
401 | 0 | } else { |
402 | 0 | mNodeElement_Cur->Child.push_back(ne); |
403 | 0 | } |
404 | |
|
405 | 0 | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
406 | 0 | } |
407 | | |
408 | | // <object |
409 | | // id="" - A unique ObjectID for the new object being defined. |
410 | | // > |
411 | | // </object> |
412 | | // An object definition. |
413 | | // Multi elements - Yes. |
414 | | // Parent element - <amf>. |
415 | 0 | void AMFImporter::ParseNode_Object(XmlNode &node) { |
416 | 0 | AMFNodeElementBase *ne = nullptr; |
417 | | |
418 | | // Read attributes for node <object>. |
419 | 0 | std::string id = node.attribute("id").as_string(); |
420 | | |
421 | | // create and if needed - define new geometry object. |
422 | 0 | ne = new AMFObject(mNodeElement_Cur); |
423 | |
|
424 | 0 | AMFObject &als = *((AMFObject *)ne); // alias for convenience |
425 | |
|
426 | 0 | if (!id.empty()) { |
427 | 0 | als.ID = id; |
428 | 0 | } |
429 | | |
430 | | // Check for child nodes |
431 | 0 | if (!node.empty()) { |
432 | 0 | ParseHelper_Node_Enter(ne); |
433 | 0 | for (auto ¤tNode : node.children()) { |
434 | 0 | const std::string ¤tName = currentNode.name(); |
435 | 0 | if (currentName == "color") { |
436 | 0 | ParseNode_Color(currentNode); |
437 | 0 | } else if (currentName == "mesh") { |
438 | 0 | ParseNode_Mesh(currentNode); |
439 | 0 | } else if (currentName == "metadata") { |
440 | 0 | ParseNode_Metadata(currentNode); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | ParseHelper_Node_Exit(); |
444 | 0 | } else { |
445 | 0 | mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element |
446 | 0 | } |
447 | |
|
448 | 0 | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
449 | 0 | } |
450 | | |
451 | | // <metadata |
452 | | // type="" - The type of the attribute. |
453 | | // > |
454 | | // </metadata> |
455 | | // Specify additional information about an entity. |
456 | | // Multi elements - Yes. |
457 | | // Parent element - <amf>, <object>, <volume>, <material>, <vertex>. |
458 | | // |
459 | | // Reserved types are: |
460 | | // "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user. |
461 | | // "Description" - A description of the content of the entity |
462 | | // "URL" - A link to an external resource relating to the entity |
463 | | // "Author" - Specifies the name(s) of the author(s) of the entity |
464 | | // "Company" - Specifying the company generating the entity |
465 | | // "CAD" - specifies the name of the originating CAD software and version |
466 | | // "Revision" - specifies the revision of the entity |
467 | | // "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system |
468 | | // "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only) |
469 | 0 | void AMFImporter::ParseNode_Metadata(XmlNode &node) { |
470 | 0 | AMFNodeElementBase *ne = nullptr; |
471 | |
|
472 | 0 | std::string type = node.attribute("type").as_string(), value; |
473 | 0 | XmlParser::getValueAsString(node, value); |
474 | | |
475 | | // read attribute |
476 | 0 | ne = new AMFMetadata(mNodeElement_Cur); |
477 | 0 | ((AMFMetadata *)ne)->Type = type; |
478 | 0 | ((AMFMetadata *)ne)->Value = value; |
479 | 0 | mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element |
480 | 0 | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
481 | 0 | } |
482 | | |
483 | 30 | bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*pCheckSig*/) const { |
484 | 30 | static const char *tokens[] = { "<amf" }; |
485 | 30 | return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); |
486 | 30 | } |
487 | | |
488 | 33 | const aiImporterDesc *AMFImporter::GetInfo() const { |
489 | 33 | return &Description; |
490 | 33 | } |
491 | | |
492 | 0 | void AMFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { |
493 | 0 | Clear(); // delete old graph. |
494 | 0 | ParseFile(pFile, pIOHandler); |
495 | 0 | Postprocess_BuildScene(pScene); |
496 | | // scene graph is ready, exit. |
497 | 0 | } |
498 | | |
499 | | } // namespace Assimp |
500 | | |
501 | | #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER |