1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2021, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12 
13 * Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16 
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21 
22 * Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39 ----------------------------------------------------------------------
40 */
41 /// \file   X3DImporter.cpp
42 /// \brief  X3D-format files importer for Assimp: main algorithm implementation.
43 /// \date   2015-2016
44 /// \author smal.root@gmail.com
45 
46 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
47 
48 #include "X3DImporter.hpp"
49 #include "X3DImporter_Macro.hpp"
50 
51 #include <assimp/DefaultIOSystem.h>
52 
53 // Header files, stdlib.
54 #include <iterator>
55 #include <memory>
56 
57 namespace Assimp {
58 
59 /// Constant which holds the importer description
60 const aiImporterDesc X3DImporter::Description = {
61     "Extensible 3D(X3D) Importer",
62     "smalcom",
63     "",
64     "See documentation in source code. Chapter: Limitations.",
65     aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
66     0,
67     0,
68     0,
69     0,
70     "x3d x3db"
71 };
72 
isNodeEmpty(XmlNode & node)73 bool X3DImporter::isNodeEmpty(XmlNode &node) {
74     return node.first_child().empty();
75 }
76 
checkNodeMustBeEmpty(XmlNode & node)77 void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) {
78     if (!isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty.");
79 }
80 
skipUnsupportedNode(const std::string & pParentNodeName,XmlNode & node)81 void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) {
82     static const size_t Uns_Skip_Len = 192;
83     static const char *Uns_Skip[Uns_Skip_Len] = {
84         // CAD geometry component
85         "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
86         // Core
87         "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo",
88         // Distributed interactive simulation (DIS) component
89         "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu",
90         // Cube map environmental texturing component
91         "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture",
92         // Environmental effects component
93         "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground",
94         // Environmental sensor component
95         "ProximitySensor", "TransformSensor", "VisibilitySensor",
96         // Followers component
97         "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D",
98         "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D",
99         // Geospatial component
100         "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor",
101         "GeoTouchSensor", "GeoTransform", "GeoViewpoint",
102         // Humanoid Animation (H-Anim) component
103         "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite",
104         // Interpolation component
105         "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator",
106         "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
107         "SplineScalarInterpolator", "SquadOrientationInterpolator",
108         // Key device sensor component
109         "KeySensor", "StringSensor",
110         // Layering component
111         "Layer", "LayerSet", "Viewport",
112         // Layout component
113         "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup",
114         // Navigation component
115         "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup",
116         // Networking component
117         "EXPORT", "IMPORT", "Anchor", "LoadSensor",
118         // NURBS component
119         "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface",
120         "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate",
121         "NurbsTrimmedSurface",
122         // Particle systems component
123         "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter",
124         "VolumeEmitter", "WindPhysicsModel",
125         // Picking component
126         "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor",
127         // Pointing device sensor component
128         "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor",
129         // Rendering component
130         "ClipPlane",
131         // Rigid body physics
132         "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint",
133         "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint",
134         // Scripting component
135         "Script",
136         // Programmable shaders component
137         "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart",
138         "ShaderProgram",
139         // Shape component
140         "FillProperties", "LineProperties", "TwoSidedMaterial",
141         // Sound component
142         "AudioClip", "Sound",
143         // Text component
144         "FontStyle", "Text",
145         // Texturing3D Component
146         "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D",
147         // Texturing component
148         "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties",
149         // Time component
150         "TimeSensor",
151         // Event Utilities component
152         "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger",
153         // Volume rendering component
154         "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData",
155         "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle",
156         "VolumeData"
157     };
158 
159     const std::string nn = node.name();
160 
161     if (nn.empty()) {
162         const std::string nv = node.value();
163         if (!nv.empty()) {
164             LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + ".");
165             return;
166         }
167     }
168 
169     bool found = false;
170 
171     for (size_t i = 0; i < Uns_Skip_Len; i++) {
172         if (nn == Uns_Skip[i]) {
173             found = true;
174         }
175     }
176 
177     if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
178 
179     LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
180 }
181 
X3DImporter()182 X3DImporter::X3DImporter() :
183         mNodeElementCur(nullptr),
184         mScene(nullptr),
185         mpIOHandler(nullptr) {
186     // empty
187 }
188 
~X3DImporter()189 X3DImporter::~X3DImporter() {
190     // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
191     Clear();
192 }
193 
Clear()194 void X3DImporter::Clear() {
195     mNodeElementCur = nullptr;
196     // Delete all elements
197     if (!NodeElement_List.empty()) {
198         for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) {
199             delete *it;
200         }
201         NodeElement_List.clear();
202     }
203 }
204 
ParseFile(const std::string & file,IOSystem * pIOHandler)205 void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) {
206     ai_assert(nullptr != pIOHandler);
207 
208     static const std::string mode = "rb";
209     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode));
210     if (!fileStream.get()) {
211         throw DeadlyImportError("Failed to open file " + file + ".");
212     }
213 
214     XmlParser theParser;
215     if (!theParser.parse(fileStream.get())) {
216         return;
217     }
218 
219     XmlNode *node = theParser.findNode("X3D");
220     if (nullptr == node) {
221         return;
222     }
223 
224     for (auto &currentNode : node->children()) {
225         const std::string &currentName = currentNode.name();
226         if (currentName == "head") {
227             readHead(currentNode);
228         } else if (currentName == "Scene") {
229             readScene(currentNode);
230         } else {
231             skipUnsupportedNode("X3D", currentNode);
232         }
233     }
234 }
235 
CanRead(const std::string & pFile,IOSystem *,bool checkSig) const236 bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig) const {
237     if (checkSig) {
238         if (GetExtension(pFile) == "x3d")
239             return true;
240     }
241 
242     return false;
243 }
244 
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)245 void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
246     mpIOHandler = pIOHandler;
247 
248     Clear();
249     std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
250     if (!stream) {
251         throw DeadlyImportError("Could not open file for reading");
252     }
253     std::string::size_type slashPos = pFile.find_last_of("\\/");
254 
255     mScene = pScene;
256     pScene->mRootNode = new aiNode(pFile);
257     pScene->mRootNode->mParent = nullptr;
258     pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
259 
260     pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
261     ParseFile(pFile, pIOHandler);
262     pIOHandler->PopDirectory();
263 
264     //search for root node element
265 
266     mNodeElementCur = NodeElement_List.front();
267     while (mNodeElementCur->Parent != nullptr) {
268         mNodeElementCur = mNodeElementCur->Parent;
269     }
270 
271     { // fill aiScene with objects.
272         std::list<aiMesh *> mesh_list;
273         std::list<aiMaterial *> mat_list;
274         std::list<aiLight *> light_list;
275 
276         // create nodes tree
277         Postprocess_BuildNode(*mNodeElementCur, *pScene->mRootNode, mesh_list, mat_list, light_list);
278         // copy needed data to scene
279         if (!mesh_list.empty()) {
280             std::list<aiMesh *>::const_iterator it = mesh_list.begin();
281 
282             pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
283             pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
284             for (size_t i = 0; i < pScene->mNumMeshes; i++)
285                 pScene->mMeshes[i] = *it++;
286         }
287 
288         if (!mat_list.empty()) {
289             std::list<aiMaterial *>::const_iterator it = mat_list.begin();
290 
291             pScene->mNumMaterials = static_cast<unsigned int>(mat_list.size());
292             pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
293             for (size_t i = 0; i < pScene->mNumMaterials; i++)
294                 pScene->mMaterials[i] = *it++;
295         }
296 
297         if (!light_list.empty()) {
298             std::list<aiLight *>::const_iterator it = light_list.begin();
299 
300             pScene->mNumLights = static_cast<unsigned int>(light_list.size());
301             pScene->mLights = new aiLight *[pScene->mNumLights];
302             for (size_t i = 0; i < pScene->mNumLights; i++)
303                 pScene->mLights[i] = *it++;
304         }
305     }
306 }
307 
GetInfo() const308 const aiImporterDesc *X3DImporter::GetInfo() const {
309     return &Description;
310 }
311 
312 struct meta_entry {
313     std::string name;
314     std::string value;
315 };
316 
readHead(XmlNode & node)317 void X3DImporter::readHead(XmlNode &node) {
318     std::vector<meta_entry> metaArray;
319     for (auto currentNode : node.children()) {
320         const std::string &currentName = currentNode.name();
321         if (currentName == "meta") {
322             //checkNodeMustBeEmpty(node);
323             meta_entry entry;
324             if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) {
325                 XmlParser::getStdStrAttribute(currentNode, "content", entry.value);
326                 metaArray.emplace_back(entry);
327             }
328         }
329         // TODO: check if other node types in head should be supported
330     }
331     mScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metaArray.size()));
332     unsigned int i = 0;
333     for (auto currentMeta : metaArray) {
334         mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value));
335         ++i;
336     }
337 }
338 
readChildNodes(XmlNode & node,const std::string & pParentNodeName)339 void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) {
340     if (node.empty()) {
341         return;
342     }
343     for (auto currentNode : node.children()) {
344         const std::string &currentName = currentNode.name();
345         if (currentName == "Shape")
346             readShape(currentNode);
347         else if (currentName == "Group") {
348             startReadGroup(currentNode);
349             readChildNodes(currentNode, "Group");
350             endReadGroup();
351         } else if (currentName == "StaticGroup") {
352             startReadStaticGroup(currentNode);
353             readChildNodes(currentNode, "StaticGroup");
354             endReadStaticGroup();
355         } else if (currentName == "Transform") {
356             startReadTransform(currentNode);
357             readChildNodes(currentNode, "Transform");
358             endReadTransform();
359         } else if (currentName == "Switch") {
360             startReadSwitch(currentNode);
361             readChildNodes(currentNode, "Switch");
362             endReadSwitch();
363         } else if (currentName == "DirectionalLight") {
364             readDirectionalLight(currentNode);
365         } else if (currentName == "PointLight") {
366             readPointLight(currentNode);
367         } else if (currentName == "SpotLight") {
368             readSpotLight(currentNode);
369         } else if (currentName == "Inline") {
370             readInline(currentNode);
371         } else if (!checkForMetadataNode(currentNode)) {
372             skipUnsupportedNode(pParentNodeName, currentNode);
373         }
374     }
375 }
376 
readScene(XmlNode & node)377 void X3DImporter::readScene(XmlNode &node) {
378     ParseHelper_Group_Begin(true);
379     readChildNodes(node, "Scene");
380     ParseHelper_Node_Exit();
381 }
382 
383 /*********************************************************************************************************************************************/
384 /************************************************************ Functions: find set ************************************************************/
385 /*********************************************************************************************************************************************/
386 
FindNodeElement_FromRoot(const std::string & pID,const X3DElemType pType,X3DNodeElementBase ** pElement)387 bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) {
388     for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) {
389         if (((*it)->Type == pType) && ((*it)->ID == pID)) {
390             if (pElement != nullptr) *pElement = *it;
391 
392             return true;
393         }
394     } // for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
395 
396     return false;
397 }
398 
FindNodeElement_FromNode(X3DNodeElementBase * pStartNode,const std::string & pID,const X3DElemType pType,X3DNodeElementBase ** pElement)399 bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID,
400         const X3DElemType pType, X3DNodeElementBase **pElement) {
401     bool found = false; // flag: true - if requested element is found.
402 
403     // Check if pStartNode - this is the element, we are looking for.
404     if ((pStartNode->Type == pType) && (pStartNode->ID == pID)) {
405         found = true;
406         if (pElement != nullptr) {
407             *pElement = pStartNode;
408         }
409 
410         goto fne_fn_end;
411     } // if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
412 
413     // Check childs of pStartNode.
414     for (std::list<X3DNodeElementBase *>::iterator ch_it = pStartNode->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) {
415         found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
416         if (found) {
417             break;
418         }
419     } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++)
420 
421 fne_fn_end:
422 
423     return found;
424 }
425 
FindNodeElement(const std::string & pID,const X3DElemType pType,X3DNodeElementBase ** pElement)426 bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) {
427     X3DNodeElementBase *tnd = mNodeElementCur; // temporary pointer to node.
428     bool static_search = false; // flag: true if searching in static node.
429 
430     // At first check if we have deal with static node. Go up through parent nodes and check flag.
431     while (tnd != nullptr) {
432         if (tnd->Type == X3DElemType::ENET_Group) {
433             if (((X3DNodeElementGroup *)tnd)->Static) {
434                 static_search = true; // Flag found, stop walking up. Node with static flag will holded in tnd variable.
435                 break;
436             }
437         }
438 
439         tnd = tnd->Parent; // go up in graph.
440     } // while (tnd != nullptr)
441 
442     // at now call appropriate search function.
443     if (static_search) {
444         return FindNodeElement_FromNode(tnd, pID, pType, pElement);
445     } else {
446         return FindNodeElement_FromRoot(pID, pType, pElement);
447     }
448 }
449 
450 /*********************************************************************************************************************************************/
451 /************************************************************ Functions: parse set ***********************************************************/
452 /*********************************************************************************************************************************************/
453 
ParseHelper_Group_Begin(const bool pStatic)454 void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) {
455     X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent.
456 
457     // if we are adding not the root element then add new element to current element child list.
458     if (mNodeElementCur != nullptr) {
459         mNodeElementCur->Children.push_back(new_group);
460     }
461 
462     NodeElement_List.push_back(new_group); // it's a new element - add it to list.
463     mNodeElementCur = new_group; // switch current element to new one.
464 }
465 
ParseHelper_Node_Enter(X3DNodeElementBase * pNode)466 void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) {
467     ai_assert(nullptr != pNode);
468 
469     mNodeElementCur->Children.push_back(pNode); // add new element to current element child list.
470     mNodeElementCur = pNode; // switch current element to new one.
471 }
472 
ParseHelper_Node_Exit()473 void X3DImporter::ParseHelper_Node_Exit() {
474     // check if we can walk up.
475     if (mNodeElementCur != nullptr) {
476         mNodeElementCur = mNodeElementCur->Parent;
477     } else {
478         int i = 0;
479         ++i;
480     }
481 }
482 
483 #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
484 
485 } // namespace Assimp
486