/src/assimp/code/AssetLib/AMF/AMFImporter.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2026, 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 | 134k | void AMFImporter::Clear() { |
70 | 134k | mNodeElement_Cur = nullptr; |
71 | 134k | mUnit.clear(); |
72 | 134k | mMaterial_Converted.clear(); |
73 | 134k | mTexture_Converted.clear(); |
74 | | // Delete all elements |
75 | 134k | if (!mNodeElement_List.empty()) { |
76 | 1.61M | for (AMFNodeElementBase *ne : mNodeElement_List) { |
77 | 1.61M | delete ne; |
78 | 1.61M | } |
79 | | |
80 | 2.75k | mNodeElement_List.clear(); |
81 | 2.75k | } |
82 | 134k | } |
83 | | |
84 | | AMFImporter::AMFImporter() AI_NO_EXCEPT : |
85 | 130k | mNodeElement_Cur(nullptr), |
86 | 130k | mXmlParser(nullptr) { |
87 | | // empty |
88 | 130k | } |
89 | | |
90 | 130k | AMFImporter::~AMFImporter() { |
91 | 130k | delete mXmlParser; |
92 | | // Clear() is accounting if data already is deleted. So, just check again if all data is deleted. |
93 | 130k | Clear(); |
94 | 130k | } |
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 | 25.8k | bool AMFImporter::Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const { |
115 | 25.8k | aiString node_name(pID.c_str()); |
116 | 37.2k | for (std::unique_ptr<aiNode> &node : nodeArray) { |
117 | 37.2k | if (node->mName == node_name) { |
118 | 25.7k | if (pNode != nullptr) { |
119 | 25.7k | *pNode = node.get(); |
120 | 25.7k | } |
121 | | |
122 | 25.7k | return true; |
123 | 25.7k | } |
124 | 37.2k | } // for(aiNode* node: pNodeList) |
125 | | |
126 | 130 | return false; |
127 | 25.8k | } |
128 | | |
129 | 3.29k | bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const { |
130 | 13.2k | for (const SPP_Material &mat : mMaterial_Converted) { |
131 | 13.2k | if (mat.ID == pID) { |
132 | 3.24k | if (pConvertedMaterial != nullptr) { |
133 | 3.24k | *pConvertedMaterial = &mat; |
134 | 3.24k | } |
135 | | |
136 | 3.24k | return true; |
137 | 3.24k | } |
138 | 13.2k | } // for(const SPP_Material& mat: mMaterial_Converted) |
139 | | |
140 | 53 | return false; |
141 | 3.29k | } |
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 | 108 | void AMFImporter::Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &attrName) { |
156 | 108 | throw DeadlyImportError("Attribute \"" + attrName + "\" in node <" + nodeName + "> has incorrect value."); |
157 | 108 | } |
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 | 183 | void AMFImporter::Throw_ID_NotFound(const std::string &pID) const { |
164 | 183 | throw DeadlyImportError("Not found node with name \"", pID, "\"."); |
165 | 183 | } |
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 | 3.34k | void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) { |
238 | 3.34k | std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb")); |
239 | | |
240 | | // Check whether we can read from the file |
241 | 3.34k | if (file == nullptr) { |
242 | 0 | throw DeadlyImportError("Failed to open AMF file ", pFile, "."); |
243 | 0 | } |
244 | | |
245 | 3.34k | mXmlParser = new XmlParser(); |
246 | 3.34k | if (!mXmlParser->parse(file.get())) { |
247 | 338 | delete mXmlParser; |
248 | 338 | mXmlParser = nullptr; |
249 | 338 | throw DeadlyImportError("Failed to create XML reader for file ", pFile, "."); |
250 | 338 | } |
251 | | |
252 | | // Start reading, search for root tag <amf> |
253 | 3.01k | if (!mXmlParser->hasNode("amf")) { |
254 | 22 | throw DeadlyImportError("Root node \"amf\" not found."); |
255 | 22 | } |
256 | 2.98k | ParseNode_Root(); |
257 | 2.98k | } // namespace Assimp |
258 | | |
259 | 1.35M | void AMFImporter::ParseHelper_Node_Enter(AMFNodeElementBase *node) { |
260 | 1.35M | mNodeElement_Cur->Child.push_back(node); // add new element to current element child list. |
261 | 1.35M | mNodeElement_Cur = node; |
262 | 1.35M | } |
263 | | |
264 | 1.35M | void AMFImporter::ParseHelper_Node_Exit() { |
265 | 1.35M | if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent; |
266 | 1.35M | } |
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 | 2.98k | void AMFImporter::ParseNode_Root() { |
276 | 2.98k | AMFNodeElementBase *ne = nullptr; |
277 | 2.98k | XmlNode *root = mXmlParser->findNode("amf"); |
278 | 2.98k | if (nullptr == root) { |
279 | 0 | throw DeadlyImportError("Root node \"amf\" not found."); |
280 | 0 | } |
281 | 2.98k | XmlNode node = *root; |
282 | 2.98k | mUnit = ai_tolower(std::string(node.attribute("unit").as_string())); |
283 | | |
284 | 2.98k | mVersion = node.attribute("version").as_string(); |
285 | | |
286 | | // Read attributes for node <amf>. |
287 | | // Check attributes |
288 | 2.98k | if (!mUnit.empty()) { |
289 | 216 | if ((mUnit != "inch") && (mUnit != "millimeters") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) { |
290 | 108 | Throw_IncorrectAttrValue("unit", mUnit); |
291 | 108 | } |
292 | 216 | } |
293 | | |
294 | | // create root node element. |
295 | 2.98k | ne = new AMFRoot(nullptr); |
296 | | |
297 | 2.98k | mNodeElement_Cur = ne; // set first "current" element |
298 | | // and assign attribute's values |
299 | 2.98k | ((AMFRoot *)ne)->Unit = mUnit; |
300 | 2.98k | ((AMFRoot *)ne)->Version = mVersion; |
301 | | |
302 | | // Check for child nodes |
303 | 503k | for (XmlNode ¤tNode : node.children() ) { |
304 | 503k | const std::string currentName = currentNode.name(); |
305 | 503k | if (currentName == "object") { |
306 | 193k | ParseNode_Object(currentNode); |
307 | 309k | } else if (currentName == "material") { |
308 | 7.64k | ParseNode_Material(currentNode); |
309 | 301k | } else if (currentName == "texture") { |
310 | 54 | ParseNode_Texture(currentNode); |
311 | 301k | } else if (currentName == "constellation") { |
312 | 5.27k | ParseNode_Constellation(currentNode); |
313 | 296k | } else if (currentName == "metadata") { |
314 | 190k | ParseNode_Metadata(currentNode); |
315 | 190k | } |
316 | 503k | mNodeElement_Cur = ne; |
317 | 503k | } |
318 | 2.98k | mNodeElement_Cur = ne; // force restore "current" element |
319 | 2.98k | mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph. |
320 | 2.98k | } |
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 | 5.27k | void AMFImporter::ParseNode_Constellation(XmlNode &node) { |
330 | 5.27k | std::string id = node.attribute("id").as_string(); |
331 | | |
332 | | // create and if needed - define new grouping object. |
333 | 5.27k | AMFNodeElementBase *ne = new AMFConstellation(mNodeElement_Cur); |
334 | | |
335 | 5.27k | AMFConstellation &als = *((AMFConstellation *)ne); // alias for convenience |
336 | | |
337 | 5.27k | if (!id.empty()) { |
338 | 4.07k | als.ID = id; |
339 | 4.07k | } |
340 | | |
341 | | // Check for child nodes |
342 | 5.27k | if (!node.empty()) { |
343 | 5.27k | ParseHelper_Node_Enter(ne); |
344 | 84.8k | for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { |
345 | 79.5k | std::string name = currentNode.name(); |
346 | 79.5k | if (name == "instance") { |
347 | 30.8k | ParseNode_Instance(currentNode); |
348 | 48.7k | } else if (name == "metadata") { |
349 | 33.8k | ParseNode_Metadata(currentNode); |
350 | 33.8k | } |
351 | 79.5k | } |
352 | 5.27k | ParseHelper_Node_Exit(); |
353 | 5.27k | } else { |
354 | 0 | mNodeElement_Cur->Child.push_back(ne); |
355 | 0 | } |
356 | 5.27k | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
357 | 5.27k | } |
358 | | |
359 | | // <instance |
360 | | // objectid="" - The Object ID of the new constellation being defined. |
361 | | // > |
362 | | // </instance> |
363 | | // A collection of objects or constellations with specific relative locations. |
364 | | // Multi elements - Yes. |
365 | | // Parent element - <amf>. |
366 | 30.8k | void AMFImporter::ParseNode_Instance(XmlNode &node) { |
367 | 30.8k | AMFNodeElementBase *ne(nullptr); |
368 | | |
369 | | // Read attributes for node <constellation>. |
370 | 30.8k | std::string objectid = node.attribute("objectid").as_string(); |
371 | | |
372 | | // used object id must be defined, check that. |
373 | 30.8k | if (objectid.empty()) { |
374 | 0 | throw DeadlyImportError("\"objectid\" in <instance> must be defined."); |
375 | 0 | } |
376 | | // create and define new grouping object. |
377 | 30.8k | ne = new AMFInstance(mNodeElement_Cur); |
378 | 30.8k | AMFInstance &als = *((AMFInstance *)ne); |
379 | 30.8k | als.ObjectID = objectid; |
380 | | |
381 | 30.8k | if (!node.empty()) { |
382 | 30.8k | ParseHelper_Node_Enter(ne); |
383 | 198k | for (auto ¤tNode : node.children()) { |
384 | 198k | const std::string ¤tName = currentNode.name(); |
385 | 198k | if (currentName == "deltax") { |
386 | 25.8k | XmlParser::getValueAsReal(currentNode, als.Delta.x); |
387 | 172k | } else if (currentName == "deltay") { |
388 | 24.6k | XmlParser::getValueAsReal(currentNode, als.Delta.y); |
389 | 147k | } else if (currentName == "deltaz") { |
390 | 24.9k | XmlParser::getValueAsReal(currentNode, als.Delta.z); |
391 | 122k | } else if (currentName == "rx") { |
392 | 25.6k | XmlParser::getValueAsReal(currentNode, als.Delta.x); |
393 | 97.3k | } else if (currentName == "ry") { |
394 | 25.5k | XmlParser::getValueAsReal(currentNode, als.Delta.y); |
395 | 71.7k | } else if (currentName == "rz") { |
396 | 26.6k | XmlParser::getValueAsReal(currentNode, als.Delta.z); |
397 | 26.6k | } |
398 | 198k | } |
399 | 30.8k | ParseHelper_Node_Exit(); |
400 | 30.8k | } else { |
401 | 0 | mNodeElement_Cur->Child.push_back(ne); |
402 | 0 | } |
403 | | |
404 | 30.8k | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
405 | 30.8k | } |
406 | | |
407 | | // <object |
408 | | // id="" - A unique ObjectID for the new object being defined. |
409 | | // > |
410 | | // </object> |
411 | | // An object definition. |
412 | | // Multi elements - Yes. |
413 | | // Parent element - <amf>. |
414 | 193k | void AMFImporter::ParseNode_Object(XmlNode &node) { |
415 | 193k | AMFNodeElementBase *ne = nullptr; |
416 | | |
417 | | // Read attributes for node <object>. |
418 | 193k | std::string id = node.attribute("id").as_string(); |
419 | | |
420 | | // create and if needed - define new geometry object. |
421 | 193k | ne = new AMFObject(mNodeElement_Cur); |
422 | | |
423 | 193k | AMFObject &als = *((AMFObject *)ne); // alias for convenience |
424 | | |
425 | 193k | if (!id.empty()) { |
426 | 24.4k | als.ID = id; |
427 | 24.4k | } |
428 | | |
429 | | // Check for child nodes |
430 | 193k | if (!node.empty()) { |
431 | 193k | ParseHelper_Node_Enter(ne); |
432 | 193k | for (auto ¤tNode : node.children()) { |
433 | 73.6k | const std::string ¤tName = currentNode.name(); |
434 | 73.6k | if (currentName == "color") { |
435 | 11.1k | ParseNode_Color(currentNode); |
436 | 62.5k | } else if (currentName == "mesh") { |
437 | 39.0k | ParseNode_Mesh(currentNode); |
438 | 39.0k | } else if (currentName == "metadata") { |
439 | 2.71k | ParseNode_Metadata(currentNode); |
440 | 2.71k | } |
441 | 73.6k | } |
442 | 193k | ParseHelper_Node_Exit(); |
443 | 193k | } else { |
444 | 0 | mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element |
445 | 0 | } |
446 | | |
447 | 193k | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
448 | 193k | } |
449 | | |
450 | | // <metadata |
451 | | // type="" - The type of the attribute. |
452 | | // > |
453 | | // </metadata> |
454 | | // Specify additional information about an entity. |
455 | | // Multi elements - Yes. |
456 | | // Parent element - <amf>, <object>, <volume>, <material>, <vertex>. |
457 | | // |
458 | | // Reserved types are: |
459 | | // "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user. |
460 | | // "Description" - A description of the content of the entity |
461 | | // "URL" - A link to an external resource relating to the entity |
462 | | // "Author" - Specifies the name(s) of the author(s) of the entity |
463 | | // "Company" - Specifying the company generating the entity |
464 | | // "CAD" - specifies the name of the originating CAD software and version |
465 | | // "Revision" - specifies the revision of the entity |
466 | | // "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system |
467 | | // "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only) |
468 | 252k | void AMFImporter::ParseNode_Metadata(XmlNode &node) { |
469 | 252k | AMFNodeElementBase *ne = nullptr; |
470 | | |
471 | 252k | std::string type = node.attribute("type").as_string(), value; |
472 | 252k | XmlParser::getValueAsString(node, value); |
473 | | |
474 | | // read attribute |
475 | 252k | ne = new AMFMetadata(mNodeElement_Cur); |
476 | 252k | ((AMFMetadata *)ne)->MetaType = type; |
477 | 252k | ((AMFMetadata *)ne)->Value = value; |
478 | 252k | mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element |
479 | 252k | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
480 | 252k | } |
481 | | |
482 | 12.4k | bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*pCheckSig*/) const { |
483 | 12.4k | static const char *tokens[] = { "<amf" }; |
484 | 12.4k | return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); |
485 | 12.4k | } |
486 | | |
487 | 136k | const aiImporterDesc *AMFImporter::GetInfo() const { |
488 | 136k | return &Description; |
489 | 136k | } |
490 | | |
491 | 3.34k | void AMFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { |
492 | 3.34k | Clear(); // delete old graph. |
493 | 3.34k | ParseFile(pFile, pIOHandler); |
494 | 3.34k | Postprocess_BuildScene(pScene); |
495 | | // scene graph is ready, exit. |
496 | 3.34k | } |
497 | | |
498 | | } // namespace Assimp |
499 | | |
500 | | #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER |