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 ¤tNode : node->children()) {
225 const std::string ¤tName = 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 ¤tName = 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 ¤tName = 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