Alexandr Arutjunov 9 роки тому
батько
коміт
3274951f2a

+ 16 - 0
code/CMakeLists.txt

@@ -610,6 +610,22 @@ ADD_ASSIMP_IMPORTER( X
   XFileExporter.cpp
 )
 
+ADD_ASSIMP_IMPORTER(X3D
+  X3DImporter.cpp
+  X3DImporter.hpp
+  X3DImporter_Geometry2D.cpp
+  X3DImporter_Geometry3D.cpp
+  X3DImporter_Group.cpp
+  X3DImporter_Light.cpp
+  X3DImporter_Metadata.cpp
+  X3DImporter_Networking.cpp
+  X3DImporter_Node.hpp
+  X3DImporter_Postprocess.cpp
+  X3DImporter_Rendering.cpp
+  X3DImporter_Shape.cpp
+  X3DImporter_Texturing.cpp
+)
+
 ADD_ASSIMP_IMPORTER( GLTF
   glTFAsset.h
   glTFAsset.inl

+ 6 - 0
code/ImporterRegistry.cpp

@@ -185,6 +185,9 @@ corresponding preprocessor flag to selectively disable formats.
 #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
 #   include "D3MFImporter.h"
 #endif
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+#   include "X3DImporter.hpp"
+#endif
 
 namespace Assimp {
 
@@ -331,6 +334,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 #if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER )
     out.push_back(new D3MFImporter() );
 #endif
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+    out.push_back( new X3DImporter() );
+#endif
 }
 
 /** will delete all registered importers. */

+ 1560 - 0
code/X3DImporter.cpp

@@ -0,0 +1,1560 @@
+/// \file X3DImporter.hpp
+/// \brief X3D-format files importer for Assimp: main algorithm implementation.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+#include "fast_atof.h"
+#include "DefaultIOSystem.h"
+
+#include <boost/format.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <string>
+
+namespace Assimp
+{
+
+/// \var aiImporterDesc X3DImporter::Description
+/// Conastant which hold importer description
+const aiImporterDesc X3DImporter::Description = {
+	"Extensible 3D(X3D) Importer",
+	"nevorek",
+	"",
+	"See documentation in source code. Chapter: Limitations.",
+	aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+	0,
+	0,
+	0,
+	0,
+	"x3d"
+};
+
+void X3DImporter::Clear()
+{
+	// Delete all elements
+	if(NodeElement_List.size())
+	{
+		for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++) delete *it;
+
+		NodeElement_List.clear();
+	}
+}
+
+X3DImporter::~X3DImporter()
+{
+	if(mReader != NULL) delete mReader;
+	// Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
+	Clear();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: find set ************************************************************/
+/*********************************************************************************************************************************************/
+
+bool X3DImporter::FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
+{
+	for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
+	{
+		if(((*it)->Type == pType) && ((*it)->ID == pID))
+		{
+			if(pElement != NULL) *pElement = *it;
+
+			return true;
+		}
+	}// for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
+
+	return false;
+}
+
+bool X3DImporter::FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID,
+													const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
+{
+bool found = false;// flag: true - if requested element is found.
+
+	// Check if pStartNode - this is the element, we are looking for.
+	if((pStartNode->Type == pType) && (pStartNode->ID == pID))
+	{
+		found = true;
+		if(pElement != NULL) *pElement = pStartNode;
+
+		goto fne_fn_end;
+	}// if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
+
+	// Check childs of pStartNode.
+	for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = pStartNode->Child.begin(); ch_it != pStartNode->Child.end(); ch_it++)
+	{
+		found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
+		if(found) break;
+	}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Child.begin(); ch_it != it->Child.end(); ch_it++)
+
+fne_fn_end:
+
+	return found;
+}
+
+bool X3DImporter::FindNodeElement(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
+{
+CX3DImporter_NodeElement* tnd = NodeElement_Cur;// temporary pointer to node.
+bool static_search = false;// flag: true if searching in static node.
+
+    // At first check if we have deal with static node. Go up thru parent nodes and check flag.
+    while(tnd != NULL)
+    {
+		if(tnd->Type == CX3DImporter_NodeElement::ENET_Group)
+		{
+			if(((CX3DImporter_NodeElement_Group*)tnd)->Static)
+			{
+				static_search = true;// Flag found, stop walking up. Node with static flag will holded in tnd variable.
+
+				break;
+			}
+		}
+
+		tnd = tnd->Parent;// go up in graph.
+    }// while(tnd != NULL)
+
+    // at now call appropriate search function.
+    if(static_search)
+		return FindNodeElement_FromNode(tnd, pID, pType, pElement);
+	else
+		return FindNodeElement_FromRoot(pID, pType, pElement);
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: throw set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void X3DImporter::Throw_ArgOutOfRange(const std::string& pArgument)
+{
+	throw DeadlyImportError("Argument value is out of range for: \"" + pArgument + "\".");
+}
+
+void X3DImporter::Throw_CloseNotFound(const std::string& pNode)
+{
+	throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+}
+
+void X3DImporter::Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue)
+{
+	throw DeadlyImportError(boost::str(boost::format("In <%s> failed to convert attribute value \"%s\" from string to array of floats.") %
+										mReader->getNodeName() % pAttrValue));
+}
+
+void X3DImporter::Throw_DEF_And_USE()
+{
+	throw DeadlyImportError(boost::str(boost::format("\"DEF\" and \"USE\" can not be defined both in <%s>.") % mReader->getNodeName()));
+}
+
+void X3DImporter::Throw_IncorrectAttr(const std::string& pAttrName)
+{
+	throw DeadlyImportError(boost::str(boost::format("Node <%s> has incorrect attribute \"%s\".") % mReader->getNodeName() % pAttrName));
+}
+
+void X3DImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
+{
+	throw DeadlyImportError(boost::str(boost::format("Attribute \"%s\" in node <%s> has incorrect value.") % pAttrName % mReader->getNodeName()));
+}
+
+void X3DImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
+{
+	throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+}
+
+void X3DImporter::Throw_TagCountIncorrect(const std::string& pNode)
+{
+	throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt.");
+}
+
+void X3DImporter::Throw_USE_NotFound(const std::string& pAttrValue)
+{
+	throw DeadlyImportError(boost::str(boost::format("Not found node with name \"%s\" in <%s>.") % pAttrValue % mReader->getNodeName()));
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************* Functions: XML set ************************************************************/
+/*********************************************************************************************************************************************/
+
+void X3DImporter::XML_CheckNode_MustBeEmpty()
+{
+	if(!mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must be empty.");
+}
+
+void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
+{
+const size_t Uns_Skip_Len = 189;
+const char* Uns_Skip[Uns_Skip_Len] = {
+	// CAD geometry component
+	"CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
+	// Core
+	"ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo",
+	// Distributed interactive simulation (DIS) component
+	"DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu",
+	// Cube map environmental texturing component
+	"ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture",
+	// Environmental effects component
+	"Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground",
+	// Environmental sensor component
+	"ProximitySensor", "TransformSensor", "VisibilitySensor",
+	// Followers component
+	"ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D",
+	"PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D",
+	// Geospatial component
+	"GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor",
+	"GeoTouchSensor", "GeoTransform", "GeoViewpoint",
+	// Humanoid Animation (H-Anim) component
+	"HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite",
+	// Interpolation component
+	"ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator",
+	"PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
+	"SplineScalarInterpolator", "SquadOrientationInterpolator",
+	// Key device sensor component
+	"KeySensor", "StringSensor"
+	// Layering component
+	"Layer", "LayerSet", "Viewport",
+	// Layout component
+	"Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup",
+	// Navigation component
+	"Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup",
+	// Networking component
+	"Anchor", "LoadSensor",
+	// NURBS component
+	"Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface",
+	"NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate",
+	"NurbsTrimmedSurface",
+	// Particle systems component
+	"BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter",
+	"VolumeEmitter", "WindPhysicsModel",
+	// Picking component
+	"LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor",
+	// Pointing device sensor component
+	"CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor",
+	// Rendering component
+	"ClipPlane",
+	// Rigid body physics
+	"BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint",
+	"MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint",
+	// Scripting component
+	"Script",
+	// Programmable shaders component
+	"ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart",
+	"ShaderProgram",
+	// Shape component
+	"FillProperties", "LineProperties", "TwoSidedMaterial",
+	// Sound component
+	"AudioClip", "Sound",
+	// Text component
+	"FontStyle", "Text",
+	// Texturing3D Component
+	"ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D",
+	// Texturing component
+	"MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties",
+	// Time component
+	"TimeSensor",
+	// Event Utilities component
+	"BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger",
+	// Volume rendering component
+	"BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData",
+	"OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle",
+	"VolumeData"
+};
+
+std::string nn(mReader->getNodeName());
+bool found = false;
+bool close_found = false;
+
+	for(size_t i = 0; i < Uns_Skip_Len; i++)
+	{
+		if(nn == Uns_Skip[i])
+		{
+			found = true;
+			if(mReader->isEmptyElement())
+			{
+				close_found = true;
+
+				goto casu_cres;
+			}
+
+			while(mReader->read())
+			{
+				if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
+				{
+					close_found = true;
+
+					goto casu_cres;
+				}
+			}
+		}
+	}
+
+casu_cres:
+
+	if(!found) throw DeadlyImportError(boost::str(boost::format("Unknown node \"%s\" in %s.") % nn % pParentNodeName));
+
+	if(close_found)
+		LogInfo(boost::str(boost::format("Skipping node \"%s\" in %s.") % nn % pParentNodeName));
+	else
+		Throw_CloseNotFound(nn);
+}
+
+bool X3DImporter::XML_SearchNode(const std::string& pNodeName)
+{
+	while(mReader->read())
+	{
+		if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
+	}
+
+	return false;
+}
+
+bool X3DImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
+{
+std::string val(mReader->getAttributeValue(pAttrIdx));
+
+	if(val == "false")
+		return false;
+	else if(val == "true")
+		return true;
+	else
+		throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"" + val + "\"");
+}
+
+float X3DImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
+{
+std::string val;
+float tvalf;
+
+	ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
+	fast_atoreal_move(val.c_str(), tvalf, false);
+
+	return tvalf;
+}
+
+int32_t X3DImporter::XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)
+{
+	return strtol10(mReader->getAttributeValue(pAttrIdx));
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue)
+{
+std::list<float> tlist;
+std::list<float>::iterator it;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
+	if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	it = tlist.begin();
+	pValue.r = *it++;
+	pValue.g = *it++;
+	pValue.b = *it;
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue)
+{
+std::list<float> tlist;
+std::list<float>::iterator it;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
+	if(tlist.size() != 2) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	it = tlist.begin();
+	pValue.x = *it++;
+	pValue.y = *it;
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue)
+{
+std::list<float> tlist;
+std::list<float>::iterator it;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
+	if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	it = tlist.begin();
+	pValue.x = *it++;
+	pValue.y = *it++;
+	pValue.z = *it;
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue)
+{
+char* tok_str;
+size_t tok_str_len;
+
+	// make copy of attribute value - string with list of bool values. Also all bool values is strings.
+	tok_str_len = strlen(mReader->getAttributeValue(pAttrIdx));
+	if(!tok_str_len) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
+
+	tok_str_len++;// take in account terminating '\0'.
+	tok_str = new char[tok_str_len];
+
+	strcpy(tok_str, mReader->getAttributeValue(pAttrIdx));
+	// change all spacebars to symbol '\0'. That is needed for parsing.
+	for(size_t i = 0; i < tok_str_len; i++)
+	{
+		if(tok_str[i] == ' ') tok_str[i] = 0;
+	}
+
+	// at now check what current token is
+	for(char *tok_cur = tok_str, *tok_end = (tok_str + tok_str_len); tok_cur < tok_end;)
+	{
+		if(strncmp(tok_cur, "true", 4) == 0)
+		{
+			pValue.push_back(true);
+			tok_cur += 5;// five, not four. Because '\0' must be skipped too.
+		}
+		else if(strncmp(tok_cur, "false", 5) == 0)
+		{
+			pValue.push_back(true);
+			tok_cur += 6;// six, not five. Because '\0' must be skipped too.
+		}
+		else
+		{
+			Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
+		}
+	}// for(char* tok_cur = tok_str, tok_end = (tok_str + tok_str_len); tok_cur < tok_end;)
+
+	// delete temporary string
+	delete [] tok_str;
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue)
+{
+std::list<bool> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListB(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<bool>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
+{
+const char* tstr = mReader->getAttributeValue(pAttrIdx);
+const char* tstr_end = tstr + strlen(tstr);
+
+	do
+	{
+		int32_t tval32;
+		const char* ostr;
+
+		tval32 = strtol10(tstr, &ostr);
+		if(ostr == tstr) break;
+
+		while((ostr < tstr_end) && (*ostr == ' ')) ostr++;// skip spaces between values.
+
+		tstr = ostr;
+		pValue.push_back(tval32);
+	} while(tstr < tstr_end);
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue)
+{
+std::list<int32_t> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListI32(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<int32_t>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
+{
+std::string str_fixed;
+
+	// at first check string values like '.xxx'.
+	ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), str_fixed);
+	if(!str_fixed.size()) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	// and convert all values and place it in list.
+	const char* pstr = str_fixed.c_str();
+	const char* pstr_end = pstr + str_fixed.size();
+
+	do
+	{
+		float tvalf;
+
+		while((*pstr == ' ') && (pstr < pstr_end)) pstr++;// skip spaces between values.
+
+		if(pstr < pstr_end)// additional check, because attribute value can be ended with spaces.
+		{
+			pstr = fast_atoreal_move(pstr, tvalf, false);
+			pValue.push_back(tvalf);
+		}
+	} while(pstr < pstr_end);
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue)
+{
+std::list<float> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<float>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
+{
+std::string str_fixed;
+
+	// at first check string values like '.xxx'.
+	ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), str_fixed);
+	if(!str_fixed.size()) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	// and convert all values and place it in list.
+	const char* pstr = str_fixed.c_str();
+	const char* pstr_end = pstr + str_fixed.size();
+
+	do
+	{
+		double tvald;
+
+		while((*pstr == ' ') && (pstr < pstr_end)) pstr++;// skip spaces between values.
+
+		if(pstr < pstr_end)// additional check, because attribute value can be ended with spaces.
+		{
+			pstr = fast_atoreal_move(pstr, tvald, false);
+			pValue.push_back(tvald);
+		}
+	} while(pstr < pstr_end);
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue)
+{
+std::list<double> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListD(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<double>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue)
+{
+std::list<float> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+	if(tlist.size() % 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	// copy data to array
+	for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+	{
+		aiColor3D tcol;
+
+		tcol.r = *it++;
+		tcol.g = *it++;
+		tcol.b = *it++;
+		pValue.push_back(tcol);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
+{
+std::list<aiColor3D> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListCol3f(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<aiColor3D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
+{
+std::list<float> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+	if(tlist.size() % 4) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	// copy data to array
+	for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+	{
+		aiColor4D tcol;
+
+		tcol.r = *it++;
+		tcol.g = *it++;
+		tcol.b = *it++;
+		tcol.a = *it++;
+		pValue.push_back(tcol);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue)
+{
+std::list<aiColor4D> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListCol4f(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<aiColor4D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
+{
+std::list<float> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+	if(tlist.size() % 2) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	// copy data to array
+	for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+	{
+		aiVector2D tvec;
+
+		tvec.x = *it++;
+		tvec.y = *it++;
+		pValue.push_back(tvec);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue)
+{
+std::list<aiVector2D> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListVec2f(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
+{
+std::list<float> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+	if(tlist.size() % 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
+
+	// copy data to array
+	for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+	{
+		aiVector3D tvec;
+
+		tvec.x = *it++;
+		tvec.y = *it++;
+		tvec.z = *it++;
+		pValue.push_back(tvec);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue)
+{
+std::list<aiVector3D> tlist;
+
+	XML_ReadNode_GetAttrVal_AsListVec3f(pAttrIdx, tlist);// read as list
+	// and copy to array
+	if(tlist.size() > 0)
+	{
+		pValue.reserve(tlist.size());
+		for(std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+	}
+}
+
+void X3DImporter::XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue)
+{
+char* tok_str;
+char* tok_str_end;
+size_t tok_str_len;
+
+	// make copy of attribute value - strings list.
+	tok_str_len = strlen(mReader->getAttributeValue(pAttrIdx));
+	if(!tok_str_len) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
+
+	// get pointer to begin of value.
+	tok_str = const_cast<char*>(mReader->getAttributeValue(pAttrIdx));
+	tok_str_end = tok_str + tok_str_len;
+	// string list has following format: attr_name='"s1" "s2" "sn"'.
+	do
+	{
+		char* tbeg;
+		char* tend;
+		size_t tlen;
+		std::string tstr;
+
+		// find begin of string(element of string list): "sn".
+		tbeg = strstr(tok_str, "\"");
+		if(tbeg == NULL) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
+
+		tbeg++;// forward pointer from '\"' symbol to next after it.
+		tok_str = tbeg;
+		// find end of string(element of string list): "sn".
+		tend = strstr(tok_str, "\"");
+		if(tend == NULL) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
+
+		tok_str = tend + 1;
+		// create storage for new string
+		tlen = tend - tbeg;
+		tstr.resize(tlen);// reserve enough space and copy data
+		memcpy((void*)tstr.data(), tbeg, tlen);// not strcpy because end of copied string from tok_str has no terminator.
+		// and store string in output list.
+		pValue.push_back(tstr);
+	} while(tok_str < tok_str_end);
+}
+
+/*********************************************************************************************************************************************/
+/****************************************************** Functions: geometry helper set  ******************************************************/
+/*********************************************************************************************************************************************/
+
+aiVector3D X3DImporter::GeometryHelper_Make_Point2D(const float pAngle, const float pRadius)
+{
+	return aiVector3D(pRadius * cosf(pAngle), pRadius * sinf(pAngle), 0);
+}
+
+void X3DImporter::GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments,
+												std::list<aiVector3D>& pVertices)
+{
+float angle_full, angle_step;
+
+	// check argument values ranges.
+	if((pStartAngle < -AI_MATH_TWO_PI_F) || (pStartAngle > AI_MATH_TWO_PI_F)) Throw_ArgOutOfRange("GeometryHelper_Make_Arc2D.pStartAngle");
+	if((pEndAngle < -AI_MATH_TWO_PI_F) || (pEndAngle > AI_MATH_TWO_PI_F)) Throw_ArgOutOfRange("GeometryHelper_Make_Arc2D.pEndAngle");
+	if(pRadius <= 0) Throw_ArgOutOfRange("GeometryHelper_Make_Arc2D.pRadius");
+
+	// calculate arc angle and check type of arc
+	angle_full = fabs(pEndAngle - pStartAngle);
+	if((angle_full > AI_MATH_TWO_PI_F) || (angle_full == 0.0f)) angle_full = AI_MATH_TWO_PI_F;
+
+	// calculate angle for one step - angle to next point of line.
+	angle_step = angle_full / (float)pNumSegments;
+	// make points
+	for(size_t pi = 0; pi <= pNumSegments; pi++)
+	{
+		float tangle;
+
+		tangle = pStartAngle + pi * angle_step;
+		pVertices.push_back(GeometryHelper_Make_Point2D(tangle, pRadius));
+	}// for(size_t pi = 0; pi <= pNumSegments; pi++)
+
+	// if we making full circle then add last vertex equal to first vertex
+	if(angle_full == AI_MATH_TWO_PI_F) pVertices.push_back(*pVertices.begin());
+}
+
+void X3DImporter::GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine)
+{
+std::list<aiVector3D>::const_iterator pit = pPoint.begin();
+std::list<aiVector3D>::const_iterator pit_last = pPoint.end();
+
+	pit_last--;
+
+	if(pPoint.size() < 2) Throw_ArgOutOfRange("GeometryHelper_Extend_PointToLine.pPoint.size() can not be less than 2.");
+
+	// add first point of first line.
+	pLine.push_back(*pit++);
+	// add internal points
+	while(pit != pit_last)
+	{
+		pLine.push_back(*pit);// second point of previous line
+		pLine.push_back(*pit);// first point of next line
+		pit++;
+	}
+	// add last point of last line
+	pLine.push_back(*pit);
+}
+
+void X3DImporter::GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx)
+{
+std::list<int32_t>::const_iterator plit = pPolylineCoordIdx.begin();
+
+	while(plit != pPolylineCoordIdx.end())
+	{
+		// add first point of polyline
+		pLineCoordIdx.push_back(*plit++);
+		while((*plit != (-1)) && (plit != pPolylineCoordIdx.end()))
+		{
+			std::list<int32_t>::const_iterator plit_next;
+
+			plit_next = plit, plit_next++;
+			pLineCoordIdx.push_back(*plit);// second point of previous line.
+			pLineCoordIdx.push_back(-1);// delimiter
+			if((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break;// current polyline is finished
+
+			pLineCoordIdx.push_back(*plit);// first point of next line.
+			plit = plit_next;
+		}// while((*plit != (-1)) && (plit != pPolylineCoordIdx.end()))
+	}// while(plit != pPolylineCoordIdx.end())
+}
+
+#define MESH_RectParallelepiped_CREATE_VERT \
+aiVector3D vert_set[8]; \
+float x1, x2, y1, y2, z1, z2, hs; \
+ \
+	hs = pSize.x / 2, x1 = -hs, x2 = hs; \
+	hs = pSize.y / 2, y1 = -hs, y2 = hs; \
+	hs = pSize.z / 2, z1 = -hs, z2 = hs; \
+	vert_set[0].Set(x2, y1, z2); \
+	vert_set[1].Set(x2, y2, z2); \
+	vert_set[2].Set(x2, y2, z1); \
+	vert_set[3].Set(x2, y1, z1); \
+	vert_set[4].Set(x1, y1, z2); \
+	vert_set[5].Set(x1, y2, z2); \
+	vert_set[6].Set(x1, y2, z1); \
+	vert_set[7].Set(x1, y1, z1)
+
+void X3DImporter::GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices)
+{
+	MESH_RectParallelepiped_CREATE_VERT;
+	MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0);// front
+	MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5);// back
+	MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 3, 0, 4);// left
+	MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 2, 6, 5, 1);// right
+	MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 0, 1, 5, 4);// top
+	MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 6, 2, 3);// bottom
+}
+
+#undef MESH_RectParallelepiped_CREATE_VERT
+
+void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
+{
+std::list<int32_t> f_data(pCoordIdx);
+std::vector<unsigned int> inds;
+unsigned int prim_type = 0;
+
+	if(f_data.back() != (-1)) f_data.push_back(-1);
+
+	// reserve average size.
+	pFaces.reserve(f_data.size() / 3);
+	inds.reserve(4);
+//PrintVectorSet("build. ci", pCoordIdx);
+	for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
+	{
+		// when face is got count how many indices in it.
+		if(*it == (-1))
+		{
+			aiFace tface;
+			size_t ts;
+
+			ts = inds.size();
+			switch(ts)
+			{
+				case 0: goto mg_m_err;
+				case 1: prim_type |= aiPrimitiveType_POINT;
+				case 2: prim_type |= aiPrimitiveType_LINE;
+				case 3: prim_type |= aiPrimitiveType_TRIANGLE;
+				default: prim_type |= aiPrimitiveType_POLYGON;
+			}
+
+			tface.mNumIndices = ts;
+			tface.mIndices = new unsigned int[ts];
+			memcpy(tface.mIndices, inds.data(), ts * sizeof(unsigned int));
+			pFaces.push_back(tface);
+			inds.clear();
+		}// if(*it == (-1))
+		else
+		{
+			inds.push_back(*it);
+		}// if(*it == (-1)) else
+	}// for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
+//PrintVectorSet("build. faces", pCoordIdx);
+
+	pPrimitiveTypes = prim_type;
+
+	return;
+
+mg_m_err:
+
+	for(size_t i = 0, i_e = pFaces.size(); i < i_e; i++) delete [] pFaces.at(i).mIndices;
+
+	pFaces.clear();
+}
+
+void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
+{
+std::list<aiColor4D> tcol;
+
+	// create RGBA array from RGB.
+	for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
+
+	// call existing function for adding RGBA colors
+	MeshGeometry_AddColor(pMesh, tcol, pColorPerVertex);
+}
+
+void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
+{
+std::list<aiColor4D>::const_iterator col_it = pColors.begin();
+
+	if(pColorPerVertex)
+	{
+		if(pColors.size() < pMesh.mNumVertices)
+		{
+			throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddColor1. Colors count(%s) can not be less than Vertices count(%s).") %
+																pColors.size() % pMesh.mNumVertices));
+		}
+
+		// copy colors to mesh
+		pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices];
+		for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mColors[0][i] = *col_it++;
+	}// if(pColorPerVertex)
+	else
+	{
+		if(pColors.size() < pMesh.mNumFaces)
+		{
+			throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddColor1. Colors count(%s) can not be less than Faces count(%s).") %
+																pColors.size() % pMesh.mNumFaces));
+		}
+
+		// copy colors to mesh
+		pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices];
+		for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
+		{
+			// apply color to all vertices of face
+			for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mColors[0][pMesh.mFaces[fi].mIndices[vi]] = *col_it;
+
+			col_it++;
+		}
+	}// if(pColorPerVertex) else
+}
+
+void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+										const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
+{
+std::list<aiColor4D> tcol;
+
+	// create RGBA array from RGB.
+	for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
+
+	// call existing function for adding RGBA colors
+	MeshGeometry_AddColor(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex);
+}
+
+void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+										const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
+{
+std::vector<aiColor4D> col_tgt_arr;
+std::list<aiColor4D> col_tgt_list;
+std::vector<aiColor4D> col_arr_copy;
+
+	if(pCoordIdx.size() == 0) throw DeadlyImportError("MeshGeometry_AddColor2. pCoordIdx can not be empty.");
+
+	// copy list to array because we are need indexed access to colors.
+	col_arr_copy.reserve(pColors.size());
+	for(std::list<aiColor4D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) col_arr_copy.push_back(*it);
+
+	if(pColorPerVertex)
+	{
+		if(pColorIdx.size() > 0)
+		{
+			// check indices array count.
+			if(pColorIdx.size() < pCoordIdx.size())
+			{
+				throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddColor2. Colors indices count(%s) can not be less than Coords inidces count(%s).") %
+																	pColorIdx.size() % pCoordIdx.size()));
+			}
+			// create list with colors for every vertex.
+			col_tgt_arr.resize(pMesh.mNumVertices);
+			for(std::list<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); colidx_it++, coordidx_it++)
+			{
+				if(*colidx_it == (-1)) continue;// skip faces delimiter
+				if((unsigned int)(*coordidx_it) > pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddColor2. Coordinate idx is out of range.");
+				if((unsigned int)*colidx_it > pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddColor2. Color idx is out of range.");
+
+				col_tgt_arr[*coordidx_it] = col_arr_copy[*colidx_it];
+			}
+		}// if(pColorIdx.size() > 0)
+		else
+		{
+			// when color indices list is absent use CoordIdx.
+			// check indices array count.
+			if(pColors.size() < pMesh.mNumVertices)
+			{
+				throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddColor2. Colors count(%s) can not be less than Vertices count(%s).") %
+																	pColors.size() % pMesh.mNumVertices));
+			}
+			// create list with colors for every vertex.
+			col_tgt_arr.resize(pMesh.mNumVertices);
+			for(size_t i = 0; i < pMesh.mNumVertices; i++) col_tgt_arr[i] = col_arr_copy[i];
+		}// if(pColorIdx.size() > 0) else
+	}// if(pColorPerVertex)
+	else
+	{
+		if(pColorIdx.size() > 0)
+		{
+			// check indices array count.
+			if(pColorIdx.size() < pMesh.mNumFaces)
+			{
+				throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddColor2. Colors indices count(%s) can not be less than Faces count(%s).") %
+																	pColorIdx.size() % pMesh.mNumFaces));
+			}
+			// create list with colors for every vertex using faces indices.
+			col_tgt_arr.resize(pMesh.mNumFaces);
+
+			std::list<int32_t>::const_iterator colidx_it = pColorIdx.begin();
+			for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
+			{
+				if((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range.");
+
+				col_tgt_arr[fi] = col_arr_copy[*colidx_it++];
+			}
+		}// if(pColorIdx.size() > 0)
+		else
+		{
+			// when color indices list is absent use CoordIdx.
+			// check indices array count.
+			if(pColors.size() < pMesh.mNumFaces)
+			{
+				throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddColor2. Colors count(%s) can not be less than Faces count(%s).") %
+																	pColors.size() % pMesh.mNumFaces));
+			}
+			// create list with colors for every vertex using faces indices.
+			col_tgt_arr.resize(pMesh.mNumFaces);
+			for(size_t fi = 0; fi < pMesh.mNumFaces; fi++) col_tgt_arr[fi] = col_arr_copy[fi];
+
+		}// if(pColorIdx.size() > 0) else
+	}// if(pColorPerVertex) else
+
+	// copy array to list for calling function that add colors.
+	for(std::vector<aiColor4D>::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); it++) col_tgt_list.push_back(*it);
+	// add prepared colors list to mesh.
+	MeshGeometry_AddColor(pMesh, col_tgt_list, pColorPerVertex);
+}
+
+void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx,
+								const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
+{
+std::vector<size_t> tind;
+std::vector<aiVector3D> norm_arr_copy;
+
+	// copy list to array because we are need indexed access to normals.
+	norm_arr_copy.reserve(pNormals.size());
+	for(std::list<aiVector3D>::const_iterator it = pNormals.begin(); it != pNormals.end(); it++) norm_arr_copy.push_back(*it);
+
+	if(pNormalPerVertex)
+	{
+		const std::list<int32_t>* srcidx;
+
+		if(pNormalIdx.size() > 0)
+		{
+			// check indices array count.
+			if(pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal.");
+
+			srcidx = &pNormalIdx;
+		}
+		else
+		{
+			srcidx = &pCoordIdx;
+		}
+
+		tind.reserve(srcidx->size());
+		for(std::list<int32_t>::const_iterator it = srcidx->begin(); it != srcidx->end(); it++)
+		{
+			if(*it != (-1)) tind.push_back(*it);
+		}
+
+		// copy normals to mesh
+		pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
+		for(size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++)
+		{
+			if(tind[i] >= norm_arr_copy.size())
+				throw DeadlyImportError(boost::str(boost::format("MeshGeometry_AddNormal. Normal index(%s) is out of range. Normals count: %s.") %
+										tind[i] % norm_arr_copy.size()));
+
+			pMesh.mNormals[i] = norm_arr_copy[tind[i]];
+		}
+	}// if(pNormalPerVertex)
+	else
+	{
+		if(pNormalIdx.size() > 0)
+		{
+			if(pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count.");
+
+			std::list<int32_t>::const_iterator normidx_it = pNormalIdx.begin();
+
+			tind.reserve(pNormalIdx.size());
+			for(size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) tind.push_back(*normidx_it++);
+
+		}
+		else
+		{
+			tind.reserve(pMesh.mNumFaces);
+			for(size_t i = 0; i < pMesh.mNumFaces; i++) tind.push_back(i);
+
+		}
+
+		// copy normals to mesh
+		pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
+		for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
+		{
+			aiVector3D tnorm;
+
+			tnorm = norm_arr_copy[tind[fi]];
+			for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = tnorm;
+		}
+	}// if(pNormalPerVertex) else
+}
+
+void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
+{
+std::list<aiVector3D>::const_iterator norm_it = pNormals.begin();
+
+	if(pNormalPerVertex)
+	{
+		if(pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal.");
+
+		// copy normals to mesh
+		pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
+		for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mNormals[i] = *norm_it++;
+	}// if(pNormalPerVertex)
+	else
+	{
+		if(pNormals.size() != pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and faces count must be equal.");
+
+		// copy normals to mesh
+		pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
+		for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
+		{
+			// apply color to all vertices of face
+			for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it;
+
+			norm_it++;
+		}
+	}// if(pNormalPerVertex) else
+}
+
+void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx,
+								const std::list<aiVector2D>& pTexCoords) const
+{
+std::vector<aiVector3D> texcoord_arr_copy;
+std::vector<aiFace> faces;
+unsigned int prim_type;
+
+	// copy list to array because we are need indexed access to normals.
+	texcoord_arr_copy.reserve(pTexCoords.size());
+	for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); it++)
+	{
+		texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0));
+	}
+
+	if(pTexCoordIdx.size() > 0)
+	{
+		GeometryHelper_CoordIdxStr2FacesArr(pTexCoordIdx, faces, prim_type);
+		if(!faces.size()) throw DeadlyImportError("Failed to add texture coordinates to mesh, faces list is empty.");
+		if(faces.size() != pMesh.mNumFaces) throw DeadlyImportError("Texture coordinates faces count must be equal to mesh faces count.");
+	}
+	else
+	{
+		GeometryHelper_CoordIdxStr2FacesArr(pCoordIdx, faces, prim_type);
+	}
+
+	pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices];
+	pMesh.mNumUVComponents[0] = 2;
+	for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
+	{
+		if(pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices)
+			throw DeadlyImportError(boost::str(boost::format("Number of indices in texture face and mesh face must be equal. Invalid face index: %s") % fi));
+
+		for(size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++)
+		{
+			size_t vert_idx = pMesh.mFaces[fi].mIndices[ii];
+			size_t tc_idx = faces.at(fi).mIndices[ii];
+
+			pMesh.mTextureCoords[0][vert_idx] = texcoord_arr_copy.at(tc_idx);
+		}
+	}// for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
+}
+
+void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const
+{
+std::vector<aiVector3D> tc_arr_copy;
+
+	if(pTexCoords.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddTexCoord. Texture coordinates and vertices count must be equal.");
+
+	// copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus.
+	tc_arr_copy.reserve(pTexCoords.size());
+	for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); it++) tc_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0));
+
+	// copy texture coordinates to mesh
+	pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices];
+	pMesh.mNumUVComponents[0] = 2;
+	for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mTextureCoords[0][i] = tc_arr_copy[i];
+}
+
+aiMesh* X3DImporter::GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
+{
+aiMesh* tmesh;
+std::vector<aiFace> faces;
+unsigned int prim_type = 0;
+size_t ts;
+
+	// create faces array from input string with vertices indices.
+	GeometryHelper_CoordIdxStr2FacesArr(pCoordIdx, faces, prim_type);
+	if(!faces.size()) throw DeadlyImportError("Failed to create mesh, faces list is empty.");
+
+	//
+	// Create new mesh and copy geometry data.
+	//
+	tmesh = new aiMesh;
+	ts = faces.size();
+	// faces
+	tmesh->mFaces = new aiFace[ts];
+	tmesh->mNumFaces = ts;
+	for(size_t i = 0; i < ts; i++) tmesh->mFaces[i] = faces.at(i);
+
+	// vertices
+	std::list<aiVector3D>::const_iterator vit = pVertices.begin();
+
+	ts = pVertices.size();
+	tmesh->mVertices = new aiVector3D[ts];
+	tmesh->mNumVertices = ts;
+	for(size_t i = 0; i < ts; i++) tmesh->mVertices[i] = *vit++;
+
+	// set primitives type and return result.
+	tmesh->mPrimitiveTypes = prim_type;
+
+	return tmesh;
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: parse set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void X3DImporter::ParseHelper_Group_Begin(const bool pStatic)
+{
+CX3DImporter_NodeElement_Group* new_group = new CX3DImporter_NodeElement_Group(NodeElement_Cur, pStatic);// create new node with current node as parent.
+
+	// if we are adding not the root element then add new element to current element child list.
+	if(NodeElement_Cur != NULL) NodeElement_Cur->Child.push_back(new_group);
+
+	NodeElement_List.push_back(new_group);// it's a new element - add it to list.
+	NodeElement_Cur = new_group;// switch current element to new one.
+}
+
+void X3DImporter::ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode)
+{
+	NodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
+	NodeElement_Cur = pNode;// switch current element to new one.
+}
+
+void X3DImporter::ParseHelper_Node_Exit()
+{
+	// check if we can walk up.
+	if(NodeElement_Cur != NULL) NodeElement_Cur = NodeElement_Cur->Parent;
+}
+
+void X3DImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
+{
+size_t instr_len;
+
+	pOutString.clear();
+	instr_len = strlen(pInStr);
+	if(!instr_len) return;
+
+	pOutString.reserve(instr_len * 3 / 2);
+	// check and correct floats in format ".x". Must be "x.y".
+	if(pInStr[0] == '.') pOutString.push_back('0');
+
+	pOutString.push_back(pInStr[0]);
+	for(size_t ci = 1; ci < instr_len; ci++)
+	{
+		if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
+		{
+			pOutString.push_back('0');
+			pOutString.push_back('.');
+		}
+		else
+		{
+			pOutString.push_back(pInStr[ci]);
+		}
+	}
+}
+
+void X3DImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
+{
+irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
+boost::scoped_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
+
+	// Check whether we can read from the file
+	if(file.get() == NULL) throw DeadlyImportError("Failed to open X3D file " + pFile + ".");
+	// generate a XML reader for it
+	boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
+	mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+	if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+	// start reading
+	ParseNode_Root();
+
+	delete mReader;
+	// restore old XMLreader
+	mReader = OldReader;
+}
+
+void X3DImporter::ParseNode_Root()
+{
+LogInfo("ParseNode_Root b");
+	// search for root tag <X3D>
+	if(!XML_SearchNode("X3D")) throw DeadlyImportError("Root node \"X3D\" not found.");
+
+	ParseHelper_Group_Begin();// create root node element.
+	// parse other contents
+LogInfo("ParseNode_Root. read loop");
+	while(mReader->read())
+	{
+		if(mReader->getNodeType() != irr::io::EXN_ELEMENT) continue;
+
+		if(XML_CheckNode_NameEqual("head"))
+			ParseNode_Head();
+		else if(XML_CheckNode_NameEqual("Scene"))
+			ParseNode_Scene();
+		else
+			XML_CheckNode_SkipUnsupported("Root");
+	}
+LogInfo("ParseNode_Root. end loop");
+
+	// exit from root node element.
+	ParseHelper_Node_Exit();
+LogInfo("ParseNode_Root e");
+}
+
+void X3DImporter::ParseNode_Head()
+{
+bool close_found = false;// flag: true if close tag of node are found.
+
+	while(mReader->read())
+	{
+		if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		{
+			if(XML_CheckNode_NameEqual("meta"))
+			{
+				XML_CheckNode_MustBeEmpty();
+
+				// adding metada from <head> as MetaString from <Scene>
+				CX3DImporter_NodeElement_MetaString* ms = new CX3DImporter_NodeElement_MetaString(NodeElement_Cur);
+
+				ms->Name = mReader->getAttributeValueSafe("name");
+				// name can not be empty
+				if(!ms->Name.empty())
+				{
+					ms->Value.push_back(mReader->getAttributeValueSafe("content"));
+					NodeElement_List.push_back(ms);
+					if(NodeElement_Cur != NULL) NodeElement_Cur->Child.push_back(ms);
+				}
+			}// if(XML_CheckNode_NameEqual("meta"))
+		}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+		{
+			if(XML_CheckNode_NameEqual("head"))
+			{
+				close_found = true;
+
+				break;
+			}
+		}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT) else
+	}// while(mReader->read())
+
+	if(!close_found) Throw_CloseNotFound("head");
+
+}
+
+void X3DImporter::ParseNode_Scene()
+{
+bool close_found = false;
+ssize_t group_cnt = 0;
+ssize_t transform_cnt = 0;
+ssize_t sw_cnt = 0;
+
+	// while create static node? Because objects name used deeper in "USE" attribute can be equal to some meta in <head> node.
+	ParseHelper_Group_Begin(true);
+	while(mReader->read())
+	{
+		if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		{
+			if(XML_CheckNode_NameEqual("Shape"))
+			{
+				ParseNode_Shape_Shape();
+			}
+			else if(XML_CheckNode_NameEqual("Group"))
+			{
+				group_cnt++;
+				ParseNode_Grouping_Group();
+				if(mReader->isEmptyElement()) group_cnt--;// if node is empty then decrease group counter at this place.
+			}
+			else if(XML_CheckNode_NameEqual("StaticGroup"))
+			{
+				group_cnt++;
+				ParseNode_Grouping_StaticGroup();
+				if(mReader->isEmptyElement()) group_cnt--;// if node is empty then decrease group counter at this place.
+
+			}
+			else if(XML_CheckNode_NameEqual("Transform"))
+			{
+				transform_cnt++;
+				ParseNode_Grouping_Transform();
+				if(mReader->isEmptyElement()) transform_cnt--;// if node is empty then decrease group counter at this place.
+			}
+			else if(XML_CheckNode_NameEqual("Switch"))
+			{
+				sw_cnt++;
+				ParseNode_Grouping_Switch();
+				if(mReader->isEmptyElement()) sw_cnt--;// if node is empty then decrease group counter at this place.
+			}
+			else if(XML_CheckNode_NameEqual("DirectionalLight"))
+			{
+				ParseNode_Lighting_DirectionalLight();
+			}
+			else if(XML_CheckNode_NameEqual("PointLight"))
+			{
+				ParseNode_Lighting_PointLight();
+			}
+			else if(XML_CheckNode_NameEqual("SpotLight"))
+			{
+				ParseNode_Lighting_SpotLight();
+			}
+			else if(XML_CheckNode_NameEqual("Inline"))
+			{
+				ParseNode_Networking_Inline();
+			}
+			else if(!ParseHelper_CheckRead_X3DMetadataObject())
+			{
+				XML_CheckNode_SkipUnsupported("Scene");
+			}
+		}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+		{
+			if(XML_CheckNode_NameEqual("Scene"))
+			{
+				close_found = true;
+
+				break;
+			}
+			else if(XML_CheckNode_NameEqual("Group"))
+			{
+				group_cnt--;
+				ParseNode_Grouping_GroupEnd();
+			}
+			else if(XML_CheckNode_NameEqual("StaticGroup"))
+			{
+				group_cnt--;
+				ParseNode_Grouping_StaticGroupEnd();
+			}
+			else if(XML_CheckNode_NameEqual("Transform"))
+			{
+				transform_cnt--;
+				ParseNode_Grouping_TransformEnd();
+			}
+			else if(XML_CheckNode_NameEqual("Switch"))
+			{
+				sw_cnt--;
+				ParseNode_Grouping_SwitchEnd();
+			}
+		}// if(mReader->getNodeType() == irr::io::EXN_ELEMENT) else
+	}// while(mReader->read())
+
+	ParseHelper_Node_Exit();
+
+	if(group_cnt) Throw_TagCountIncorrect("Group");
+	if(transform_cnt) Throw_TagCountIncorrect("Transform");
+	if(sw_cnt) Throw_TagCountIncorrect("Switch");
+	if(!close_found) Throw_CloseNotFound("Scene");
+
+}
+
+/*********************************************************************************************************************************************/
+/******************************************************** Functions: BaseImporter set ********************************************************/
+/*********************************************************************************************************************************************/
+
+bool X3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
+{
+const std::string extension = GetExtension(pFile);
+
+	if(extension == "x3d") return true;
+
+	if(!extension.length() || pCheckSig)
+	{
+		const char* tokens[] = { "DOCTYPE X3D PUBLIC", "http://www.web3d.org/specifications/x3d" };
+
+		return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
+	}
+
+	return false;
+}
+
+void X3DImporter::GetExtensionList(std::set<std::string>& pExtensionList)
+{
+	pExtensionList.insert("x3d");
+}
+
+const aiImporterDesc* X3DImporter::GetInfo () const
+{
+	return &Description;
+}
+
+void X3DImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+	Clear();// delete old graph.
+	mFileDir = DefaultIOSystem::absolutePath(pFile);
+	ParseFile(pFile, pIOHandler);
+	//
+	// Assimp use static arrays of objects for fast speed of rendering. That's good, but need some additional operations/
+	// We know that geometry objects(meshes) are stored in <Shape>, also in <Shape>-><Appearance> materials(in Assimp logical view)
+	// are stored. So at first we need to count how meshes and materials are stored in scene graph.
+	//
+	// at first creating root node for aiScene.
+	pScene->mRootNode = new aiNode;
+	pScene->mRootNode->mParent = NULL;
+
+	//search for root node element
+	NodeElement_Cur = NodeElement_List.front();
+	while(NodeElement_Cur->Parent != NULL) NodeElement_Cur = NodeElement_Cur->Parent;
+
+	{// fill aiScene with objects.
+		std::list<aiMesh*> mesh_list;
+		std::list<aiMaterial*> mat_list;
+		std::list<aiLight*> light_list;
+
+		// create nodes tree
+		Postprocess_BuildNode(*NodeElement_Cur, *pScene->mRootNode, mesh_list, mat_list, light_list);
+		// copy needed data to scene
+		if(mesh_list.size() > 0)
+		{
+			std::list<aiMesh*>::const_iterator it = mesh_list.begin();
+
+			pScene->mNumMeshes = mesh_list.size();
+			pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+			for(size_t i = 0; i < pScene->mNumMeshes; i++) pScene->mMeshes[i] = *it++;
+		}
+
+		if(mat_list.size() > 0)
+		{
+			std::list<aiMaterial*>::const_iterator it = mat_list.begin();
+
+			pScene->mNumMaterials = mat_list.size();
+			pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+			for(size_t i = 0; i < pScene->mNumMaterials; i++) pScene->mMaterials[i] = *it++;
+		}
+
+		if(light_list.size() > 0)
+		{
+			std::list<aiLight*>::const_iterator it = light_list.begin();
+
+			pScene->mNumLights = light_list.size();
+			pScene->mLights = new aiLight*[pScene->mNumLights];
+			for(size_t i = 0; i < pScene->mNumLights; i++) pScene->mLights[i] = *it++;
+		}
+	}// END: fill aiScene with objects.
+
+	///TODO: IME optimize tree
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 954 - 0
code/X3DImporter.hpp

@@ -0,0 +1,954 @@
+/// \file X3DImporter.hpp
+/// \brief X3D-format files importer for Assimp.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef INCLUDED_AI_X3D_IMPORTER_H
+#define INCLUDED_AI_X3D_IMPORTER_H
+
+// Pay attention - you must include some files from Assimp before including BaseImporter.h.
+// magic region begin
+#include <set>
+
+#include "../include/assimp/DefaultLogger.hpp"
+#include "../include/assimp/importerdesc.h"
+#include "../include/assimp/ProgressHandler.hpp"
+#include "../include/assimp/types.h"
+// magic region end
+#include "BaseImporter.h"
+#include "irrXMLWrapper.h"
+
+#include "X3DImporter_Node.hpp"
+
+namespace Assimp
+{
+
+/// \class X3DImporter
+/// Class that holding scene graph which include: groups, geometry, metadata etc.
+///
+/// Limitations.
+///
+/// Pay attention that X3D is format for interactive graphic and simulations for web browsers.
+/// So not all features can be imported using Assimp.
+///
+/// Unsupported nodes:
+/// 	CAD geometry component:
+///			"CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet"
+///		Core component:
+///			"ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo"
+///		Distributed interactive simulation (DIS) component:
+///			"DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu"
+///		Cube map environmental texturing component:
+///			"ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture"
+///		Environmental effects component:
+///			"Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground"
+///		Environmental sensor component:
+///			"ProximitySensor", "TransformSensor", "VisibilitySensor"
+///		Followers component:
+///			"ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser",
+///			"PositionChaser2D", "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D"
+///		Geospatial component:
+///			"GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor",
+///			"GeoTouchSensor", "GeoTransform", "GeoViewpoint"
+///		Humanoid Animation (H-Anim) component:
+///			"HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite"
+///		Interpolation component:
+///			"ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator",
+///			"PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
+///			"SplineScalarInterpolator", "SquadOrientationInterpolator",
+///		Key device sensor component:
+///			"KeySensor", "StringSensor"
+///		Layering component:
+///			"Layer", "LayerSet", "Viewport"
+///		Layout component:
+///			"Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup"
+///		Navigation component:
+///			"Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup"
+///		Networking component:
+///			"Anchor", "LoadSensor"
+///		NURBS component:
+///			"Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface",
+///			"NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate",
+///			"NurbsTrimmedSurface"
+///		Particle systems component:
+///			"BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter",
+///			"SurfaceEmitter", "VolumeEmitter", "WindPhysicsModel"
+///		Picking component:
+///			"LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor"
+///		Pointing device sensor component:
+///			"CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor"
+///		Rendering component:
+///			"ClipPlane"
+///		Rigid body physics:
+///			"BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint",
+///			"MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint"
+///		Scripting component:
+///			"Script"
+///		Programmable shaders component:
+///			"ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart",
+///			"ShaderProgram",
+///		Shape component:
+///			"FillProperties", "LineProperties", "TwoSidedMaterial"
+///		Sound component:
+///			"AudioClip", "Sound"
+///		Text component:
+///			"FontStyle", "Text"
+///		Texturing3D Component:
+///			"ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D",
+///			"TextureTransform3D"
+///		Texturing component:
+///			"MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator",
+///			"TextureProperties",
+///		Time component:
+///			"TimeSensor"
+///		Event Utilities component:
+///			"BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger",
+///		Volume rendering component:
+///			"BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle",
+///			"IsoSurfaceVolumeData", "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle",
+///			"SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle", "VolumeData"
+///
+/// Supported nodes:
+///		Core component:
+///			"MetadataBoolean", "MetadataDouble", "MetadataFloat", "MetadataInteger", "MetadataSet", "MetadataString"
+///		Geometry2D component:
+///			"Arc2D", "ArcClose2D", "Circle2D", "Disk2D", "Polyline2D", "Polypoint2D", "Rectangle2D", "TriangleSet2D"
+///		Geometry3D component:
+///			"Box", "Cone", "Cylinder", "ElevationGrid", "Extrusion", "IndexedFaceSet", "Sphere"
+///		Grouping component:
+///			"Group", "StaticGroup", "Switch", "Transform"
+///		Lighting component:
+///			"DirectionalLight", "PointLight", "SpotLight"
+///		Networking component:
+///			"Inline"
+///		Rendering component:
+///			"Color", "ColorRGBA", "Coordinate", "IndexedLineSet", "IndexedTriangleFanSet", "IndexedTriangleSet", "IndexedTriangleStripSet", "LineSet",
+///			"PointSet", "TriangleFanSet", "TriangleSet", "TriangleStripSet", "Normal"
+///		Shape component:
+///			"Shape", "Appearance", "Material"
+///		Texturing component:
+///			"ImageTexture", "TextureCoordinate", "TextureTransform"
+///
+/// Limitations of attribute "USE".
+/// If "USE" is set then node must be empty, like that:
+///		<Node USE='name'/>
+/// not the
+///		<Node USE='name'><!-- something --> </Node>
+///
+/// Ignored attributes: "creaseAngle", "convex", "solid".
+///
+/// Texture coordinates generating: only for Sphere, Cone, Cylinder. In all other case used PLANE mapping.
+///		It's better that Assimp main code has powerfull texture coordinates generator. Then is not needed to
+///		duplicate this code in every importer.
+///
+/// Lighting limitations.
+///		If light source placed in some group with "DEF" set. And after that some node is use it group with "USE" attribute then
+///		you will get error about duplicate light sources. That's happening because Assimp require names for lights but do not like
+///		duplicates of it )).
+///
+///	Color for faces.
+/// That's happening when attribute "colorPerVertex" is set to "false". But Assimp do not hold how many colors has mesh and reuire
+/// equal length for mVertices and mColors. You will see the colors but vertices will use call which last used in "colorIdx".
+///
+///	That's all for now. Enjoy
+///
+class X3DImporter : public BaseImporter
+{
+	/***********************************************/
+	/******************** Types ********************/
+	/***********************************************/
+
+	/***********************************************/
+	/****************** Constants ******************/
+	/***********************************************/
+
+private:
+
+	static const aiImporterDesc Description;
+
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+private:
+
+    CX3DImporter_NodeElement* NodeElement_Cur;///< Current element.
+	irr::io::IrrXMLReader* mReader;///< Pointer to XML-reader object
+	std::string mFileDir;
+
+public:
+
+    std::list<CX3DImporter_NodeElement*> NodeElement_List;///< All elements of scene graph.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn X3DImporter(const X3DImporter& pScene)
+	/// Disabled copy constructor.
+	X3DImporter(const X3DImporter& pScene);
+
+	/// \fn X3DImporter& operator=(const X3DImporter& pScene)
+	/// Disabled assign operator.
+	X3DImporter& operator=(const X3DImporter& pScene);
+
+	/// \fn void Clear()
+	/// Clear all temporary data.
+	void Clear();
+
+	/***********************************************/
+	/************* Functions: find set *************/
+	/***********************************************/
+
+	/// \fn bool FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
+	/// Find requested node element. Search will be made in all existing nodes.
+	/// \param [in] pID - ID of requested element.
+	/// \param [in] pType - type of requested element.
+	/// \param [out] pElement - pointer to pointer to item found.
+	/// \return true - if the element is found, else - false.
+	bool FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement);
+
+	/// \fn bool FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
+	/// Find requested node element. Search will be made from pointed node down to childs.
+	/// \param [in] pStartNode - pointer to start node.
+	/// \param [in] pID - ID of requested element.
+	/// \param [in] pType - type of requested element.
+	/// \param [out] pElement - pointer to pointer to item found.
+	/// \return true - if the element is found, else - false.
+	bool FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID, const CX3DImporter_NodeElement::EType pType,
+									CX3DImporter_NodeElement** pElement);
+
+	/// \fn bool FindNodeElement(const std::string& pName, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
+	/// Find requested node element. For "Node"'s accounting flag "Static".
+	/// \param [in] pName - name of requested element.
+	/// \param [in] pType - type of requested element.
+	/// \param [out] pElement - pointer to pointer to item found.
+	/// \return true - if the element is found, else - false.
+	bool FindNodeElement(const std::string& pName, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement);
+
+	/***********************************************/
+	/********* Functions: postprocess set **********/
+	/***********************************************/
+
+	/// \fn aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const
+	/// \return transformation matrix from global coordinate system to local.
+	aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const;
+
+	/// \fn void PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const
+	/// Check if child elements of node element is metadata and add it to temporary list.
+	/// \param [in] pNodeElement - node element where metadata is searching.
+	/// \param [out] pList - temporary list for collected metadata.
+	void PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const;
+
+	/// \fn bool bool PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const
+	/// Check if type of node element is metadata. E.g. <MetadataSet>, <MetadataString>.
+	/// \param [in] pType - checked type.
+	/// \return true - if the type corresponds to the metadata.
+	bool PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const;
+
+	/// \fn bool PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const
+	/// Check if type of node element is geometry object and can be used to build mesh. E.g. <Box>, <Arc2D>.
+	/// \param [in] pType - checked type.
+	/// \return true - if the type corresponds to the mesh.
+	bool PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const;
+
+	/// \fn void Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const
+	/// Read CX3DImporter_NodeElement_Light, create aiLight and add it to list of the lights.
+	/// \param [in] pNodeElement - reference to lisght element(<DirectionalLight>, <PointLight>, <SpotLight>).
+	/// \param [out] pSceneLightList - reference to list of the lights.
+	void Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const;
+
+	/// \fn void Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const
+	/// Create filled structure with type \ref aiMaterial from \ref CX3DImporter_NodeElement. This function itseld extract
+	/// all needed data from scene graph.
+	/// \param [in] pNodeElement - reference to material element(<Appearance>).
+	/// \param [out] pMaterial - pointer to pointer to created material. *pMaterial must be NULL.
+	void Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const;
+
+	/// \fn void Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const
+	/// Create filled structure with type \ref aiMaterial from \ref CX3DImporter_NodeElement. This function itseld extract
+	/// all needed data from scene graph.
+	/// \param [in] pNodeElement - reference to geometry object.
+	/// \param [out] pMesh - pointer to pointer to created mesh. *pMesh must be NULL.
+	void Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const;
+
+	/// \fn void Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const
+	/// Create aiNode from CX3DImporter_NodeElement. Also function check children and make recursive call.
+	/// \param [out] pNode - pointer to pointer to created node. *pNode must be NULL.
+	/// \param [in] pNodeElement - CX3DImporter_NodeElement which read.
+	/// \param [out] pSceneNode - aiNode for filling.
+	/// \param [out] pSceneMeshList - list with aiMesh which belong to scene.
+	/// \param [out] pSceneMaterialList - list with aiMaterial which belong to scene.
+	/// \param [out] pSceneLightList - list with aiLight which belong to scene.
+	void Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList,
+								std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const;
+
+	/// \fn void Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd, std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const
+	/// To create mesh and material kept in <Schape>.
+	/// \param pShapeNodeElement - reference to node element which kept <Shape> data.
+	/// \param pNodeMeshInd - reference to list with mesh indices. When pShapeNodeElement will read new mesh index will be added to this list.
+	/// \param pSceneMeshList - reference to list with meshes. When pShapeNodeElement will read new mesh will be added to this list.
+	/// \param pSceneMaterialList - reference to list with materials. When pShapeNodeElement will read new material will be added to this list.
+	void Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd,
+								std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const;
+
+	/// \fn void Postprocess_CollectMetadata(aiNode& pSceneNode, const CX3DImporter_NodeElement& pNodeElement) const
+	/// Check if child elements of node element is metadata and add it to scene node.
+	/// \param [in] pNodeElement - node element where metadata is searching.
+	/// \param [out] pSceneNode - scene node in which metadata will be added.
+	void Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode) const;
+
+	/***********************************************/
+	/************* Functions: throw set ************/
+	/***********************************************/
+
+	/// \fn void Throw_ArgOutOfRange(const std::string& pArgument)
+	/// Call that function when argument is out of range and exception must be raised.
+	/// \throw DeadlyImportError.
+	/// \param [in] pArgument - argument name.
+	void Throw_ArgOutOfRange(const std::string& pArgument);
+
+	/// \fn void Throw_CloseNotFound(const std::string& pNode)
+	/// Call that function when close tag of node not found and exception must be raised.
+	/// E.g.:
+	/// <Scene>
+	///     <Shape>
+	/// </Scene> <!--- shape not closed --->
+	/// \throw DeadlyImportError.
+	/// \param [in] pNode - node name in which exception happened.
+	void Throw_CloseNotFound(const std::string& pNode);
+
+	/// \fn void Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue)
+	/// Call that function when string value can not be converted to floating point value and exception must be raised.
+	/// \param [in] pAttrValue - attribute value.
+	/// \throw DeadlyImportError.
+	void Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue);
+
+	/// \fn void Throw_DEF_And_USE()
+	/// Call that function when in node defined attributes "DEF" and "USE" and exception must be raised.
+	/// E.g.: <Box DEF="BigBox" USE="MegaBox">
+	/// \throw DeadlyImportError.
+	void Throw_DEF_And_USE();
+
+	/// \fn void Throw_IncorrectAttr(const std::string& pAttrName)
+	/// Call that function when attribute name is incorrect and exception must be raised.
+	/// \param [in] pAttrName - attribute name.
+	/// \throw DeadlyImportError.
+	void Throw_IncorrectAttr(const std::string& pAttrName);
+
+	/// \fn void Throw_IncorrectAttrValue(const std::string& pAttrName)
+	/// Call that function when attribute value is incorrect and exception must be raised.
+	/// \param [in] pAttrName - attribute name.
+	/// \throw DeadlyImportError.
+	void Throw_IncorrectAttrValue(const std::string& pAttrName);
+
+	/// \fn void Throw_MoreThanOnceDefined(const std::string& pNode, const std::string& pDescription)
+	/// Call that function when some type of nodes are defined twice or more when must be used only once and exception must be raised.
+	/// E.g.:
+	/// <Shape>
+	///     <Box/>    <!--- first geometry node --->
+	///     <Sphere/> <!--- second geometry node. raise exception --->
+	/// </Shape>
+	/// \throw DeadlyImportError.
+	/// \param [in] pNodeType - type of node which defined one more time.
+	/// \param [in] pDescription - message about error. E.g. what the node defined while exception raised.
+	void Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription);
+
+	/// \fn void Throw_TagCountIncorrect(const std::string& pNode)
+	/// Call that function when count of opening and closing tags which create group(e.g. <Group>) are not equal and exception must be raised.
+	/// E.g.:
+	/// <Scene>
+	///     <Transform>  <!--- first grouping node begin --->
+	///         <Group>  <!--- second grouping node begin --->
+	///     </Transform> <!--- first grouping node end --->
+	/// </Scene> <!--- one grouping node still not closed --->
+	/// \throw DeadlyImportError.
+	/// \param [in] pNode - node name in which exception happened.
+	void Throw_TagCountIncorrect(const std::string& pNode);
+
+	/// \fn void Throw_USE_NotFound(const std::string& pAttrValue)
+	/// Call that function when defined in "USE" element are not found in graph and exception must be raised.
+	/// \param [in] pAttrValue - "USE" attribute value.
+	/// \throw DeadlyImportError.
+	void Throw_USE_NotFound(const std::string& pAttrValue);
+
+	/***********************************************/
+	/************** Functions: LOG set *************/
+	/***********************************************/
+
+	/// \fn void LogInfo(const std::string& pMessage)
+	/// Short variant for calling \ref DefaultLogger::get()->info()
+	void LogInfo(const std::string& pMessage) { DefaultLogger::get()->info(pMessage); }
+
+	/// \fn void LogWarning(const std::string& pMessage)
+	/// Short variant for calling \ref DefaultLogger::get()->warn()
+	void LogWarning(const std::string& pMessage) { DefaultLogger::get()->warn(pMessage); }
+
+	/// \fn void LogError(const std::string& pMessage)
+	/// Short variant for calling \ref DefaultLogger::get()->error()
+	void LogError(const std::string& pMessage) { DefaultLogger::get()->error(pMessage); }
+
+	/***********************************************/
+	/************** Functions: XML set *************/
+	/***********************************************/
+
+	/// \fn void XML_CheckNode_MustBeEmpty()
+	/// Chek if current node is empty: <node />. If not then exception will throwed.
+	void XML_CheckNode_MustBeEmpty();
+
+	/// \fn bool XML_CheckNode_NameEqual(const std::string& pNodeName)
+	/// Chek if current node name is equal to pNodeName.
+	/// \param [in] pNodeName - name for checking.
+	/// return true if current node name is equal to pNodeName, else - false.
+	bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; }
+
+	/// \fn void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
+	/// Skip unsupported node and report about that. Depend on node name can be skipped begin tag of node all whole node.
+	/// \param [in] pParentNodeName - parent node name. Used for reporting.
+	void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName);
+
+	/// \fn bool XML_SearchNode(const std::string& pNodeName)
+	/// Search for specified node in file. XML file read pointer(mReader) will point to found node or file end after search is end.
+	/// \param [in] pNodeName - requested node name.
+	/// return true - if node is found, else - false.
+	bool XML_SearchNode(const std::string& pNodeName);
+
+	/// \fn bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \return read data.
+	bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx);
+
+	/// \fn float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \return read data.
+	float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx);
+
+	/// \fn int32_t XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \return read data.
+	int32_t XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListBool(const int pAttrIdx, std::list<bool>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue)
+	/// \overload void XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
+	void XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue);
+
+	/// \fn void XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue)
+	/// Read attribute value.
+	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
+	/// \param [out] pValue - read data.
+	void XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue);
+
+	/***********************************************/
+	/******* Functions: geometry helper set  *******/
+	/***********************************************/
+
+	/// \fn aiVector3D GeometryHelper_Make_Point2D(const float pAngle, const float pRadius)
+	/// Make point on surface oXY.
+	/// \param [in] pAngle - angle in radians between radius-vector of point and oX axis. Angle extends from the oX axis counterclockwise to the radius-vector.
+	/// \param [in] pRadius - length of radius-vector.
+	/// \return made point coordinates.
+	aiVector3D GeometryHelper_Make_Point2D(const float pAngle, const float pRadius);
+
+	/// \fn void GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments, std::list<aiVector3D>& pVertices)
+	/// Make 2D figure - linear circular arc with center in (0, 0). The z-coordinate is 0. The arc extends from the pStartAngle counterclockwise
+	/// to the pEndAngle. If pStartAngle and pEndAngle have the same value, a circle is specified. If the absolute difference between pStartAngle
+	/// and pEndAngle is greater than or equal to 2pi, a circle is specified.
+	/// \param [in] pStartAngle - angle in radians of start of the arc.
+	/// \param [in] pEndAngle - angle in radians of end of the arc.
+	/// \param [in] pRadius - radius of the arc.
+	/// \param [out] pNumSegments - number of segments in arc. In other words - tesselation factor.
+	/// \param [out] pVertices - generated vertices.
+	void GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments, std::list<aiVector3D>& pVertices);
+
+	/// \fn void GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine)
+	/// Create line set from point set.
+	/// \param [in] pPoint - input points list.
+	/// \param [out] pLine - made lines list.
+	void GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine);
+
+	/// \fn GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx)
+	/// Create CoordIdx of line set from CoordIdx of polyline set.
+	/// \param [in] pPolylineCoordIdx - vertices indices divided by delimiter "-1". Must contain faces with two or more indices.
+	/// \param [out] pLineCoordIdx - made CoordIdx of line set.
+	void GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx);
+
+	/// \fn void GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices)
+	/// Make 3D body - rectangular parallelepiped with center in (0, 0). QL mean quadlist (\sa pVertices).
+	/// \param [in] pSize - scale factor for body for every axis. E.g. (1, 2, 1) mean: X-size and Z-size - 1, Y-size - 2.
+	/// \param [out] pVertices - generated vertices. The list of vertices is grouped in quads.
+	void GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices);
+
+	/// \fn void GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
+	/// Create faces array from vertices indices array.
+	/// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
+	/// \param [in] pFaces - created faces array.
+	/// \param [in] pPrimitiveTypes - type of primitives in faces.
+	void GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const;
+
+	/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
+	/// Add colors to mesh.
+	/// a. If colorPerVertex is FALSE, colours are applied to each face, as follows:
+	///		If the colorIndex field is not empty, one colour is used for each face of the mesh. There shall be at least as many indices in the
+	///			colorIndex field as there are faces in the mesh. The colorIndex field shall not contain any negative entries.
+	///		If the colorIndex field is empty, the colours in the X3DColorNode node are applied to each face of the mesh in order.
+	///			There shall be at least as many colours in the X3DColorNode node as there are faces.
+	/// b. If colorPerVertex is TRUE, colours are applied to each vertex, as follows:
+	///		If the colorIndex field is not empty, colours are applied to each vertex of the mesh in exactly the same manner that the coordIndex
+	///			field is used to choose coordinates for each vertex from the <Coordinate> node. The colorIndex field shall contain end-of-face markers (−1)
+	///			in exactly the same places as the coordIndex field.
+	///		If the colorIndex field is empty, the coordIndex field is used to choose colours from the X3DColorNode node.
+	/// \param [in] pMesh - mesh for adding data.
+	/// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
+	/// \param [in] pColorIdx - color indices for every vertex divided by delimiter "-1" if \ref pColorPerVertex is true. if \ref pColorPerVertex is false
+	/// then pColorIdx contain color indices for every faces and must not contain delimiter "-1".
+	/// \param [in] pColors - defined colors.
+	/// \param [in] pColorPerVertex - if \ref pColorPerVertex is true then color in \ref pColors defined for every vertex, if false - for every face.
+	void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+								const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
+
+	/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
+	/// \overload void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
+	void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+								const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
+
+	/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
+	/// Add colors to mesh.
+	/// \param [in] pMesh - mesh for adding data.
+	/// \param [in] pColors - defined colors.
+	/// \param [in] pColorPerVertex - if \ref pColorPerVertex is true then color in \ref pColors defined for every vertex, if false - for every face.
+	void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
+
+	/// \fn void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
+	/// \overload void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
+	void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
+
+	/// \fn void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
+	/// Add normals to mesh. Function work similar to \ref MeshGeometry_AddColor;
+	void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx,
+								const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const;
+
+	/// \fn void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
+	/// Add normals to mesh. Function work similar to \ref MeshGeometry_AddColor;
+	void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const;
+
+	/// \fn void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx, const std::list<aiVector2D>& pTexCoords) const
+	/// Add texture coordinates to mesh. Function work similar to \ref MeshGeometry_AddColor;
+	void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx,
+								const std::list<aiVector2D>& pTexCoords) const;
+
+	/// \fn void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const
+	/// Add texture coordinates to mesh. Function work similar to \ref MeshGeometry_AddColor;
+	void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const;
+
+	/// \fn aiMesh* GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
+	/// Create mesh.
+	/// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
+	/// \param [in] pVertices - vertices of mesh.
+	/// \return created mesh.
+	aiMesh* GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const;
+
+	/***********************************************/
+	/******** Functions: parse set private *********/
+	/***********************************************/
+
+	/// \fn void ParseHelper_Group_Begin()
+	/// Create node element with type "Node" in scene graph. That operation is needed when you enter to X3D group node
+	/// like <Group>, <Transform> etc. When exiting from X3D group node(e.g. </Group>) \ref ParseHelper_Node_Exit must
+	/// be called.
+	/// \param [in] pStatic - flag: if true then static node is created(e.g. <StaticGroup>).
+	void ParseHelper_Group_Begin(const bool pStatic = false);
+
+	/// \fn void ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode)
+	/// Make pNode as current and enter deeper for parsing child nodes. At end \ref ParseHelper_Node_Exit must be called.
+	/// \param [in] pNode - new current node.
+	void ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode);
+
+	/// \fn void ParseHelper_Group_End()
+	/// This function must be called when exiting from X3D group node(e.g. </Group>). \ref ParseHelper_Group_Begin.
+	void ParseHelper_Node_Exit();
+
+	/// \fn void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
+	/// Attribute values of floating point types can take form ".x"(without leading zero). irrXMLReader can not read this form of values and it
+	/// must be converted to right form - "0.xxx".
+	/// \param [in] pInStr - pointer to input string which can contain incorrect form of values.
+	/// \param [out[ pOutString - output string with right form of values.
+	void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString);
+
+	/// \fn bool ParseHelper_CheckRead_X3DMetadataObject()
+	/// Check if current node has nodes of type X3DMetadataObject. Why we must do it? Because X3DMetadataObject can be in any non-empty X3DNode.
+	/// Meaning that X3DMetadataObject can be in any non-empty node in <Scene>.
+	/// \return true - if metadata node are found and parsed, false - metadata not found.
+	bool ParseHelper_CheckRead_X3DMetadataObject();
+
+	/// \fn bool ParseHelper_CheckRead_X3DMetadataObject()
+	/// Check if current node has nodes of type X3DGeometricPropertyNode. X3DGeometricPropertyNode
+	/// X3DGeometricPropertyNode inheritors:
+	/// <FogCoordinate>, <HAnimDisplacer>, <Color>, <ColorRGBA>, <Coordinate>, <CoordinateDouble>, <GeoCoordinate>, <Normal>,
+	/// <MultiTextureCoordinate>, <TextureCoordinate>, <TextureCoordinate3D>, <TextureCoordinate4D>, <TextureCoordinateGenerator>,
+	/// <FloatVertexAttribute>, <Matrix3VertexAttribute>, <Matrix4VertexAttribute>.
+	/// \return true - if nodes are found and parsed, false - nodes not found.
+	bool ParseHelper_CheckRead_X3DGeometricPropertyNode();
+
+	/// \fn void ParseNode_Root()
+	/// Parse <X3D> node of the file.
+	void ParseNode_Root();
+
+	/// \fn void ParseNode_Head()
+	/// Parse <head> node of the file.
+	void ParseNode_Head();
+
+	/// \fn void ParseNode_Root()
+	/// Parse <Scene> node of the file.
+	void ParseNode_Scene();
+
+	/// \fn void ParseNode_Metadata(CX3DImporter_NodeElement* pParent, const std::string& pNodeName)
+	/// Parse child nodes of <Metadata*> node.
+	/// \param [in] pNodeName - parsed node name. Must be set because that function is general and name needed for checking the end
+	/// and error reporing.
+	/// \param [in] pParentElement - parent metadata element.
+	void ParseNode_Metadata(CX3DImporter_NodeElement* pParentElement, const std::string& pNodeName);
+
+	/// \fn void ParseNode_MetadataBoolean()
+	/// Parse <MetadataBoolean> node of the file.
+	void ParseNode_MetadataBoolean();
+
+	/// \fn void ParseNode_MetadataDouble()
+	/// Parse <MetadataDouble> node of the file.
+	void ParseNode_MetadataDouble();
+
+	/// \fn void ParseNode_MetadataFloat()
+	/// Parse <MetadataFloat> node of the file.
+	void ParseNode_MetadataFloat();
+
+	/// \fn void ParseNode_MetadataInteger()
+	/// Parse <MetadataInteger> node of the file.
+	void ParseNode_MetadataInteger();
+
+	/// \fn void ParseNode_MetadataSet()
+	/// Parse <MetadataSet> node of the file.
+	void ParseNode_MetadataSet();
+
+	/// \fn void ParseNode_MetadataString()
+	/// Parse <MetadataString> node of the file.
+	void ParseNode_MetadataString();
+
+	/// \fn void ParseNode_Geometry2D_Arc2D()
+	/// Parse <Arc2D> node of the file.
+	void ParseNode_Geometry2D_Arc2D();
+
+	/// \fn void ParseNode_Geometry2D_ArcClose2D()
+	/// Parse <ArcClose2D> node of the file.
+	void ParseNode_Geometry2D_ArcClose2D();
+
+	/// \fn void ParseNode_Geometry2D_Circle2D()
+	/// Parse <Circle2D> node of the file.
+	void ParseNode_Geometry2D_Circle2D();
+
+	/// \fn void ParseNode_Geometry2D_Disk2D()
+	/// Parse <Disk2D> node of the file.
+	void ParseNode_Geometry2D_Disk2D();
+
+	/// \fn void ParseNode_Geometry2D_Polyline2D()
+	/// Parse <Polyline2D> node of the file.
+	void ParseNode_Geometry2D_Polyline2D();
+
+	/// \fn void ParseNode_Geometry2D_Polypoint2D()
+	/// Parse <Polypoint2D> node of the file.
+	void ParseNode_Geometry2D_Polypoint2D();
+
+	/// \fn void ParseNode_Geometry2D_Rectangle2D()
+	/// Parse <Rectangle2D> node of the file.
+	void ParseNode_Geometry2D_Rectangle2D();
+
+	/// \fn void ParseNode_Geometry2D_TriangleSet2D()
+	/// Parse <TriangleSet2D> node of the file.
+	void ParseNode_Geometry2D_TriangleSet2D();
+
+	/// \fn void ParseNode_Geometry3D_Box()
+	/// Parse <Box> node of the file.
+	void ParseNode_Geometry3D_Box();
+
+	/// \fn void ParseNode_Geometry3D_Cone()
+	/// Parse <Cone> node of the file.
+	void ParseNode_Geometry3D_Cone();
+
+	/// \fn void ParseNode_Geometry3D_Cylinder()
+	/// Parse <Cylinder> node of the file.
+	void ParseNode_Geometry3D_Cylinder();
+
+	/// \fn void ParseNode_Geometry3D_ElevationGrid()
+	/// Parse <ElevationGrid> node of the file.
+	void ParseNode_Geometry3D_ElevationGrid();
+
+	/// \fn void ParseNode_Geometry3D_Extrusion()
+	/// Parse <Extrusion> node of the file.
+	void ParseNode_Geometry3D_Extrusion();
+
+	/// \fn void ParseNode_Geometry3D_IndexedFaceSet()
+	/// Parse <IndexedFaceSet> node of the file.
+	void ParseNode_Geometry3D_IndexedFaceSet();
+
+	/// \fn void ParseNode_Geometry3D_Sphere()
+	/// Parse <Sphere> node of the file.
+	void ParseNode_Geometry3D_Sphere();
+
+	/// \fn void ParseNode_Grouping_Group()
+	/// Parse <Group> node of the file. And create new node in scene graph.
+	void ParseNode_Grouping_Group();
+
+	/// \fn void ParseNode_Grouping_GroupEnd()
+	/// Doing actions at an exit from <Group>. Walk up in scene graph.
+	void ParseNode_Grouping_GroupEnd();
+
+	/// \fn void ParseNode_Grouping_StaticGroup()
+	/// Parse <StaticGroup> node of the file. And create new node in scene graph.
+	void ParseNode_Grouping_StaticGroup();
+
+	/// \fn void ParseNode_Grouping_StaticGroupEnd()
+	/// Doing actions at an exit from <StaticGroup>. Walk up in scene graph.
+	void ParseNode_Grouping_StaticGroupEnd();
+
+	/// \fn void ParseNode_Grouping_Switch()
+	/// Parse <Switch> node of the file. And create new node in scene graph.
+	void ParseNode_Grouping_Switch();
+
+	/// \fn void ParseNode_Grouping_SwitchEnd()
+	/// Doing actions at an exit from <Switch>. Walk up in scene graph.
+	void ParseNode_Grouping_SwitchEnd();
+
+	/// \fn void ParseNode_Grouping_Transform()
+	/// Parse <Transform> node of the file. And create new node in scene graph.
+	void ParseNode_Grouping_Transform();
+
+	/// \fn void ParseNode_Grouping_TransformEnd()
+	/// Doing actions at an exit from <Transform>. Walk up in scene graph.
+	void ParseNode_Grouping_TransformEnd();
+
+	/// \fn void ParseNode_Rendering_Color()
+	/// Parse <Color> node of the file.
+	void ParseNode_Rendering_Color();
+
+	/// \fn void ParseNode_Rendering_ColorRGBA()
+	/// Parse <ColorRGBA> node of the file.
+	void ParseNode_Rendering_ColorRGBA();
+
+	/// \fn void ParseNode_Rendering_Coordinate()
+	/// Parse <Coordinate> node of the file.
+	void ParseNode_Rendering_Coordinate();
+
+	/// \fn void ParseNode_Rendering_Normal()
+	/// Parse <Normal> node of the file.
+	void ParseNode_Rendering_Normal();
+
+	/// \fn void ParseNode_Rendering_IndexedLineSet()
+	/// Parse <IndexedLineSet> node of the file.
+	void ParseNode_Rendering_IndexedLineSet();
+
+	/// \fn void ParseNode_Rendering_IndexedTriangleFanSet()
+	/// Parse <IndexedTriangleFanSet> node of the file.
+	void ParseNode_Rendering_IndexedTriangleFanSet();
+
+	/// \fn void ParseNode_Rendering_IndexedTriangleSet()
+	/// Parse <IndexedTriangleSet> node of the file.
+	void ParseNode_Rendering_IndexedTriangleSet();
+
+	/// \fn void ParseNode_Rendering_IndexedTriangleStripSet()
+	/// Parse <IndexedTriangleStripSet> node of the file.
+	void ParseNode_Rendering_IndexedTriangleStripSet();
+
+	/// \fn void ParseNode_Rendering_LineSet()
+	/// Parse <LineSet> node of the file.
+	void ParseNode_Rendering_LineSet();
+
+	/// \fn void ParseNode_Rendering_PointSet()
+	/// Parse <PointSet> node of the file.
+	void ParseNode_Rendering_PointSet();
+
+	/// \fn void ParseNode_Rendering_TriangleFanSet()
+	/// Parse <TriangleFanSet> node of the file.
+	void ParseNode_Rendering_TriangleFanSet();
+
+	/// \fn void ParseNode_Rendering_TriangleSet()
+	/// Parse <TriangleSet> node of the file.
+	void ParseNode_Rendering_TriangleSet();
+
+	/// \fn void ParseNode_Rendering_TriangleStripSet()
+	/// Parse <TriangleStripSet> node of the file.
+	void ParseNode_Rendering_TriangleStripSet();
+
+	/// \fn void ParseNode_Texturing_ImageTexture()
+	/// Parse <ImageTexture> node of the file.
+	void ParseNode_Texturing_ImageTexture();
+
+	/// \fn void ParseNode_Texturing_TextureCoordinate()
+	/// Parse <TextureCoordinate> node of the file.
+	void ParseNode_Texturing_TextureCoordinate();
+
+	/// \fn void ParseNode_Texturing_TextureTransform()
+	/// Parse <TextureTransform> node of the file.
+	void ParseNode_Texturing_TextureTransform();
+
+	/// \fn void ParseNode_Shape_Shape()
+	/// Parse <Shape> node of the file.
+	void ParseNode_Shape_Shape();
+
+	/// \fn void ParseNode_Shape_Appearance()
+	/// Parse <Appearance> node of the file.
+	void ParseNode_Shape_Appearance();
+
+	/// \fn void ParseNode_Shape_Material()
+	/// Parse <Material> node of the file.
+	void ParseNode_Shape_Material();
+
+	/// \fn void ParseNode_Networking_Inline()
+	/// Parse <Inline> node of the file.
+	void ParseNode_Networking_Inline();
+
+	/// \fn void ParseNode_Lighting_DirectionalLight()
+	/// Parse <DirectionalLight> node of the file.
+	void ParseNode_Lighting_DirectionalLight();
+
+	/// \fn void ParseNode_Lighting_PointLight()
+	/// Parse <PointLight> node of the file.
+	void ParseNode_Lighting_PointLight();
+
+	/// \fn void ParseNode_Lighting_SpotLight()
+	/// Parse <SpotLight> node of the file.
+	void ParseNode_Lighting_SpotLight();
+
+public:
+
+	/// \fn X3DImporter()
+	/// Default constructor.
+	X3DImporter()
+		: NodeElement_Cur(NULL), mReader(NULL)
+	{}
+
+	/// \fn ~X3DImporter()
+	/// Default destructor.
+	~X3DImporter();
+
+	/***********************************************/
+	/******** Functions: parse set, public *********/
+	/***********************************************/
+
+	/// \fn void ParseFile(const std::string& pFile, IOSystem* pIOHandler)
+	/// Parse X3D file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
+	/// Also exception can be throwed if trouble will found.
+	/// \param [in] pFile - name of file to be parsed.
+	/// \param [in] pIOHandler - pointer to IO helper object.
+	void ParseFile(const std::string& pFile, IOSystem* pIOHandler);
+
+	/***********************************************/
+	/********* Functions: BaseImporter set *********/
+	/***********************************************/
+
+	bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const;
+	void GetExtensionList(std::set<std::string>& pExtensionList);
+	void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+	const aiImporterDesc* GetInfo ()const;
+
+};// class X3DImporter
+
+}// namespace Assimp
+
+#endif // INCLUDED_AI_X3D_IMPORTER_H

+ 481 - 0
code/X3DImporter_Geometry2D.cpp

@@ -0,0 +1,481 @@
+/// \file X3DImporter_Geometry2D.cpp
+/// \brief Parsing data from nodes of "Geometry2D" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Node.hpp"
+#include "X3DImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+// <Arc2D
+// DEF=""              ID
+// USE=""              IDREF
+// endAngle="1.570796" SFFloat [initializeOnly]
+// radius="1"          SFFloat [initializeOnly]
+// startAngle="0"      SFFloat [initializeOnly]
+// />
+// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
+// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle
+// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different
+// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified.
+void X3DImporter::ParseNode_Geometry2D_Arc2D()
+{
+std::string def, use;
+float endAngle = AI_MATH_HALF_PI_F;
+float radius = 1;
+float startAngle = 0;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// create point list of geometry object and convert it to line set.
+		std::list<aiVector3D> tlist;
+
+		GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist);///TODO: IME - AI_CONFIG for NumSeg
+		GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Arc2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <ArcClose2D
+// DEF=""              ID
+// USE=""              IDREF
+// closureType="PIE"   SFString [initializeOnly], {"PIE", "CHORD"}
+// endAngle="1.570796" SFFloat  [initializeOnly]
+// radius="1"          SFFloat  [initializeOnly]
+// solid="false"       SFBool   [initializeOnly]
+// startAngle="0"      SFFloat  [initializeOnly]
+// />
+// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
+// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius
+// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater
+// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has
+// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between
+// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center.
+// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then
+// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point
+// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when
+// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
+void X3DImporter::ParseNode_Geometry2D_ArcClose2D()
+{
+std::string def, use;
+std::string closureType("PIE");
+float endAngle = AI_MATH_HALF_PI_F;
+float radius = 1;
+bool solid = false;
+float startAngle = 0;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
+		// create point list of geometry object.
+		GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);///TODO: IME - AI_CONFIG for NumSeg
+		// add chord or two radiuses only if not a circle was defined
+		if(!((fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle)))
+		{
+			std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices;// just short alias.
+
+			if((closureType == "PIE") || (closureType == "\"PIE\""))
+				vlist.push_back(aiVector3D(0, 0, 0));// center point - first radial line
+			else if((closureType != "CHORD") && (closureType != "\"CHORD\""))
+				Throw_IncorrectAttrValue("closureType");
+
+			vlist.push_back(*vlist.begin());// arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE).
+		}
+
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.size();
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "ArcClose2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Circle2D
+// DEF=""     ID
+// USE=""     IDREF
+// radius="1" SFFloat  [initializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry2D_Circle2D()
+{
+std::string def, use;
+float radius = 1;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// create point list of geometry object and convert it to line set.
+		std::list<aiVector3D> tlist;
+
+		GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist);///TODO: IME - AI_CONFIG for NumSeg
+		GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Circle2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Disk2D
+// DEF=""          ID
+// USE=""          IDREF
+// innerRadius="0" SFFloat  [initializeOnly]
+// outerRadius="1" SFFloat  [initializeOnly]
+// solid="false"   SFBool   [initializeOnly]
+// />
+// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the
+// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero.
+// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely
+// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall
+// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of
+// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
+void X3DImporter::ParseNode_Geometry2D_Disk2D()
+{
+std::string def, use;
+float innerRadius = 0;
+float outerRadius = 1;
+bool solid = false;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne);
+	}
+	else
+	{
+		std::list<aiVector3D> tlist_o, tlist_i;
+
+		if(innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// create point list of geometry object.
+		///TODO: IME - AI_CONFIG for NumSeg
+		GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o);// outer circle
+		if(innerRadius == 0.0f)
+		{// make filled disk
+			// in tlist_o we already have points of circle. just copy it and sign as polygon.
+			((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices = tlist_o;
+			((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = tlist_o.size();
+		}
+		else if(innerRadius == outerRadius)
+		{// make circle
+			// in tlist_o we already have points of circle. convert it to line set.
+			GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
+			((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
+		}
+		else
+		{// make disk
+			std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices;// just short alias.
+
+			GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i);// inner circle
+			//
+			// create quad list from two point lists
+			//
+			if(tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list.");// tlist_i and tlist_o has equal size.
+
+			// add all quads except last
+			for(std::list<aiVector3D>::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();)
+			{
+				// do not forget - CCW direction
+				vlist.push_back(*it_i++);// 1st point
+				vlist.push_back(*it_o++);// 2nd point
+				vlist.push_back(*it_o);// 3rd point
+				vlist.push_back(*it_i);// 4th point
+			}
+
+			// add last quad
+			vlist.push_back(*tlist_i.end());// 1st point
+			vlist.push_back(*tlist_o.end());// 2nd point
+			vlist.push_back(*tlist_o.begin());// 3rd point
+			vlist.push_back(*tlist_o.begin());// 4th point
+
+			((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 4;
+		}
+
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Disk2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Polyline2D
+// DEF=""          ID
+// USE=""          IDREF
+// lineSegments="" MFVec2F [intializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry2D_Polyline2D()
+{
+std::string def, use;
+std::list<aiVector2D> lineSegments;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		//
+		// convert read point list of geometry object to line set.
+		//
+		std::list<aiVector3D> tlist;
+
+		// convert vec2 to vec3
+		for(std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); it2++) tlist.push_back(aiVector3D(it2->x, it2->y, 0));
+
+		// convert point set to line set
+		GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 2;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Polyline2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Polypoint2D
+// DEF=""   ID
+// USE=""   IDREF
+// point="" MFVec2F [inputOutput]
+// />
+void X3DImporter::ParseNode_Geometry2D_Polypoint2D()
+{
+std::string def, use;
+std::list<aiVector2D> point;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// convert vec2 to vec3
+		for(std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); it2++)
+		{
+			((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
+		}
+
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 1;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Polypoint2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Rectangle2D
+// DEF=""        ID
+// USE=""        IDREF
+// size="2 2"    SFVec2f [initializeOnly]
+// solid="false" SFBool  [initializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry2D_Rectangle2D()
+{
+std::string def, use;
+aiVector2D size(2, 2);
+bool solid = false;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		float x1 = -size.x / 2.0f;
+		float x2 = size.x / 2.0f;
+		float y1 = -size.y / 2.0f;
+		float y2 = size.y / 2.0f;
+		std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices;// just short alias.
+
+		vlist.push_back(aiVector3D(x2, y1, 0));// 1st point
+		vlist.push_back(aiVector3D(x2, y2, 0));// 2nd point
+		vlist.push_back(aiVector3D(x1, y2, 0));// 3rd point
+		vlist.push_back(aiVector3D(x1, y1, 0));// 4th point
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 4;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Rectangle2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <TriangleSet2D
+// DEF=""        ID
+// USE=""        IDREF
+// solid="false" SFBool  [initializeOnly]
+// vertices=""   MFVec2F [inputOutput]
+// />
+void X3DImporter::ParseNode_Geometry2D_TriangleSet2D()
+{
+std::string def, use;
+bool solid = false;
+std::list<aiVector2D> vertices;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne);
+	}
+	else
+	{
+		if(vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// convert vec2 to vec3
+		for(std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); it2++)
+		{
+			((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
+		}
+
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->Solid = solid;
+		((CX3DImporter_NodeElement_Geometry2D*)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "TriangleSet2D");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 947 - 0
code/X3DImporter_Geometry3D.cpp

@@ -0,0 +1,947 @@
+/// \file X3DImporter_Geometry3D.cpp
+/// \brief Parsing data from nodes of "Geometry3D" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "StandardShapes.h"
+
+namespace Assimp
+{
+
+// <Box
+// DEF=""       ID
+// USE=""       IDREF
+// size="2 2 2" SFVec3f [initializeOnly]
+// solid="true" SFBool  [initializeOnly]
+// />
+// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes.
+// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes
+// respectively and each component value shall be greater than zero.
+void X3DImporter::ParseNode_Geometry3D_Box()
+{
+std::string def, use;
+bool solid = true;
+aiVector3D size(2, 2, 2);
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices);// get quad list
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 4;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Box");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Cone
+// DEF=""           ID
+// USE=""           IDREF
+// bottom="true"    SFBool [initializeOnly]
+// bottomRadius="1" SFloat [initializeOnly]
+// height="2"       SFloat [initializeOnly]
+// side="true"      SFBool [initializeOnly]
+// solid="true"     SFBool [initializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry3D_Cone()
+{
+std::string use, def;
+bool bottom = true;
+float bottomRadius = 1;
+float height = 2;
+bool side = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne);
+	}
+	else
+	{
+		const unsigned int tess = 30;///TODO: IME tesselation factor thru ai_property
+		std::vector<aiVector3D> tvec;// temp array for vertices.
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// make cone or parts according to flags.
+		if(side)
+		{
+			StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom);
+		}
+		else if(bottom)
+		{
+			StandardShapes::MakeCircle(bottomRadius, tess, tvec);
+			height = -(height / 2);
+			for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); it++) it->y = height;// y - because circle made in oXZ.
+		}
+
+		// copy data from temp array
+		for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); it++) ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it);
+
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Cone");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Cylinder
+// DEF=""        ID
+// USE=""        IDREF
+// bottom="true" SFBool [initializeOnly]
+// height="2"    SFloat [initializeOnly]
+// radius="1"    SFloat [initializeOnly]
+// side="true"   SFBool [initializeOnly]
+// solid="true"  SFBool [initializeOnly]
+// top="true"    SFBool [initializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry3D_Cylinder()
+{
+std::string use, def;
+bool bottom = true;
+float height = 2;
+float radius = 1;
+bool side = true;
+bool solid = true;
+bool top = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne);
+	}
+	else
+	{
+		const unsigned int tess = 30;///TODO: IME tesselation factor thru ai_property
+		std::vector<aiVector3D> tside;// temp array for vertices of side.
+		std::vector<aiVector3D> tcir;// temp array for vertices of circle.
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		// make cilynder or parts according to flags.
+		if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true);
+
+		height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height.
+		if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir);
+		// copy data from temp arrays
+		std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices;// just short alias.
+
+		for(std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); it++) vlist.push_back(*it);
+
+		if(top)
+		{
+			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); it++)
+			{
+				(*it).y = height;// y - because circle made in oXZ.
+				vlist.push_back(*it);
+			}
+		}// if(top)
+
+		if(bottom)
+		{
+			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); it++)
+			{
+				(*it).y = -height;// y - because circle made in oXZ.
+				vlist.push_back(*it);
+			}
+		}// if(top)
+
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Cylinder");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <ElevationGrid
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// creaseAngle="0"        SFloat  [initializeOnly]
+// height=""              MFloat  [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// xDimension="0"         SFInt32 [initializeOnly]
+// xSpacing="1.0"         SFloat  [initializeOnly]
+// zDimension="0"         SFInt32 [initializeOnly]
+// zSpacing="1.0"         SFloat  [initializeOnly]
+// >
+//   <!-- ColorNormalTexCoordContentModel -->
+// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single
+// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </ElevationGrid>
+// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described
+// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate
+// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero.
+// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals.
+void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+float creaseAngle = 0;
+std::list<float> height;
+bool normalPerVertex = true;
+bool solid = true;
+int32_t xDimension = 0;
+float xSpacing = 1;
+int32_t zDimension = 0;
+float zSpacing = 1;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsListF);
+		MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32);
+		MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32);
+		MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne);
+	}
+	else
+	{
+		if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in <ElevationGrid> must be grater than zero.");
+		if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in <ElevationGrid> must be grater than zero.");
+		if((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\"");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_ElevationGrid& grid_alias = *((CX3DImporter_NodeElement_ElevationGrid*)ne);// create alias for conveience
+
+		{// create grid vertices list
+			std::list<float>::const_iterator he_it = height.begin();
+
+			for(int32_t zi = 0; zi < zDimension; zi++)// rows
+			{
+				for(int32_t xi = 0; xi < xDimension; xi++)// columns
+				{
+					aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi);
+
+					grid_alias.Vertices.push_back(tvec);
+					he_it++;
+				}
+			}
+		}// END: create grid vertices list
+		//
+		// create faces list. In "coordIdx" format
+		//
+		// check if we have quads
+		if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set.
+		{
+			((CX3DImporter_NodeElement_ElevationGrid*)ne)->NumIndices = 2;// will be holded as line set.
+			for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++)
+			{
+				grid_alias.CoordIdx.push_back(i);
+				grid_alias.CoordIdx.push_back(i + 1);
+				grid_alias.CoordIdx.push_back(-1);
+			}
+		}
+		else// two or more elements in every dimension is set. create quad set.
+		{
+			((CX3DImporter_NodeElement_ElevationGrid*)ne)->NumIndices = 4;
+			for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows
+			{
+				for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns
+				{
+					// points direction in face.
+					if(ccw)
+					{
+						// CCW:
+						//	3 2
+						//	0 1
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
+					}
+					else
+					{
+						// CW:
+						//	0 1
+						//	3 2
+						grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
+						grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
+					}// if(ccw) else
+
+					grid_alias.CoordIdx.push_back(-1);
+				}// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)
+			}// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)
+		}// if((xDimension < 2) || (zDimension < 2)) else
+
+		grid_alias.ColorPerVertex = colorPerVertex;
+		grid_alias.NormalPerVertex = normalPerVertex;
+		grid_alias.CreaseAngle = creaseAngle;
+		grid_alias.Solid = solid;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("ElevationGrid");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid");
+
+			MACRO_NODECHECK_LOOPEND("ElevationGrid");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}// if(!mReader->isEmptyElement()) else
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+template<typename TVector>
+static void GeometryHelper_Extrusion_CurveIsClosed(std::vector<TVector>& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed)
+{
+size_t cur_sz = pCurve.size();
+
+	pCurveIsClosed = false;
+	// for curve with less than four points checking is have no sense,
+	if(cur_sz < 4) return;
+
+	for(size_t s = 3, s_e = cur_sz; s < s_e; s++)
+	{
+		// search for first point of duplicated part.
+		if(pCurve[0] == pCurve[s])
+		{
+			bool found = true;
+
+			// check if tail(indexed by b2) is duplicate of head(indexed by b1).
+			for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
+			{
+				if(pCurve[b1] != pCurve[b2])
+				{// points not match: clear flag and break loop.
+					found = false;
+
+					break;
+				}
+			}// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
+
+			// if duplicate tail is found then drop or not it depending on flags.
+			if(found)
+			{
+				pCurveIsClosed = true;
+				if(pDropTail)
+				{
+					if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics.
+
+					pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail
+				}
+
+				break;
+			}// if(found)
+		}// if(pCurve[0] == pCurve[s])
+	}// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++)
+}
+
+static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed)
+{
+const size_t spine_idx_last = pSpine.size() - 1;
+aiVector3D tvec;
+
+	if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases
+	{
+		if(pSpine_Closed)
+		{// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] − spine[n − 2]) to compute the Y-axis.
+			// As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0])
+			// in tail are removed.
+			// So, last point in pSpine is a spine[n - 2]
+			tvec = pSpine[1] - pSpine[spine_idx_last];
+		}
+		else if(pSpine_PointIdx == 0)
+		{// The Y-axis used for the first point is the vector from spine[0] to spine[1]
+			tvec = pSpine[1] - pSpine[0];
+		}
+		else
+		{// The Y-axis used for the last point it is the vector from spine[n−2] to spine[n−1]. In our case(see above about droping tail) spine[n - 1] is
+			// the spine[0].
+			tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1];
+		}
+	}// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))
+	else
+	{// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] − spine[i−1]).
+		tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1];
+	}// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else
+
+	return tvec.Normalize();
+}
+
+static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed,
+													const aiVector3D pVecZ_Prev)
+{
+const aiVector3D zero_vec(0);
+const size_t spine_idx_last = pSpine.size() - 1;
+aiVector3D tvec;
+
+	// at first special cases
+	if(pSpine.size() < 3)// spine have not enough points for vector calculations.
+	{
+		tvec.Set(0, 0, 1);
+	}
+	else if(pSpine_PointIdx == 0)// special case: first point
+	{
+		if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate.
+		{
+			tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]);
+		}
+		else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z.
+		{
+			bool found = false;
+
+			// As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear)
+			// then the Z-axis for the first spine point with a defined Z-axis is used."
+			// Walk thru spine and find Z.
+			for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++)
+			{
+				// (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1])
+				tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]);
+				found = !tvec.Equal(zero_vec);
+			}
+
+			// if entire spine are collinear then use OZ axis.
+			if(!found) tvec.Set(0, 0, 1);
+		}// if(pSpine_Closed) else
+	}// else if(pSpine_PointIdx == 0)
+	else if(pSpine_PointIdx == spine_idx_last)// special case: last point
+	{
+		if(pSpine_Closed)
+		{// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2].
+			tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
+			// if taken spine vectors are collinear then use previous vector Z.
+			if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
+		}
+		else
+		{// vector Z for last point of not closed curve is previous vector Z.
+			tvec = pVecZ_Prev;
+		}
+	}
+	else// regular point
+	{
+		tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
+		// if taken spine vectors are collinear then use previous vector Z.
+		if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
+	}
+
+	// After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis
+	// is flipped (multiplied by −1).
+	if((tvec * pVecZ_Prev) < 0) tvec = -tvec;
+
+	return tvec.Normalize();
+}
+
+// <Extrusion
+// DEF=""                                 ID
+// USE=""                                 IDREF
+// beginCap="true"                        SFBool     [initializeOnly]
+// ccw="true"                             SFBool     [initializeOnly]
+// convex="true"                          SFBool     [initializeOnly]
+// creaseAngle="0.0"                      SFloat     [initializeOnly]
+// crossSection="1 1 1 -1 -1 -1 -1 1 1 1" MFVec2f    [initializeOnly]
+// endCap="true"                          SFBool     [initializeOnly]
+// orientation="0 0 1 0"                  MFRotation [initializeOnly]
+// scale="1 1"                            MFVec2f    [initializeOnly]
+// solid="true"                           SFBool     [initializeOnly]
+// spine="0 0 0 0 1 0"                    MFVec3f    [initializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry3D_Extrusion()
+{
+std::string use, def;
+bool beginCap = true;
+bool ccw = true;
+bool convex = true;
+float creaseAngle = 0;
+std::vector<aiVector2D> crossSection;
+bool endCap = true;
+std::vector<float> orientation;
+std::vector<aiVector2D> scale;
+bool solid = true;
+std::vector<aiVector3D> spine;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f);
+		MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF);
+		MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne);
+	}
+	else
+	{
+		//
+		// check if default values must be assigned
+		//
+		if(spine.size() == 0)
+		{
+			spine.resize(2);
+			spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0);
+		}
+		else if(spine.size() == 1)
+		{
+			throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points.");
+		}
+
+		if(crossSection.size() == 0)
+		{
+			crossSection.resize(5);
+			crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1);
+		}
+
+		{// orientation
+			size_t ori_size = orientation.size() / 4;
+
+			if(ori_size < spine.size())
+			{
+				float add_ori[4];// values that will be added
+
+				if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points.
+				{
+					add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3];
+				}
+				else// else - use default values
+				{
+					add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0;
+				}
+
+				orientation.reserve(spine.size() * 4);
+				for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++)
+					orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]);
+			}
+
+			if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in <Extrusion> must has multiple four quantity of numbers.");
+		}// END: orientation
+
+		{// scale
+			if(scale.size() < spine.size())
+			{
+				aiVector2D add_sc;
+
+				if(scale.size() == 1)// if "scale" has one element then use it value for all spine points.
+					add_sc = scale[0];
+				else// else - use default values
+					add_sc.Set(1, 1);
+
+				scale.reserve(spine.size());
+				for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc);
+			}
+		}// END: scale
+		//
+		// create and if needed - define new geometry object.
+		//
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_IndexedSet& ext_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);// create alias for conveience
+		// assign part of input data
+		ext_alias.CCW = ccw;
+		ext_alias.Convex = convex;
+		ext_alias.CreaseAngle = creaseAngle;
+		ext_alias.Solid = solid;
+
+		//
+		// How we done it at all?
+		// 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector
+		// are applied vor every basis.
+		// 2. After that we can create array of point sets: which are scaled, transfered to basis of relative basis and at final translated to real position
+		// using relative spine point.
+		// 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if
+		// needed. While createing CootdIdx is taking in account CCW flag.
+		// 4. The last step: create Vertices list.
+		//
+        bool spine_closed;// flag: true if spine curve is closed.
+        bool cross_closed;// flag: true if cross curve is closed.
+        std::vector<aiMatrix3x3> basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z.
+        std::vector<std::vector<aiVector3D> > pointset_arr;// array of point sets: cross curves.
+
+        // detect closed curves
+        GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end.
+        GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end.
+        // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface.
+        if(spine_closed)
+        {
+			beginCap |= endCap;
+			endCap = false;
+		}
+
+        {// 1. Calculate array of basises.
+			aiMatrix4x4 rotmat;
+			aiVector3D vecX(0), vecY(0), vecZ(0);
+
+			basis_arr.resize(spine.size());
+			for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
+			{
+				aiVector3D tvec;
+
+				// get axises of basis.
+				vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed);
+				vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ);
+				vecX = (vecY ^ vecZ).Normalize();
+				// get rotation matrix and apply "orientation" to basis
+				aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat);
+				tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z;
+				tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z;
+				tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z;
+			}// for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
+		}// END: 1. Calculate array of basises
+
+		{// 2. Create array of point sets.
+			aiMatrix4x4 scmat;
+			std::vector<aiVector3D> tcross(crossSection.size());
+
+			pointset_arr.resize(spine.size());
+			for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
+			{
+				aiVector3D tc23vec;
+
+				tc23vec.Set(scale[spi].x, 0, scale[spi].y);
+				aiMatrix4x4::Scaling(tc23vec, scmat);
+				for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
+				{
+					aiVector3D tvecX, tvecY, tvecZ;
+
+					tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y);
+					// apply scaling to point
+					tcross[cri] = scmat * tc23vec;
+					//
+					// transfer point to new basis
+					// calculate coordinate in new basis
+					tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x;
+					tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y;
+					tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z;
+					// apply new coordinates and translate it to spine point.
+					tcross[cri] = tvecX + tvecY + tvecZ + spine[spi];
+				}// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++)
+
+				pointset_arr[spi] = tcross;// store transfered point set
+			}// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++)
+		}// END: 2. Create array of point sets.
+
+		{// 3. Create CoordIdx.
+			// add caps if needed
+			if(beginCap)
+			{
+				// add cap as polygon. vertices of cap are places at begin, so just add numbers from zero.
+				for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(i);
+
+				// add delimiter
+				ext_alias.CoordIndex.push_back(-1);
+			}// if(beginCap)
+
+			if(endCap)
+			{
+				// add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset.
+				size_t beg = (pointset_arr.size() - 1) * crossSection.size();
+
+				for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(i);
+
+				// add delimiter
+				ext_alias.CoordIndex.push_back(-1);
+			}// if(beginCap)
+
+			// add quads
+			for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++)
+			{
+				const size_t cr_sz = crossSection.size();
+				const size_t cr_last = crossSection.size() - 1;
+
+				size_t right_col;// hold index basis for points of quad placed in right column;
+
+				if(spi != spi_e)
+					right_col = spi + 1;
+				else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve.
+					right_col = 0;
+				else
+					break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine.
+
+				for(size_t cri = 0; cri < cr_sz; cri++)
+				{
+					if(cri != cr_last)
+					{
+						MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
+											spi * cr_sz + cri, right_col * cr_sz + cri, right_col * cr_sz + cri + 1, spi * cr_sz + cri + 1);
+						// add delimiter
+						ext_alias.CoordIndex.push_back(-1);
+					}
+					else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve.
+					{
+						MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
+											spi * cr_sz + cri, right_col * cr_sz + cri, right_col * cr_sz + 0, spi * cr_sz + 0);
+						// add delimiter
+						ext_alias.CoordIndex.push_back(-1);
+					}
+				}// for(size_t cri = 0; cri < cr_sz; cri++)
+			}// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++)
+		}// END: 3. Create CoordIdx.
+
+		{// 4. Create vertices list.
+			// just copy all vertices
+			for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
+			{
+				for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
+				{
+					ext_alias.Vertices.push_back(pointset_arr[spi][cri]);
+				}
+			}
+		}// END: 4. Create vertices list.
+//PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex);
+//PrintVectorSet("Ext. Vertices", ext_alias.Vertices);
+		// check for child nodes
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Extrusion");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <IndexedFaceSet
+// DEF=""                         ID
+// USE=""                         IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorIndex=""          MFInt32 [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// convex="true"          SFBool  [initializeOnly]
+// coordIndex=""          MFInt32 [initializeOnly]
+// creaseAngle="0"        SFFloat [initializeOnly]
+// normalIndex=""         MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// texCoordIndex=""       MFInt32 [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedFaceSet>
+void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet()
+{
+std::string use, def;
+bool ccw = true;
+std::list<int32_t> colorIndex;
+bool colorPerVertex = true;
+bool convex = true;
+std::list<int32_t> coordIndex;
+float creaseAngle = 0;
+std::list<int32_t> normalIndex;
+bool normalPerVertex = true;
+bool solid = true;
+std::list<int32_t> texCoordIndex;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsListI32);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne);
+	}
+	else
+	{
+		// check data
+		if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorIndex = colorIndex;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.Convex = convex;
+		ne_alias.CoordIndex = coordIndex;
+		ne_alias.CreaseAngle = creaseAngle;
+		ne_alias.NormalIndex = normalIndex;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+		ne_alias.TexCoordIndex = texCoordIndex;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet");
+
+			MACRO_NODECHECK_LOOPEND("IndexedFaceSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Sphere
+// DEF=""       ID
+// USE=""       IDREF
+// radius="1"   SFloat [initializeOnly]
+// solid="true" SFBool [initializeOnly]
+// />
+void X3DImporter::ParseNode_Geometry3D_Sphere()
+{
+std::string use, def;
+float radius = 1;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne);
+	}
+	else
+	{
+		const unsigned int tess = 3;///TODO: IME tesselation factor thru ai_property
+		std::vector<aiVector3D> tlist;
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		StandardShapes::MakeSphere(tess, tlist);
+		// copy data from temp array and apply scale
+		for(std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); it++)
+		{
+			((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it * radius);
+		}
+
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
+		((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Sphere");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 269 - 0
code/X3DImporter_Group.cpp

@@ -0,0 +1,269 @@
+/// \file X3DImporter_Group.cpp
+/// \brief Parsing data from nodes of "Grouping" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+// <Group
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// >
+//    <!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </Group>
+// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform.
+void X3DImporter::ParseNode_Grouping_Group()
+{
+std::string def, use;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		CX3DImporter_NodeElement* ne;
+
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
+	}
+	else
+	{
+		ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
+		// at this place new group mode created and made current, so we can name it.
+		if(!def.empty()) NodeElement_Cur->ID = def;
+		// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+		// for empty element exit from node in that place
+		if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
+	}// if(!use.empty()) else
+}
+
+void X3DImporter::ParseNode_Grouping_GroupEnd()
+{
+	ParseHelper_Node_Exit();// go up in scene graph
+}
+
+// <StaticGroup
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// >
+//    <!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </StaticGroup>
+// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or
+// contain any USE references outside the StaticGroup.
+void X3DImporter::ParseNode_Grouping_StaticGroup()
+{
+std::string def, use;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		CX3DImporter_NodeElement* ne;
+
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
+	}
+	else
+	{
+		ParseHelper_Group_Begin(true);// create new grouping element and go deeper if node has children.
+		// at this place new group mode created and made current, so we can name it.
+		if(!def.empty()) NodeElement_Cur->ID = def;
+		// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+		// for empty element exit from node in that place
+		if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
+	}// if(!use.empty()) else
+}
+
+void X3DImporter::ParseNode_Grouping_StaticGroupEnd()
+{
+	ParseHelper_Node_Exit();// go up in scene graph
+}
+
+// <Switch
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// whichChoice="-1"    SFInt32 [inputOutput]
+// >
+//    <!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </Switch>
+// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child
+// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing
+// is chosen.
+void X3DImporter::ParseNode_Grouping_Switch()
+{
+std::string def, use;
+int32_t whichChoice = -1;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		CX3DImporter_NodeElement* ne;
+
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
+	}
+	else
+	{
+		ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
+		// at this place new group mode created and made current, so we can name it.
+		if(!def.empty()) NodeElement_Cur->ID = def;
+
+		// also set values specific to this type of group
+		((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->UseChoice = true;
+		((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->Choice = whichChoice;
+		// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+		// for empty element exit from node in that place
+		if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
+	}// if(!use.empty()) else
+}
+
+void X3DImporter::ParseNode_Grouping_SwitchEnd()
+{
+	// just exit from node. Defined choice will be accepted at postprocessing stage.
+	ParseHelper_Node_Exit();// go up in scene graph
+}
+
+// <Transform
+// DEF=""                     ID
+// USE=""                     IDREF
+// bboxCenter="0 0 0"         SFVec3f    [initializeOnly]
+// bboxSize="-1 -1 -1"        SFVec3f    [initializeOnly]
+// center="0 0 0"             SFVec3f    [inputOutput]
+// rotation="0 0 1 0"         SFRotation [inputOutput]
+// scale="1 1 1"              SFVec3f    [inputOutput]
+// scaleOrientation="0 0 1 0" SFRotation [inputOutput]
+// translation="0 0 0"        SFVec3f    [inputOutput]
+// >
+//    <!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </Transform>
+// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
+// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate
+// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the
+// equivalent transformation matrices,
+//   P' = T * C * R * SR * S * -SR * -C * P
+void X3DImporter::ParseNode_Grouping_Transform()
+{
+aiVector3D center(0, 0, 0);
+float rotation[4] = {0, 0, 1, 0};
+aiVector3D scale(1, 1, 1);// A value of zero indicates that any child geometry shall not be displayed
+float scale_orientation[4] = {0, 0, 1, 0};
+aiVector3D translation(0, 0, 0);
+aiMatrix4x4 matr, tmatr;
+std::string use, def;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f);
+		if(an == "rotation")
+		{
+			std::vector<float> tvec;
+
+			XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
+			if(tvec.size() != 4) throw DeadlyImportError("<Transform>: rotation vector must have 4 elements.");
+
+			memcpy(rotation, tvec.data(), sizeof(rotation));
+
+			continue;
+		}
+
+		if(an == "scaleOrientation")
+		{
+			std::vector<float> tvec;
+
+			XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
+			if(tvec.size() != 4) throw DeadlyImportError("<Transform>: scaleOrientation vector must have 4 elements.");
+
+			memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation));
+
+			continue;
+		}
+
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		CX3DImporter_NodeElement* ne;
+
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
+	}
+	else
+	{
+		ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
+		// at this place new group mode created and made current, so we can name it.
+		if(!def.empty()) NodeElement_Cur->ID = def;
+
+		//
+		// also set values specific to this type of group
+		//
+		// calculate tranformation matrix
+		aiMatrix4x4::Translation(translation, matr);// T
+		aiMatrix4x4::Translation(center, tmatr);// C
+		matr *= tmatr;
+		aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr);// R
+		matr *= tmatr;
+		aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr);// SR
+		matr *= tmatr;
+		aiMatrix4x4::Scaling(scale, tmatr);// S
+		matr *= tmatr;
+		aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr);// -SR
+		matr *= tmatr;
+		aiMatrix4x4::Translation(-center, tmatr);// -C
+		matr *= tmatr;
+		// and assign it
+		((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->Transformation = matr;
+		// in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+		// for empty element exit from node in that place
+		if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
+	}// if(!use.empty()) else
+}
+
+void X3DImporter::ParseNode_Grouping_TransformEnd()
+{
+	ParseHelper_Node_Exit();// go up in scene graph
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 251 - 0
code/X3DImporter_Light.cpp

@@ -0,0 +1,251 @@
+/// \file X3DImporter_Light.cpp
+/// \brief Parsing data from nodes of "Lighting" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+#include <boost/format.hpp>
+
+namespace Assimp
+{
+
+// <DirectionalLight
+// DEF=""               ID
+// USE=""               IDREF
+// ambientIntensity="0" SFFloat [inputOutput]
+// color="1 1 1"        SFColor [inputOutput]
+// direction="0 0 -1"   SFVec3f [inputOutput]
+// global="false"       SFBool  [inputOutput]
+// intensity="1"        SFFloat [inputOutput]
+// on="true"            SFBool  [inputOutput]
+// />
+void X3DImporter::ParseNode_Lighting_DirectionalLight()
+{
+std::string def, use;
+float ambientIntensity = 0;
+aiColor3D color(1, 1, 1);
+aiVector3D direction(0, 0, -1);
+bool global = false;
+float intensity = 1;
+bool on = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
+		MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne);
+	}
+	else
+	{
+		if(on)
+		{
+			// create and if needed - define new geometry object.
+			ne = new CX3DImporter_NodeElement_Light(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur);
+			if(!def.empty())
+				ne->ID = def;
+			else
+				ne->ID = boost::str(boost::format("DirectionalLight_%s") % (size_t)ne);// make random name
+
+			((CX3DImporter_NodeElement_Light*)ne)->AmbientIntensity = ambientIntensity;
+			((CX3DImporter_NodeElement_Light*)ne)->Color = color;
+			((CX3DImporter_NodeElement_Light*)ne)->Direction = direction;
+			((CX3DImporter_NodeElement_Light*)ne)->Global = global;
+			((CX3DImporter_NodeElement_Light*)ne)->Intensity = intensity;
+			// Assimp want a node with name similar to a light. "Why? I don't no." )
+			ParseHelper_Group_Begin(false);
+
+			NodeElement_Cur->ID = ne->ID;// assign name to node and return to light element.
+			ParseHelper_Node_Exit();
+			// check for child nodes
+			if(!mReader->isEmptyElement())
+				ParseNode_Metadata(ne, "DirectionalLight");
+			else
+				NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+			NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+		}// if(on)
+	}// if(!use.empty()) else
+}
+
+// <PointLight
+// DEF=""               ID
+// USE=""               IDREF
+// ambientIntensity="0" SFFloat [inputOutput]
+// attenuation="1 0 0"  SFVec3f [inputOutput]
+// color="1 1 1"        SFColor [inputOutput]
+// global="true"        SFBool  [inputOutput]
+// intensity="1"        SFFloat [inputOutput]
+// location="0 0 0"     SFVec3f [inputOutput]
+// on="true"            SFBool  [inputOutput]
+// radius="100"         SFFloat [inputOutput]
+// />
+void X3DImporter::ParseNode_Lighting_PointLight()
+{
+std::string def, use;
+float ambientIntensity = 0;
+aiVector3D attenuation(1, 0, 0);
+aiColor3D color(1, 1, 1);
+bool global = true;
+float intensity = 1;
+aiVector3D location(0, 0, 0);
+bool on = true;
+float radius = 100;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
+		MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne);
+	}
+	else
+	{
+		if(on)
+		{
+			// create and if needed - define new geometry object.
+			ne = new CX3DImporter_NodeElement_Light(CX3DImporter_NodeElement::ENET_PointLight, NodeElement_Cur);
+			if(!def.empty()) ne->ID = def;
+
+			((CX3DImporter_NodeElement_Light*)ne)->AmbientIntensity = ambientIntensity;
+			((CX3DImporter_NodeElement_Light*)ne)->Attenuation = attenuation;
+			((CX3DImporter_NodeElement_Light*)ne)->Color = color;
+			((CX3DImporter_NodeElement_Light*)ne)->Global = global;
+			((CX3DImporter_NodeElement_Light*)ne)->Intensity = intensity;
+			((CX3DImporter_NodeElement_Light*)ne)->Location = location;
+			((CX3DImporter_NodeElement_Light*)ne)->Radius = radius;
+			// Assimp want a node with name similar to a light. "Why? I don't no." )
+			ParseHelper_Group_Begin(false);
+			// make random name
+			if(ne->ID.empty()) ne->ID = boost::str(boost::format("PointLight_%s") % (size_t)ne);
+
+			NodeElement_Cur->ID = ne->ID;// assign name to node and return to light element.
+			ParseHelper_Node_Exit();
+			// check for child nodes
+			if(!mReader->isEmptyElement())
+				ParseNode_Metadata(ne, "PointLight");
+			else
+				NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+			NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+		}// if(on)
+	}// if(!use.empty()) else
+}
+
+// <SpotLight
+// DEF=""                 ID
+// USE=""                 IDREF
+// ambientIntensity="0"   SFFloat [inputOutput]
+// attenuation="1 0 0"    SFVec3f [inputOutput]
+// beamWidth="0.7854"     SFFloat [inputOutput]
+// color="1 1 1"          SFColor [inputOutput]
+// cutOffAngle="1.570796" SFFloat [inputOutput]
+// direction="0 0 -1"     SFVec3f [inputOutput]
+// global="true"          SFBool  [inputOutput]
+// intensity="1"          SFFloat [inputOutput]
+// location="0 0 0"       SFVec3f [inputOutput]
+// on="true"              SFBool  [inputOutput]
+// radius="100"           SFFloat [inputOutput]
+// />
+void X3DImporter::ParseNode_Lighting_SpotLight()
+{
+std::string def, use;
+float ambientIntensity = 0;
+aiVector3D attenuation(1, 0, 0);
+float beamWidth = 0.7854;
+aiColor3D color(1, 1, 1);
+float cutOffAngle = 1.570796;
+aiVector3D direction(0, 0, -1);
+bool global = true;
+float intensity = 1;
+aiVector3D location(0, 0, 0);
+bool on = true;
+float radius = 100;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
+		MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f);
+		MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne);
+	}
+	else
+	{
+		if(on)
+		{
+			// create and if needed - define new geometry object.
+			ne = new CX3DImporter_NodeElement_Light(CX3DImporter_NodeElement::ENET_SpotLight, NodeElement_Cur);
+			if(!def.empty()) ne->ID = def;
+
+			if(beamWidth > cutOffAngle) beamWidth = cutOffAngle;
+
+			((CX3DImporter_NodeElement_Light*)ne)->AmbientIntensity = ambientIntensity;
+			((CX3DImporter_NodeElement_Light*)ne)->Attenuation = attenuation;
+			((CX3DImporter_NodeElement_Light*)ne)->BeamWidth = beamWidth;
+			((CX3DImporter_NodeElement_Light*)ne)->Color = color;
+			((CX3DImporter_NodeElement_Light*)ne)->CutOffAngle = cutOffAngle;
+			((CX3DImporter_NodeElement_Light*)ne)->Direction = direction;
+			((CX3DImporter_NodeElement_Light*)ne)->Global = global;
+			((CX3DImporter_NodeElement_Light*)ne)->Intensity = intensity;
+			((CX3DImporter_NodeElement_Light*)ne)->Location = location;
+			((CX3DImporter_NodeElement_Light*)ne)->Radius = radius;
+
+			// Assimp want a node with name similar to a light. "Why? I don't no." )
+			ParseHelper_Group_Begin(false);
+			// make random name
+			if(ne->ID.empty()) ne->ID = boost::str(boost::format("SpotLight_%s") % (size_t)ne);
+
+			NodeElement_Cur->ID = ne->ID;// assign name to node and return to light element.
+			ParseHelper_Node_Exit();
+			// check for child nodes
+			if(!mReader->isEmptyElement())
+				ParseNode_Metadata(ne, "SpotLight");
+			else
+				NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+			NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+		}// if(on)
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 154 - 0
code/X3DImporter_Macro.hpp

@@ -0,0 +1,154 @@
+/// \file X3DImporter_Macro.hpp
+/// \brief Useful macrodefines.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED
+#define X3DIMPORTER_MACRO_HPP_INCLUDED
+
+/// \def MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pNE)
+/// Used for regular checking while attribute "USE" is defined.
+/// \param [in] pDEF - string holding "DEF" value.
+/// \param [in] pUSE - string holding "USE" value.
+/// \param [in] pType - type of element to find.
+/// \param [out] pNE - pointer to found node element.
+#define MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pType, pNE) \
+	do { \
+	XML_CheckNode_MustBeEmpty(); \
+	if(!pDEF.empty()) Throw_DEF_And_USE(); \
+	if(!FindNodeElement(pUSE, CX3DImporter_NodeElement::pType, &pNE)) Throw_USE_NotFound(pUSE); \
+	 \
+	NodeElement_Cur->Child.push_back(pNE);/* add found object as child to current element */ \
+	} while(false)
+
+/// \def MACRO_ATTRREAD_LOOPBEG
+/// Begin of loop that read attributes values.
+#define MACRO_ATTRREAD_LOOPBEG \
+	for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
+	{ \
+		std::string an(mReader->getAttributeName(idx));
+
+/// \def MACRO_ATTRREAD_LOOPEND
+/// End of loop that read attributes values.
+#define MACRO_ATTRREAD_LOOPEND \
+		Throw_IncorrectAttr(an); \
+	}
+
+/// \def MACRO_ATTRREAD_CHECK_REF
+/// Check curent attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
+/// "continue" will called.
+/// \param [in] pAttrName - attribute name.
+/// \param [out] pVarName - output variable name.
+/// \param [in] pFunction - function which read attribute value and write it to pVarName.
+#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
+	if(an == pAttrName) \
+	{ \
+		pFunction(idx, pVarName); \
+		continue; \
+	}
+
+/// \def MACRO_ATTRREAD_CHECK_RET
+/// Check curent attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
+/// If result was read then  "continue" will called.
+/// \param [in] pAttrName - attribute name.
+/// \param [out] pVarName - output variable name.
+/// \param [in] pFunction - function which read attribute value and write it to pVarName.
+#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
+	if(an == pAttrName) \
+	{ \
+		pVarName = pFunction(idx); \
+		continue; \
+	}
+
+/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET
+/// Compact variant for checking "USE" and "DEF". Also skip bbox attributes: "bboxCenter", "bboxSize".
+/// If result was read then  "continue" will called.
+/// \param [out] pDEF_Var - output variable name for "DEF" value.
+/// \param [out] pUSE_Var - output variable name for "USE" value.
+#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pDEF_Var, pUSE_Var) \
+	MACRO_ATTRREAD_CHECK_RET("DEF", pDEF_Var, mReader->getAttributeValue); \
+	MACRO_ATTRREAD_CHECK_RET("USE", pUSE_Var, mReader->getAttributeValue); \
+	if(an == "bboxCenter") continue; \
+	if(an == "bboxSize") continue; \
+	if(an == "containerField") continue; \
+	do {} while(false)
+
+/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
+/// Begin of loop of parsing child nodes. Do not add ';' at end.
+/// \param [in] pNodeName - current node name.
+#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
+	do { \
+	bool close_found = false; \
+	 \
+	while(mReader->read()) \
+	{ \
+		if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
+		{
+
+/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
+/// End of loop of parsing child nodes.
+/// \param [in] pNodeName - current node name.
+#define MACRO_NODECHECK_LOOPEND(pNodeName) \
+		}/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
+		else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
+		{ \
+			if(XML_CheckNode_NameEqual(pNodeName)) \
+			{ \
+				close_found = true; \
+	 \
+				break; \
+			} \
+		}/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
+	}/* while(mReader->read()) */ \
+	 \
+	if(!close_found) Throw_CloseNotFound(pNodeName); \
+	 \
+	} while(false)
+
+#define MACRO_NODECHECK_METADATA(pNodeName) \
+	MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
+			/* and childs must be metadata nodes */ \
+			if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported(pNodeName); \
+	 MACRO_NODECHECK_LOOPEND(pNodeName)
+
+/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4)
+/// Add points as quad. Means that pP1..pP4 set in CCW order.
+#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \
+	do { \
+	if(pCCW) \
+	{ \
+		pOut.push_back(pIn[pP1]); \
+		pOut.push_back(pIn[pP2]); \
+		pOut.push_back(pIn[pP3]); \
+		pOut.push_back(pIn[pP4]); \
+	} \
+	else \
+	{ \
+		pOut.push_back(pIn[pP4]); \
+		pOut.push_back(pIn[pP3]); \
+		pOut.push_back(pIn[pP2]); \
+		pOut.push_back(pIn[pP1]); \
+	} \
+	} while(false)
+
+/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4)
+/// Add points as quad. Means that pP1..pP4 set in CCW order.
+#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \
+	do { \
+	if(pCCW) \
+	{ \
+		pOut.push_back(pP1); \
+		pOut.push_back(pP2); \
+		pOut.push_back(pP3); \
+		pOut.push_back(pP4); \
+	} \
+	else \
+	{ \
+		pOut.push_back(pP4); \
+		pOut.push_back(pP3); \
+		pOut.push_back(pP2); \
+		pOut.push_back(pP1); \
+	} \
+	} while(false)
+
+#endif // X3DIMPORTER_MACRO_HPP_INCLUDED

+ 236 - 0
code/X3DImporter_Metadata.cpp

@@ -0,0 +1,236 @@
+/// \file X3DImporter_Metadata.cpp
+/// \brief Parsing data from nodes of "Metadata" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName)
+/// Find element by "USE" or create new one.
+/// \param [in] pDEF_Var - variable name with "DEF" value.
+/// \param [in] pUSE_Var - variable name with "USE" value.
+/// \param [in] pReference - variable name with "reference" value.
+/// \param [in] pValue - variable name with "value" value.
+/// \param [in, out] pNE - pointer to node element.
+/// \param [in] pMetaClass - Class of node.
+/// \param [in] pMetaName - Name of node.
+/// \param [in] pType - type of element to find.
+#define MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType) \
+	/* if "USE" defined then find already defined element. */ \
+	if(!pUSE_Var.empty()) \
+	{ \
+		MACRO_USE_CHECKANDAPPLY(pDEF_Var, pUSE_Var, pType, pNE); \
+	} \
+	else \
+	{ \
+		pNE = new pMetaClass(NodeElement_Cur); \
+		if(!pDEF_Var.empty()) pNE->ID = pDEF_Var; \
+	 \
+		((pMetaClass*)pNE)->Reference = pReference; \
+		((pMetaClass*)pNE)->Value = pValue; \
+		/* also metadata node can contain childs */ \
+		if(!mReader->isEmptyElement()) \
+			ParseNode_Metadata(pNE, pMetaName);/* in that case node element will be added to child elements list of current node. */ \
+		else \
+			NodeElement_Cur->Child.push_back(pNE);/* else - add element to child list manualy */ \
+	 \
+		NodeElement_List.push_back(pNE);/* add new element to elements list. */ \
+	}/* if(!pUSE_Var.empty()) else */ \
+	 \
+	do {} while(false)
+
+bool X3DImporter::ParseHelper_CheckRead_X3DMetadataObject()
+{
+	if(XML_CheckNode_NameEqual("MetadataBoolean"))
+		ParseNode_MetadataBoolean();
+	else if(XML_CheckNode_NameEqual("MetadataDouble"))
+		ParseNode_MetadataDouble();
+	else if(XML_CheckNode_NameEqual("MetadataFloat"))
+		ParseNode_MetadataFloat();
+	else if(XML_CheckNode_NameEqual("MetadataInteger"))
+		ParseNode_MetadataInteger();
+	else if(XML_CheckNode_NameEqual("MetadataSet"))
+		ParseNode_MetadataSet();
+	else if(XML_CheckNode_NameEqual("MetadataString"))
+		ParseNode_MetadataString();
+	else
+		return false;
+
+	return true;
+}
+
+void X3DImporter::ParseNode_Metadata(CX3DImporter_NodeElement* pParentElement, const std::string& pNodeName)
+{
+	ParseHelper_Node_Enter(pParentElement);
+	MACRO_NODECHECK_METADATA(mReader->getNodeName());
+	ParseHelper_Node_Exit();
+}
+
+// <MetadataBoolean
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFBool   [inputOutput]
+// />
+void X3DImporter::ParseNode_MetadataBoolean()
+{
+std::string def, use;
+std::string name, reference;
+std::list<bool> value;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListB);
+	MACRO_ATTRREAD_LOOPEND;
+
+	MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaBoolean, "MetadataBoolean", ENET_MetaBoolean);
+}
+
+// <MetadataDouble
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFDouble [inputOutput]
+// />
+void X3DImporter::ParseNode_MetadataDouble()
+{
+std::string def, use;
+std::string name, reference;
+std::list<double> value;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListD);
+	MACRO_ATTRREAD_LOOPEND;
+
+	MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaDouble, "MetadataDouble", ENET_MetaDouble);
+}
+
+// <MetadataFloat
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFFloat  [inputOutput]
+// />
+void X3DImporter::ParseNode_MetadataFloat()
+{
+std::string def, use;
+std::string name, reference;
+std::list<float> value;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListF);
+	MACRO_ATTRREAD_LOOPEND;
+
+	MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaFloat, "MetadataFloat", ENET_MetaFloat);
+}
+
+// <MetadataInteger
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString  [inputOutput]
+// reference="" SFString  [inputOutput]
+// value=""     MFInteger [inputOutput]
+// />
+void X3DImporter::ParseNode_MetadataInteger()
+{
+std::string def, use;
+std::string name, reference;
+std::list<int32_t> value;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListI32);
+	MACRO_ATTRREAD_LOOPEND;
+
+	MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaInteger, "MetadataInteger", ENET_MetaInteger);
+}
+
+// <MetadataSet
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// />
+void X3DImporter::ParseNode_MetadataSet()
+{
+std::string def, use;
+std::string name, reference;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_MetaSet, ne);
+	}
+	else
+	{
+		ne = new CX3DImporter_NodeElement_MetaSet(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_MetaSet*)ne)->Reference = reference;
+		// also metadata node can contain childs
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "MetadataSet");
+		else
+			NodeElement_Cur->Child.push_back(ne);// made object as child to current element
+
+		NodeElement_List.push_back(ne);// add new element to elements list.
+	}// if(!use.empty()) else
+}
+
+// <MetadataString
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFString [inputOutput]
+// />
+void X3DImporter::ParseNode_MetadataString()
+{
+std::string def, use;
+std::string name, reference;
+std::list<std::string> value;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListS);
+	MACRO_ATTRREAD_LOOPEND;
+
+	MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaString, "MetadataString", ENET_MetaString);
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 69 - 0
code/X3DImporter_Networking.cpp

@@ -0,0 +1,69 @@
+/// \file X3DImporter_Rendering.cpp
+/// \brief Parsing data from nodes of "Networking" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+#include "DefaultIOSystem.h"
+
+namespace Assimp
+{
+
+// <Inline
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f  [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f  [initializeOnly]
+// load="true"         SFBool   [inputOutput]
+// url=""              MFString [inputOutput]
+// />
+void X3DImporter::ParseNode_Networking_Inline()
+{
+std::string def, use;
+bool load = true;
+std::list<std::string> url;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("load", load, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("url", url, XML_ReadNode_GetAttrVal_AsListS);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		CX3DImporter_NodeElement* ne;
+
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
+	}
+	else
+	{
+		ParseHelper_Group_Begin(true);// create new grouping element and go deeper if node has children.
+		// at this place new group mode created and made current, so we can name it.
+		if(!def.empty()) NodeElement_Cur->ID = def;
+
+		if(load && (url.size() > 0))
+		{
+			DefaultIOSystem io_handler;
+			std::string full_path;
+
+			full_path = mFileDir + "/" + url.front();
+			// Attribute "url" can contain list of strings. But we need only one - first.
+			ParseFile(full_path, &io_handler);
+		}
+
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement()) ParseNode_Metadata(NodeElement_Cur, "Inline");
+
+		// exit from node in that place
+		ParseHelper_Node_Exit();
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 761 - 0
code/X3DImporter_Node.hpp

@@ -0,0 +1,761 @@
+/// \file X3DImporter_Node.hpp
+/// \brief Elements of scene graph.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H
+#define INCLUDED_AI_X3D_IMPORTER_NODE_H
+
+#include <list>
+#include <string>
+
+#include "../include/assimp/types.h"
+#include "../include/assimp/scene.h"
+
+/// \class CX3DImporter_NodeElement
+/// Base class for elements of nodes.
+class CX3DImporter_NodeElement
+{
+	/***********************************************/
+	/******************** Types ********************/
+	/***********************************************/
+
+public:
+
+	/// \enum EType
+	/// Define what data type contain node element.
+	enum EType
+	{
+		ENET_Group,                  ///< Element has type "Group".
+		ENET_MetaBoolean,            ///< Element has type "Metadata boolean".
+		ENET_MetaDouble,             ///< Element has type "Metadata double".
+		ENET_MetaFloat,              ///< Element has type "Metadata float".
+		ENET_MetaInteger,            ///< Element has type "Metadata integer".
+		ENET_MetaSet,                ///< Element has type "Metadata set".
+		ENET_MetaString,             ///< Element has type "Metadata string".
+		ENET_Arc2D,                  ///< Element has type "Arc2D".
+		ENET_ArcClose2D,             ///< Element has type "ArcClose2D".
+		ENET_Circle2D,               ///< Element has type "Circle2D".
+		ENET_Disk2D,                 ///< Element has type "Disk2D".
+		ENET_Polyline2D,             ///< Element has type "Polyline2D".
+		ENET_Polypoint2D,            ///< Element has type "Polypoint2D".
+		ENET_Rectangle2D,            ///< Element has type "Rectangle2D".
+		ENET_TriangleSet2D,          ///< Element has type "TriangleSet2D".
+		ENET_Box,                    ///< Element has type "Box".
+		ENET_Cone,                   ///< Element has type "Cone".
+		ENET_Cylinder,               ///< Element has type "Cylinder".
+		ENET_Sphere,                 ///< Element has type "Sphere".
+		ENET_ElevationGrid,          ///< Element has type "ElevationGrid".
+		ENET_Extrusion,              ///< Element has type "Extrusion".
+		ENET_Coordinate,             ///< Element has type "Coordinate".
+		ENET_Normal,                 ///< Element has type "Normal".
+		ENET_TextureCoordinate,      ///< Element has type "TextureCoordinate".
+		ENET_IndexedFaceSet,         ///< Element has type "IndexedFaceSet".
+		ENET_IndexedLineSet,         ///< Element has type "IndexedLineSet".
+		ENET_IndexedTriangleSet,     ///< Element has type "IndexedTriangleSet".
+		ENET_IndexedTriangleFanSet,  ///< Element has type "IndexedTriangleFanSet".
+		ENET_IndexedTriangleStripSet,///< Element has type "IndexedTriangleStripSet".
+		ENET_LineSet,                ///< Element has type "LineSet".
+		ENET_PointSet,               ///< Element has type "PointSet".
+		ENET_TriangleSet,            ///< Element has type "TriangleSet".
+		ENET_TriangleFanSet,         ///< Element has type "TriangleFanSet".
+		ENET_TriangleStripSet,       ///< Element has type "TriangleStripSet".
+		ENET_Color,                  ///< Element has type "Color".
+		ENET_ColorRGBA,              ///< Element has type "ColorRGBA".
+		ENET_Shape,                  ///< Element has type "Shape".
+		ENET_Appearance,             ///< Element has type "Appearance".
+		ENET_Material,               ///< Element has type "Material".
+		ENET_ImageTexture,           ///< Element has type "ImageTexture".
+		ENET_TextureTransform,       ///< Element has type "TextureTransform".
+		ENET_DirectionalLight,       ///< Element has type "DirectionalLight".
+		ENET_PointLight,             ///< Element has type "PointLight".
+		ENET_SpotLight,              ///< Element has type "SpotLight".
+
+		ENET_Invalid                 ///< Element has invalid type and possible contain invalid data.
+	};
+
+	/***********************************************/
+	/****************** Constants ******************/
+	/***********************************************/
+
+public:
+
+	const EType Type;
+
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	std::string ID;///< ID of the element. Can be empty. In X3D synonym for "ID" attribute.
+	CX3DImporter_NodeElement* Parent;///< Parrent element. If NULL then this node is root.
+	std::list<CX3DImporter_NodeElement*> Child;///< Child elements.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement(const CX3DImporter_NodeElement& pNodeElement)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement(const CX3DImporter_NodeElement& pNodeElement);
+
+	/// \fn CX3DImporter_NodeElement& operator=(const CX3DImporter_NodeElement& pNodeElement)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement& operator=(const CX3DImporter_NodeElement& pNodeElement);
+
+	/// \fn CX3DImporter_NodeElement()
+	/// Disabled default constructor.
+	CX3DImporter_NodeElement();
+
+protected:
+
+	/// \fn CX3DImporter_NodeElement(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// In constructor inheritor must set element type.
+	/// \param [in] pType - element type.
+	/// \param [in] pParent - parent element.
+	CX3DImporter_NodeElement(const EType pType, CX3DImporter_NodeElement* pParent)
+		: Type(pType), Parent(pParent)
+	{}
+
+};// class IX3DImporter_NodeElement
+
+/// \class CX3DImporter_NodeElement_Group
+/// Class that define grouping node. Define transformation matrix for children.
+/// Also can select which child will be kept and others are removed.
+class CX3DImporter_NodeElement_Group : public CX3DImporter_NodeElement
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	aiMatrix4x4 Transformation;///< Transformation matrix.
+
+	/// \var bool Static
+	/// As you know node elements can use already defined node elements when attribute "USE" is defined.
+	/// Standard search when looking for an element in the whole scene graph, existing at this moment.
+	/// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static.
+	bool Static;
+
+	bool UseChoice;///< Flag: if true then use number from \ref Choice to choose what the child will be kept.
+	int32_t Choice;///< Number of the child which will be kept.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_Group(const CX3DImporter_NodeElement_Group& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_Group(const CX3DImporter_NodeElement_Group& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Group& operator=(const CX3DImporter_NodeElement_Group& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_Group& operator=(const CX3DImporter_NodeElement_Group& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Group()
+	/// Disabled default constructor.
+	CX3DImporter_NodeElement_Group();
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_Group(CX3DImporter_NodeElement_Group* pParent, const bool pStatic = false)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pStatic - static node flag.
+	CX3DImporter_NodeElement_Group(CX3DImporter_NodeElement* pParent, const bool pStatic = false)
+		: CX3DImporter_NodeElement(ENET_Group, pParent), Static(pStatic), UseChoice(false)
+	{}
+
+};// class CX3DImporter_NodeElement_Group
+
+/// \class CX3DImporter_NodeElement_Meta
+/// This struct describe metavalue.
+class CX3DImporter_NodeElement_Meta : public CX3DImporter_NodeElement
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	std::string Name;///< Name of metadata object.
+	/// \var std::string Reference
+	/// If provided, it identifies the metadata standard or other specification that defines the name field. If the reference field is not provided or is
+	/// empty, the meaning of the name field is considered implicit to the characters in the string.
+	std::string Reference;
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_Meta(const CX3DImporter_NodeElement_Meta& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_Meta(const CX3DImporter_NodeElement_Meta& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Meta& operator=(const CX3DImporter_NodeElement_Meta& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_Meta& operator=(const CX3DImporter_NodeElement_Meta& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Meta()
+	/// Disabled default constructor.
+	CX3DImporter_NodeElement_Meta();
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_Meta(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// In constructor inheritor must set element type.
+	/// \param [in] pType - element type.
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_Meta(const EType pType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(pType, pParent)
+	{}
+
+};// class CX3DImporter_NodeElement_Meta
+
+/// \struct CX3DImporter_NodeElement_MetaBoolean
+/// This struct describe metavalue of type boolean.
+struct CX3DImporter_NodeElement_MetaBoolean : public CX3DImporter_NodeElement_Meta
+{
+	std::list<bool> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_MetaBoolean(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_MetaBoolean(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Meta(ENET_MetaBoolean, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_MetaBoolean
+
+/// \struct CX3DImporter_NodeElement_MetaDouble
+/// This struct describe metavalue of type double.
+struct CX3DImporter_NodeElement_MetaDouble : public CX3DImporter_NodeElement_Meta
+{
+	std::list<double> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_MetaDouble(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_MetaDouble(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Meta(ENET_MetaDouble, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_MetaDouble
+
+/// \struct CX3DImporter_NodeElement_MetaFloat
+/// This struct describe metavalue of type float.
+struct CX3DImporter_NodeElement_MetaFloat : public CX3DImporter_NodeElement_Meta
+{
+	std::list<float> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_MetaFloat(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_MetaFloat(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Meta(ENET_MetaFloat, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_MetaFloat
+
+/// \struct CX3DImporter_NodeElement_MetaInteger
+/// This struct describe metavalue of type integer.
+struct CX3DImporter_NodeElement_MetaInteger : public CX3DImporter_NodeElement_Meta
+{
+	std::list<int32_t> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_MetaInteger(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_MetaInteger(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Meta(ENET_MetaInteger, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_MetaInteger
+
+/// \struct CX3DImporter_NodeElement_MetaSet
+/// This struct describe container for metaobjects.
+struct CX3DImporter_NodeElement_MetaSet : public CX3DImporter_NodeElement_Meta
+{
+	std::list<CX3DImporter_NodeElement_Meta> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_MetaSet(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_MetaSet(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Meta(ENET_MetaSet, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_MetaSet
+
+/// \struct CX3DImporter_NodeElement_MetaString
+/// This struct describe metavalue of type string.
+struct CX3DImporter_NodeElement_MetaString : public CX3DImporter_NodeElement_Meta
+{
+	std::list<std::string> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_MetaString(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_MetaString(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Meta(ENET_MetaString, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_MetaString
+
+/// \struct CX3DImporter_NodeElement_Color
+/// This struct hold <Color> value.
+struct CX3DImporter_NodeElement_Color : public CX3DImporter_NodeElement
+{
+	std::list<aiColor3D> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_Color(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_Color(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_Color, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_Color
+
+/// \struct CX3DImporter_NodeElement_ColorRGBA
+/// This struct hold <ColorRGBA> value.
+struct CX3DImporter_NodeElement_ColorRGBA : public CX3DImporter_NodeElement
+{
+	std::list<aiColor4D> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_ColorRGBA(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_ColorRGBA(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_ColorRGBA, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_ColorRGBA
+
+/// \struct CX3DImporter_NodeElement_Coordinate
+/// This struct hold <Coordinate> value.
+struct CX3DImporter_NodeElement_Coordinate : public CX3DImporter_NodeElement
+{
+	std::list<aiVector3D> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_Coordinate(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_Coordinate(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_Coordinate, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_Coordinate
+
+/// \struct CX3DImporter_NodeElement_Normal
+/// This struct hold <Normal> value.
+struct CX3DImporter_NodeElement_Normal : public CX3DImporter_NodeElement
+{
+	std::list<aiVector3D> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_Normal(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_Normal(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_Normal, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_Normal
+
+/// \struct CX3DImporter_NodeElement_TextureCoordinate
+/// This struct hold <TextureCoordinate> value.
+struct CX3DImporter_NodeElement_TextureCoordinate : public CX3DImporter_NodeElement
+{
+	std::list<aiVector2D> Value;///< Stored value.
+
+	/// \fn CX3DImporter_NodeElement_TextureCoordinate(CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_TextureCoordinate(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_TextureCoordinate, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_TextureCoordinate
+
+/// \class CX3DImporter_NodeElement_Geometry2D
+/// Two-dimensional figure.
+class CX3DImporter_NodeElement_Geometry2D : public CX3DImporter_NodeElement
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	std::list<aiVector3D> Vertices;///< Vertices list.
+	size_t NumIndices;///< Number of indices in one face.
+	bool Solid;///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_Geometry2D(const CX3DImporter_NodeElement_Geometry2D& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_Geometry2D(const CX3DImporter_NodeElement_Geometry2D& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Geometry2D& operator=(const CX3DImporter_NodeElement_Geometry2D& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_Geometry2D& operator=(const CX3DImporter_NodeElement_Geometry2D& pNode);
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_Geometry2D(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pType - type of geometry object.
+	CX3DImporter_NodeElement_Geometry2D(const EType pType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(pType, pParent), Solid(true)
+	{}
+
+};// class CX3DImporter_NodeElement_Geometry2D
+
+/// \class CX3DImporter_NodeElement_Geometry3D
+/// Three-dimensional body.
+class CX3DImporter_NodeElement_Geometry3D : public CX3DImporter_NodeElement
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	std::list<aiVector3D> Vertices;///< Vertices list.
+	size_t NumIndices;///< Number of indices in one face.
+	bool Solid;///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_Geometry3D(const CX3DImporter_NodeElement_Geometry3D& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_Geometry3D(const CX3DImporter_NodeElement_Geometry3D& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Geometry3D& operator=(const CX3DImporter_NodeElement_Geometry3D& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_Geometry3D& operator=(const CX3DImporter_NodeElement_Geometry3D& pNode);
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_Geometry3D(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pType - type of geometry object.
+	CX3DImporter_NodeElement_Geometry3D(const EType pType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(pType, pParent), Solid(true)
+	{}
+
+};// class CX3DImporter_NodeElement_Geometry3D
+
+/// \class CX3DImporter_NodeElement_ElevationGrid
+/// Uniform rectangular grid of varying height.
+class CX3DImporter_NodeElement_ElevationGrid : public CX3DImporter_NodeElement_Geometry3D
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
+	bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
+	/// \var CreaseAngle
+	/// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
+	/// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
+	float CreaseAngle;
+	std::list<int32_t> CoordIdx;///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_ElevationGrid(const CX3DImporter_NodeElement_ElevationGrid& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_ElevationGrid(const CX3DImporter_NodeElement_ElevationGrid& pNode);
+
+	/// \fn CX3DImporter_NodeElement_ElevationGrid& operator=(const CX3DImporter_NodeElement_ElevationGrid& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_ElevationGrid& operator=(const CX3DImporter_NodeElement_ElevationGrid& pNode);
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_ElevationGrid(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pType - type of geometry object.
+	CX3DImporter_NodeElement_ElevationGrid(const EType pType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Geometry3D(pType, pParent)
+	{}
+
+};// class CX3DImporter_NodeElement_IndexedSet
+
+/// \class CX3DImporter_NodeElement_IndexedSet
+/// Shape with indexed vertices.
+class CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	/// \var CCW
+	/// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
+	/// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
+	/// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
+	/// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
+	/// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
+	/// ccw field, results are undefined.
+	bool CCW;
+	std::list<int32_t> ColorIndex;///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>.
+	bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
+	/// \var Convex
+	/// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself,
+	/// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results
+	/// even if the convex field is FALSE.
+	bool Convex;
+	std::list<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
+	/// \var CreaseAngle
+	/// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
+	/// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
+	float CreaseAngle;
+	std::list<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
+	bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
+	std::list<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_IndexedSet(const CX3DImporter_NodeElement_IndexedSet& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_IndexedSet(const CX3DImporter_NodeElement_IndexedSet& pNode);
+
+	/// \fn CX3DImporter_NodeElement_IndexedSet& operator=(const CX3DImporter_NodeElement_IndexedSet& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_IndexedSet& operator=(const CX3DImporter_NodeElement_IndexedSet& pNode);
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_IndexedSet(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pType - type of geometry object.
+	CX3DImporter_NodeElement_IndexedSet(const EType pType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Geometry3D(pType, pParent)
+	{}
+
+};// class CX3DImporter_NodeElement_IndexedSet
+
+/// \class CX3DImporter_NodeElement_Set
+/// Shape with set of vertices.
+class CX3DImporter_NodeElement_Set : public CX3DImporter_NodeElement_Geometry3D
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	/// \var CCW
+	/// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
+	/// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
+	/// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
+	/// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
+	/// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
+	/// ccw field, results are undefined.
+	bool CCW;
+	bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
+	bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
+	std::list<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
+	std::list<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
+	std::list<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
+	std::list<int32_t> VertexCount;///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_Set(const CX3DImporter_NodeElement_Set& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_Set(const CX3DImporter_NodeElement_Set& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Set& operator=(const CX3DImporter_NodeElement_Set& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_Set& operator=(const CX3DImporter_NodeElement_Set& pNode);
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_Set(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pType - type of geometry object.
+	CX3DImporter_NodeElement_Set(const EType pType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement_Geometry3D(pType, pParent)
+	{}
+
+};// class CX3DImporter_NodeElement_Set
+
+/// \struct CX3DImporter_NodeElement_Shape
+/// This struct hold <Shape> value.
+struct CX3DImporter_NodeElement_Shape : public CX3DImporter_NodeElement
+{
+	/// \fn CX3DImporter_NodeElement_Shape(CX3DImporter_NodeElement_Shape* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_Shape(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_Shape, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_Shape
+
+/// \struct CX3DImporter_NodeElement_Appearance
+/// This struct hold <Appearance> value.
+struct CX3DImporter_NodeElement_Appearance : public CX3DImporter_NodeElement
+{
+	/// \fn CX3DImporter_NodeElement_Appearance(CX3DImporter_NodeElement_Appearance* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_Appearance(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_Appearance, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_Appearance
+
+/// \class CX3DImporter_NodeElement_Material
+/// Material.
+class CX3DImporter_NodeElement_Material : public CX3DImporter_NodeElement
+{
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+public:
+
+	float AmbientIntensity;///< Specifies how much ambient light from light sources this surface shall reflect.
+	aiColor3D DiffuseColor;///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source.
+	aiColor3D EmissiveColor;///< Models "glowing" objects. This can be useful for displaying pre-lit models.
+	float Shininess;///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights.
+	aiColor3D SpecularColor;///< The specularColor and shininess fields determine the specular highlights.
+	float Transparency;///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque.
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+private:
+
+	/// \fn CX3DImporter_NodeElement_Material(const CX3DImporter_NodeElement_Material& pNode)
+	/// Disabled copy constructor.
+	CX3DImporter_NodeElement_Material(const CX3DImporter_NodeElement_Material& pNode);
+
+	/// \fn CX3DImporter_NodeElement_Material& operator=(const CX3DImporter_NodeElement_Material& pNode)
+	/// Disabled assign operator.
+	CX3DImporter_NodeElement_Material& operator=(const CX3DImporter_NodeElement_Material& pNode);
+
+public:
+
+	/// \fn CX3DImporter_NodeElement_Material(const EType pType, CX3DImporter_NodeElement* pParent)
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pType - type of geometry object.
+	CX3DImporter_NodeElement_Material(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_Material, pParent)
+	{}
+
+};// class CX3DImporter_NodeElement_Material
+
+/// \struct CX3DImporter_NodeElement_ImageTexture
+/// This struct hold <ImageTexture> value.
+struct CX3DImporter_NodeElement_ImageTexture : public CX3DImporter_NodeElement
+{
+	/// \var RepeatS
+	/// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated
+	/// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are
+	/// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field.
+	bool RepeatS;
+	bool RepeatT;///< See \ref RepeatS.
+	std::string URL;///< URL of the texture.
+	/// \fn CX3DImporter_NodeElement_ImageTexture(CX3DImporter_NodeElement_ImageTexture* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_ImageTexture(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_ImageTexture, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_ImageTexture
+
+/// \struct CX3DImporter_NodeElement_TextureTransform
+/// This struct hold <TextureTransform> value.
+struct CX3DImporter_NodeElement_TextureTransform : public CX3DImporter_NodeElement
+{
+	aiVector2D Center;///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied.
+	float Rotation;///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied.
+	aiVector2D Scale;///< Specifies a scaling factor in S and T of the texture coordinates about the center point.
+	aiVector2D Translation;///<  Specifies a translation of the texture coordinates.
+
+	/// \fn CX3DImporter_NodeElement_TextureTransform(CX3DImporter_NodeElement_TextureTransform* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	CX3DImporter_NodeElement_TextureTransform(CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(ENET_TextureTransform, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_TextureTransform
+
+/// \struct CX3DImporter_NodeElement_Light
+/// This struct hold <TextureTransform> value.
+struct CX3DImporter_NodeElement_Light : public CX3DImporter_NodeElement
+{
+	float AmbientIntensity;///< Specifies the intensity of the ambient emission from the light.
+	aiColor3D Color;///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value.
+	aiVector3D Direction;///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system.
+	/// \var Global
+	/// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence.
+	/// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light.
+	bool Global;
+	float Intensity;///< Specifies the brightness of the direct emission from the light.
+	/// \var Attenuation
+	/// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor
+	/// is: "1 / max(attenuation[0] + attenuation[1] × r + attenuation[2] × r2, 1)", where r is the distance from the light to the surface being illuminated.
+	aiVector3D Attenuation;
+	aiVector3D Location;///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin.
+	float Radius;///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source.
+	float BeamWidth;///< Specifies an inner solid angle in which the light source emits light at uniform full intensity.
+	float CutOffAngle;///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle).
+
+	/// \fn CX3DImporter_NodeElement_Light(EType pLightType, CX3DImporter_NodeElement* pParent)
+	/// Constructor
+	/// \param [in] pParent - pointer to parent node.
+	/// \param [in] pLightType - type of the light source.
+	CX3DImporter_NodeElement_Light(EType pLightType, CX3DImporter_NodeElement* pParent)
+		: CX3DImporter_NodeElement(pLightType, pParent)
+	{}
+
+};// struct CX3DImporter_NodeElement_Light
+
+#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H

+ 772 - 0
code/X3DImporter_Postprocess.cpp

@@ -0,0 +1,772 @@
+/// \file X3DImporter_Postprocess.cpp
+/// \brief Convert built scenegraph and objects to Assimp scenegraph.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "StandardShapes.h"
+
+#include <boost/format.hpp>
+#include <string>
+
+#include <algorithm>
+#include <iterator>
+
+namespace Assimp
+{
+
+aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const
+{
+CX3DImporter_NodeElement* cur_node;
+std::list<aiMatrix4x4> matr;
+aiMatrix4x4 out_matr;
+
+	// starting walk from current element to root
+	cur_node = NodeElement_Cur;
+	if(cur_node != NULL)
+	{
+		do
+		{
+			// if cur_node is group then store group transformation matrix in list.
+			if(cur_node->Type == CX3DImporter_NodeElement::ENET_Group) matr.push_back(((CX3DImporter_NodeElement_Group*)cur_node)->Transformation);
+
+			cur_node = cur_node->Parent;
+		} while(cur_node != NULL);
+	}
+
+	// multiplicate all matrices in reverse order
+	for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); rit++) out_matr = out_matr * (*rit);
+
+	return out_matr;
+}
+
+void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const
+{
+	// walk thru childs and find for metadata.
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
+	{
+		if(((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) ||
+			((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) ||
+			((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaString))
+		{
+			pList.push_back(*el_it);
+		}
+		else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaSet)
+		{
+			PostprocessHelper_CollectMetadata(**el_it, pList);
+		}
+	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
+}
+
+bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const
+{
+	if((pType == CX3DImporter_NodeElement::ENET_MetaBoolean) || (pType == CX3DImporter_NodeElement::ENET_MetaDouble) ||
+		(pType == CX3DImporter_NodeElement::ENET_MetaFloat) || (pType == CX3DImporter_NodeElement::ENET_MetaInteger) ||
+		(pType == CX3DImporter_NodeElement::ENET_MetaString) || (pType == CX3DImporter_NodeElement::ENET_MetaSet))
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const
+{
+	if((pType == CX3DImporter_NodeElement::ENET_Arc2D) || (pType == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
+		(pType == CX3DImporter_NodeElement::ENET_Box) || (pType == CX3DImporter_NodeElement::ENET_Circle2D) ||
+		(pType == CX3DImporter_NodeElement::ENET_Cone) || (pType == CX3DImporter_NodeElement::ENET_Cylinder) ||
+		(pType == CX3DImporter_NodeElement::ENET_Disk2D) || (pType == CX3DImporter_NodeElement::ENET_ElevationGrid) ||
+		(pType == CX3DImporter_NodeElement::ENET_Extrusion) || (pType == CX3DImporter_NodeElement::ENET_IndexedFaceSet) ||
+		(pType == CX3DImporter_NodeElement::ENET_IndexedLineSet) || (pType == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
+		(pType == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || (pType == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet) ||
+		(pType == CX3DImporter_NodeElement::ENET_PointSet) || (pType == CX3DImporter_NodeElement::ENET_LineSet) ||
+		(pType == CX3DImporter_NodeElement::ENET_Polyline2D) || (pType == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
+		(pType == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pType == CX3DImporter_NodeElement::ENET_Sphere) ||
+		(pType == CX3DImporter_NodeElement::ENET_TriangleFanSet) || (pType == CX3DImporter_NodeElement::ENET_TriangleSet) ||
+		(pType == CX3DImporter_NodeElement::ENET_TriangleSet2D) || (pType == CX3DImporter_NodeElement::ENET_TriangleStripSet))
+	{
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeElement, std::list<aiLight*>& pSceneLightList) const
+{
+aiLight* new_light = new aiLight;
+const CX3DImporter_NodeElement_Light& ne = *((CX3DImporter_NodeElement_Light*)&pNodeElement);
+aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent();
+
+	new_light->mName = ne.ID;
+	new_light->mColorAmbient = ne.Color * ne.AmbientIntensity;
+	new_light->mColorDiffuse = ne.Color * ne.Intensity;
+	new_light->mColorSpecular = ne.Color * ne.Intensity;
+	switch(pNodeElement.Type)
+	{
+		case CX3DImporter_NodeElement::ENET_DirectionalLight:
+			new_light->mType = aiLightSource_DIRECTIONAL;
+			new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
+
+			break;
+		case CX3DImporter_NodeElement::ENET_PointLight:
+			new_light->mType = aiLightSource_POINT;
+			new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
+			new_light->mAttenuationConstant = ne.Attenuation.x;
+			new_light->mAttenuationLinear = ne.Attenuation.y;
+			new_light->mAttenuationQuadratic = ne.Attenuation.z;
+
+			break;
+		case CX3DImporter_NodeElement::ENET_SpotLight:
+			new_light->mType = aiLightSource_SPOT;
+			new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
+			new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
+			new_light->mAttenuationConstant = ne.Attenuation.x;
+			new_light->mAttenuationLinear = ne.Attenuation.y;
+			new_light->mAttenuationQuadratic = ne.Attenuation.z;
+			new_light->mAngleInnerCone = ne.BeamWidth;
+			new_light->mAngleOuterCone = ne.CutOffAngle;
+
+			break;
+		default:
+			throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildLight. Unknown type of light: %s") % pNodeElement.Type));
+	}
+
+	pSceneLightList.push_back(new_light);
+}
+
+void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNodeElement, aiMaterial** pMaterial) const
+{
+	// check argument
+	if(pMaterial == NULL) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is NULL.");
+	if(*pMaterial != NULL) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be NULL.");
+
+	*pMaterial = new aiMaterial;
+	aiMaterial& taimat = **pMaterial;// creating alias for convenience.
+
+	// at this point pNodeElement point to <Appearance> node. Walk thru childs and add all stored data.
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
+	{
+		if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
+		{
+			aiColor3D tcol3;
+			float tvalf;
+			CX3DImporter_NodeElement_Material& tnemat = *((CX3DImporter_NodeElement_Material*)*el_it);
+
+			tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity;
+			taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT);
+			taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE);
+			taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE);
+			taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR);
+			tvalf = 1;
+			taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH);
+			taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS);
+			tvalf = 1.0 - tnemat.Transparency;
+			taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY);
+		}// if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
+		else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
+		{
+			CX3DImporter_NodeElement_ImageTexture& tnetex = *((CX3DImporter_NodeElement_ImageTexture*)*el_it);
+			aiString url_str(tnetex.URL.c_str());
+			int mode = aiTextureOp_Multiply;
+
+			taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0));
+			taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+			taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+			taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
+		}// else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
+		else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
+		{
+			aiUVTransform trans;
+			CX3DImporter_NodeElement_TextureTransform& tnetextr = *((CX3DImporter_NodeElement_TextureTransform*)*el_it);
+
+			trans.mTranslation = tnetextr.Translation - tnetextr.Center;
+			trans.mScaling = tnetextr.Scale;
+			trans.mRotation = tnetextr.Rotation;
+			taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
+		}// else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
+	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
+}
+
+void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeElement, aiMesh** pMesh) const
+{
+	// check argument
+	if(pMesh == NULL) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is NULL.");
+	if(*pMesh != NULL) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be NULL.");
+
+	/************************************************************************************************************************************/
+	/************************************************************ Geometry2D ************************************************************/
+	/************************************************************************************************************************************/
+	if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
+		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) ||
+		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
+		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D))
+	{
+		CX3DImporter_NodeElement_Geometry2D& tnemesh = *((CX3DImporter_NodeElement_Geometry2D*)&pNodeElement);// create alias for convenience
+		std::vector<aiVector3D> tarr;
+
+		tarr.reserve(tnemesh.Vertices.size());
+		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); it++) tarr.push_back(*it);
+		*pMesh = StandardShapes::MakeMesh(tarr, tnemesh.NumIndices);// create mesh from vertices using Assimp help.
+
+		return;// mesh is build, nothing to do anymore.
+	}
+	/************************************************************************************************************************************/
+	/************************************************************ Geometry3D ************************************************************/
+	/************************************************************************************************************************************/
+	//
+	// Predefined figures
+	//
+	if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) ||
+		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere))
+	{
+		CX3DImporter_NodeElement_Geometry3D& tnemesh = *((CX3DImporter_NodeElement_Geometry3D*)&pNodeElement);// create alias for convenience
+		std::vector<aiVector3D> tarr;
+
+		tarr.reserve(tnemesh.Vertices.size());
+		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); it++) tarr.push_back(*it);
+
+		*pMesh = StandardShapes::MakeMesh(tarr, tnemesh.NumIndices);// create mesh from vertices using Assimp help.
+
+		return;// mesh is build, nothing to do anymore.
+	}
+	//
+	// Parametric figures
+	//
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
+	{
+		CX3DImporter_NodeElement_ElevationGrid& tnemesh = *((CX3DImporter_NodeElement_ElevationGrid*)&pNodeElement);// create alias for convenience
+
+		// at first create mesh from existing vertices.
+		*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices);
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
+				MeshGeometry_AddNormal(**pMesh,  ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value, tnemesh.NormalPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
+				MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of ElevationGrid: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
+	//
+	// Indexed primitives sets
+	//
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
+	{
+		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
+										tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
+				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
+										tnemesh.NormalPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
+				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
+
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
+	{
+		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
+										tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of IndexedLineSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
+
+	if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) ||
+		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
+		(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
+	{
+		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value,
+										tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
+				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
+										tnemesh.NormalPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
+				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \
+																	IndexedTriangleStripSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
+
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)
+	{
+		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
+
+		*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices);
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)
+
+	//
+	// Primitives sets
+	//
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
+	{
+		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				std::vector<aiVector3D> vec_copy;
+
+				vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
+				for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
+					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); it++)
+				{
+					vec_copy.push_back(*it);
+				}
+
+				*pMesh = StandardShapes::MakeMesh(vec_copy, 1);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, true);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of PointSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
+
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
+	{
+		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, true);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, true);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of LineSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
+
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
+	{
+		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value,tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
+				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
+										tnemesh.NormalPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
+				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
+
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
+	{
+		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				std::vector<aiVector3D> vec_copy;
+
+				vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
+				for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
+					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); it++)
+				{
+					vec_copy.push_back(*it);
+				}
+
+				*pMesh = StandardShapes::MakeMesh(vec_copy, 3);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
+				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
+										tnemesh.NormalPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
+				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of TrianlgeSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
+
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
+	{
+		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
+
+		// at first search for <Coordinate> node and create mesh.
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+			{
+				*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value);
+			}
+		}
+
+		// copy additional information from children
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		{
+			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
+				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA*)*ch_it)->Value, tnemesh.ColorPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
+				{} // skip because already read when mesh created.
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
+				MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal*)*ch_it)->Value,
+										tnemesh.NormalPerVertex);
+			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
+				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
+			else
+				throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown child of TriangleStripSet: %s.") % (*ch_it)->Type));
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+
+		return;// mesh is build, nothing to do anymore.
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
+
+	throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildMesh. Unknown mesh type: %s.") % pNodeElement.Type));
+}
+
+void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList,
+										std::list<aiMaterial*>& pSceneMaterialList, std::list<aiLight*>& pSceneLightList) const
+{
+std::list<CX3DImporter_NodeElement*>::const_iterator chit_begin = pNodeElement.Child.begin();
+std::list<CX3DImporter_NodeElement*>::const_iterator chit_end = pNodeElement.Child.end();
+std::list<aiNode*> SceneNode_Child;
+std::list<aiLight*> SceneNode_Light;
+std::list<unsigned int> SceneNode_Mesh;
+
+	// At first read all metadata
+	Postprocess_CollectMetadata(pNodeElement, pSceneNode);
+	// check if we have deal with grouping node. Which can contain transformation or switch
+	if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
+	{
+		const CX3DImporter_NodeElement_Group& tne_group = *((CX3DImporter_NodeElement_Group*)&pNodeElement);// create alias for convenience
+
+		pSceneNode.mTransformation = tne_group.Transformation;
+		if(tne_group.UseChoice)
+		{
+			// If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen.
+			if((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Child.size()))
+			{
+				chit_begin = pNodeElement.Child.end();
+				chit_end = pNodeElement.Child.end();
+			}
+			else
+			{
+				for(size_t i = 0; i < (size_t)tne_group.Choice; i++) chit_begin++;// forward iterator to choosed node.
+
+				chit_end = chit_begin;
+				chit_end++;// point end iterator to next element after choosed.
+			}
+		}// if(tne_group.UseChoice)
+	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
+
+	// Reserve memory for fast access and check children.
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
+	{// in this loop we do not read metadata because it's already read at begin.
+		if((*it)->Type == CX3DImporter_NodeElement::ENET_Group)
+		{
+			// if child is group then create new node and do recursive call.
+			aiNode* new_node = new aiNode;
+
+			new_node->mName = (*it)->ID;
+			new_node->mParent = &pSceneNode;
+			SceneNode_Child.push_back(new_node);
+			Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList);
+		}
+		else if((*it)->Type == CX3DImporter_NodeElement::ENET_Shape)
+		{
+			// shape can contain only one geometry and one appearance nodes.
+			Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape*)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList);
+		}
+		else if(((*it)->Type == CX3DImporter_NodeElement::ENET_DirectionalLight) || ((*it)->Type == CX3DImporter_NodeElement::ENET_PointLight) ||
+				((*it)->Type == CX3DImporter_NodeElement::ENET_SpotLight))
+		{
+			Postprocess_BuildLight(*((CX3DImporter_NodeElement_Light*)*it), pSceneLightList);
+		}
+		else if(!PostprocessHelper_ElementIsMetadata((*it)->Type))// skip metadata
+		{
+			throw DeadlyImportError(boost::str(boost::format("Postprocess_BuildNode. Unknown type: %s.") % (*it)->Type));
+		}
+	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
+
+	// copy data about children and meshes to aiNode.
+	if(SceneNode_Child.size() > 0)
+	{
+		std::list<aiNode*>::const_iterator it = SceneNode_Child.begin();
+
+		pSceneNode.mNumChildren = SceneNode_Child.size();
+		pSceneNode.mChildren = new aiNode*[pSceneNode.mNumChildren];
+		for(size_t i = 0; i < pSceneNode.mNumChildren; i++) pSceneNode.mChildren[i] = *it++;
+	}
+
+	if(SceneNode_Mesh.size() > 0)
+	{
+		std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin();
+
+		pSceneNode.mNumMeshes = SceneNode_Mesh.size();
+		pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
+		for(size_t i = 0; i < pSceneNode.mNumMeshes; i++) pSceneNode.mMeshes[i] = *it++;
+	}
+
+	// that's all. return to previous deals
+}
+
+void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& pShapeNodeElement, std::list<unsigned int>& pNodeMeshInd,
+							std::list<aiMesh*>& pSceneMeshList, std::list<aiMaterial*>& pSceneMaterialList) const
+{
+aiMaterial* tmat = NULL;
+aiMesh* tmesh = NULL;
+CX3DImporter_NodeElement::EType mesh_type = CX3DImporter_NodeElement::ENET_Invalid;
+unsigned int mat_ind = 0;
+
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)
+	{
+		if(PostprocessHelper_ElementIsMesh((*it)->Type))
+		{
+			Postprocess_BuildMesh(**it, &tmesh);
+			if(tmesh != NULL)
+			{
+				// if mesh successfully built then add data about it to arrays
+				pNodeMeshInd.push_back(pSceneMeshList.size());
+				pSceneMeshList.push_back(tmesh);
+				// keep mesh type. Need above for texture coordinate generation.
+				mesh_type = (*it)->Type;
+			}
+		}
+		else if((*it)->Type == CX3DImporter_NodeElement::ENET_Appearance)
+		{
+			Postprocess_BuildMaterial(**it, &tmat);
+			if(tmat != NULL)
+			{
+				// if material successfully built then add data about it to array
+				mat_ind = pSceneMaterialList.size();
+				pSceneMaterialList.push_back(tmat);
+			}
+		}
+	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)
+
+	// associate read material with read mesh.
+	if((tmesh != NULL) && (tmat != NULL))
+	{
+		tmesh->mMaterialIndex = mat_ind;
+		// Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates.
+		if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
+		{
+			int32_t tm;
+			aiVector3D tvec3;
+
+			switch(mesh_type)
+			{
+				case CX3DImporter_NodeElement::ENET_Box:
+					tm = aiTextureMapping_BOX;
+					break;
+				case CX3DImporter_NodeElement::ENET_Cone:
+				case CX3DImporter_NodeElement::ENET_Cylinder:
+					tm = aiTextureMapping_CYLINDER;
+					break;
+				case CX3DImporter_NodeElement::ENET_Sphere:
+					tm = aiTextureMapping_SPHERE;
+					break;
+				default:
+					tm = aiTextureMapping_PLANE;
+					break;
+			}// switch(mesh_type)
+
+			tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
+		}// if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
+	}// if((tmesh != NULL) && (tmat != NULL))
+}
+
+void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode) const
+{
+std::list<CX3DImporter_NodeElement*> meta_list;
+size_t meta_idx = 0;
+
+	PostprocessHelper_CollectMetadata(pNodeElement, meta_list);// find metadata in current node element.
+	if(meta_list.size() > 0)
+	{
+		if(pSceneNode.mMetaData != NULL) throw DeadlyImportError("Postprocess. MetaData member in node are not NULL. Something went wrong.");
+
+		// copy collected metadata to output node.
+		pSceneNode.mMetaData = new aiMetadata();
+		pSceneNode.mMetaData->mNumProperties = meta_list.size();
+		pSceneNode.mMetaData->mKeys = new aiString[pSceneNode.mMetaData->mNumProperties];
+		pSceneNode.mMetaData->mValues = new aiMetadataEntry[pSceneNode.mMetaData->mNumProperties];
+		for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++)
+		{
+			CX3DImporter_NodeElement_Meta* cur_meta = (CX3DImporter_NodeElement_Meta*)*it;
+
+			// due to limitations we can add only first element of value list.
+			// Add an element according to its type.
+			if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean)
+			{
+				if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
+					pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.begin()));
+			}
+			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble)
+			{
+				// at this case also converting double to float.
+				if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
+					pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble*)cur_meta)->Value.begin()));
+			}
+			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat)
+			{
+				if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
+					pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat*)cur_meta)->Value.begin()));
+			}
+			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger)
+			{
+				if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
+					pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger*)cur_meta)->Value.begin()));
+			}
+			else if((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString)
+			{
+				if(((CX3DImporter_NodeElement_MetaBoolean*)cur_meta)->Value.size() > 0)
+					pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, ((CX3DImporter_NodeElement_MetaString*)cur_meta)->Value.begin()->data());
+			}
+			else
+			{
+				throw DeadlyImportError("Postprocess. Unknown metadata type.");
+			}// if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else
+		}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++)
+	}// if(meta_list.size() > 0)
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 937 - 0
code/X3DImporter_Rendering.cpp

@@ -0,0 +1,937 @@
+/// \file X3DImporter_Rendering.cpp
+/// \brief Parsing data from nodes of "Rendering" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+// <Color
+// DEF=""           ID
+// USE=""           IDREF
+// color="" MFColor [inputOutput]
+// />
+void X3DImporter::ParseNode_Rendering_Color()
+{
+std::string use, def;
+std::list<aiColor3D> color;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsListCol3f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Color, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Color(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_Color*)ne)->Value = color;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Color");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <ColorRGBA
+// DEF=""               ID
+// USE=""               IDREF
+// color="" MFColorRGBA [inputOutput]
+// />
+void X3DImporter::ParseNode_Rendering_ColorRGBA()
+{
+std::string use, def;
+std::list<aiColor4D> color;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsListCol4f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_ColorRGBA, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_ColorRGBA(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_ColorRGBA*)ne)->Value = color;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "ColorRGBA");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Coordinate
+// DEF=""      ID
+// USE=""      IDREF
+// point=""    MFVec3f [inputOutput]
+// />
+void X3DImporter::ParseNode_Rendering_Coordinate()
+{
+std::string use, def;
+std::list<aiVector3D> point;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec3f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Coordinate, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Coordinate(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_Coordinate*)ne)->Value = point;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Coordinate");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <IndexedLineSet
+// DEF=""                ID
+// USE=""                IDREF
+// colorIndex=""         MFInt32 [initializeOnly]
+// colorPerVertex="true" SFBool  [initializeOnly]
+// coordIndex=""         MFInt32 [initializeOnly]
+// >
+//    <!-- ColorCoordinateContentModel -->
+// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
+// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedLineSet>
+void X3DImporter::ParseNode_Rendering_IndexedLineSet()
+{
+std::string use, def;
+std::list<int32_t> colorIndex;
+bool colorPerVertex = true;
+std::list<int32_t> coordIndex;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsListI32);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedLineSet, ne);
+	}
+	else
+	{
+		// check data
+		if((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3)))
+			throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedLineSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
+
+		ne_alias.ColorIndex = colorIndex;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.CoordIndex = coordIndex;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("IndexedLineSet");
+				// check for Color and Coordinate nodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedLineSet");
+
+			MACRO_NODECHECK_LOOPEND("IndexedLineSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <IndexedTriangleFanSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// index=""               MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedTriangleFanSet>
+void X3DImporter::ParseNode_Rendering_IndexedTriangleFanSet()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+std::list<int32_t> index;
+bool normalPerVertex = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedTriangleFanSet, ne);
+	}
+	else
+	{
+		// check data
+		if(index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.CoordIndex = index;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("IndexedTriangleFanSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedTriangleFanSet");
+
+			MACRO_NODECHECK_LOOPEND("IndexedTriangleFanSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <IndexedTriangleSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// index=""               MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedTriangleSet>
+void X3DImporter::ParseNode_Rendering_IndexedTriangleSet()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+std::list<int32_t> index;
+bool normalPerVertex = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedTriangleSet, ne);
+	}
+	else
+	{
+		// check data
+		if(index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedTriangleSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.CoordIndex = index;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("IndexedTriangleSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedTriangleSet");
+
+			MACRO_NODECHECK_LOOPEND("IndexedTriangleSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <IndexedTriangleStripSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// index=""               MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedTriangleStripSet>
+void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+std::list<int32_t> index;
+bool normalPerVertex = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedTriangleStripSet, ne);
+	}
+	else
+	{
+		// check data
+		if(index.size() == 0) throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.CoordIndex = index;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("IndexedTriangleStripSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedTriangleStripSet");
+
+			MACRO_NODECHECK_LOOPEND("IndexedTriangleStripSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <LineSet
+// DEF=""         ID
+// USE=""         IDREF
+// vertexCount="" MFInt32 [initializeOnly]
+// >
+//    <!-- ColorCoordinateContentModel -->
+// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
+// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </LineSet>
+void X3DImporter::ParseNode_Rendering_LineSet()
+{
+std::string use, def;
+std::list<int32_t> vertexCount;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("vertexCount", vertexCount, XML_ReadNode_GetAttrVal_AsListI32);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_LineSet, ne);
+	}
+	else
+	{
+		// check data
+		if(vertexCount.size() == 0) throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Set(CX3DImporter_NodeElement::ENET_LineSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
+
+		ne_alias.VertexCount = vertexCount;
+		// create CoordIdx
+		size_t coord_num = 0;
+
+		ne_alias.CoordIndex.clear();
+		for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+		{
+			if(*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two.");
+
+			for(int32_t i = 0; i < *vc_it; i++) ne_alias.CoordIndex.push_back(coord_num++);// add vertices indices
+
+			 ne_alias.CoordIndex.push_back(-1);// add face delimiter.
+		}
+
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("LineSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("LineSet");
+
+			MACRO_NODECHECK_LOOPEND("LineSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <PointSet
+// DEF="" ID
+// USE="" IDREF
+// >
+//    <!-- ColorCoordinateContentModel -->
+// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
+// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </PointSet>
+void X3DImporter::ParseNode_Rendering_PointSet()
+{
+std::string use, def;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointSet, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_PointSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("PointSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("PointSet");
+
+			MACRO_NODECHECK_LOOPEND("PointSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <TriangleFanSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// fanCount=""            MFInt32 [inputOutput]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </TriangleFanSet>
+void X3DImporter::ParseNode_Rendering_TriangleFanSet()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+std::list<int32_t> fanCount;
+bool normalPerVertex = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("fanCount", fanCount, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleFanSet, ne);
+	}
+	else
+	{
+		// check data
+		if(fanCount.size() == 0) throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Set(CX3DImporter_NodeElement::ENET_TriangleFanSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.VertexCount = fanCount;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+		// create CoordIdx
+		size_t coord_num_first, coord_num_prev;
+
+		ne_alias.CoordIndex.clear();
+		// assign indices for first triangle
+		coord_num_first = 0;
+		coord_num_prev = 1;
+		for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+		{
+			if(*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three.");
+
+			for(int32_t vc = 2; vc < *vc_it; vc++)
+			{
+				if(ccw)
+				{
+					// 2 1
+					//  0
+					ne_alias.CoordIndex.push_back(coord_num_first);// first vertex is a center and always is [0].
+					ne_alias.CoordIndex.push_back(coord_num_prev++);
+					ne_alias.CoordIndex.push_back(coord_num_prev);
+				}
+				else
+				{
+					// 1 2
+					//  0
+					ne_alias.CoordIndex.push_back(coord_num_first);// first vertex is a center and always is [0].
+					ne_alias.CoordIndex.push_back(coord_num_prev + 1);
+					ne_alias.CoordIndex.push_back(coord_num_prev++);
+				}// if(ccw) else
+
+				ne_alias.CoordIndex.push_back(-1);// add face delimiter.
+			}// for(int32_t vc = 2; vc < *vc_it; vc++)
+
+			coord_num_prev++;// that index will be center of next fan
+			coord_num_first = coord_num_prev++;// forward to next point - second point of fan
+		}// for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("TriangleFanSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("TriangleFanSet");
+
+			MACRO_NODECHECK_LOOPEND("TriangleFanSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <TriangleSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </TriangleSet>
+void X3DImporter::ParseNode_Rendering_TriangleSet()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+bool normalPerVertex = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_TriangleSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("TriangleSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("TriangleSet");
+
+			MACRO_NODECHECK_LOOPEND("TriangleSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <TriangleStripSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// stripCount=""          MFInt32 [inputOutput]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </TriangleStripSet>
+void X3DImporter::ParseNode_Rendering_TriangleStripSet()
+{
+std::string use, def;
+bool ccw = true;
+bool colorPerVertex = true;
+std::list<int32_t> stripCount;
+bool normalPerVertex = true;
+bool solid = true;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("stripCount", stripCount, XML_ReadNode_GetAttrVal_AsListI32);
+		MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleStripSet, ne);
+	}
+	else
+	{
+		// check data
+		if(stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Set(CX3DImporter_NodeElement::ENET_TriangleStripSet, NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		CX3DImporter_NodeElement_Set& ne_alias = *((CX3DImporter_NodeElement_Set*)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.VertexCount = stripCount;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+		// create CoordIdx
+		size_t coord_num0, coord_num1, coord_num2;// indices of current triangle
+		bool odd_tri;// sequence of current triangle
+		size_t coord_num_sb;// index of first point of strip
+
+		ne_alias.CoordIndex.clear();
+		coord_num_sb = 0;
+		for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+		{
+			if(*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three.");
+
+			// set initial values for first triangle
+			coord_num0 = coord_num_sb;
+			coord_num1 = coord_num_sb + 1;
+			coord_num2 = coord_num_sb + 2;
+			odd_tri = true;
+
+			for(int32_t vc = 2; vc < *vc_it; vc++)
+			{
+				if(ccw)
+				{
+					// 0 2
+					//  1
+					ne_alias.CoordIndex.push_back(coord_num0);
+					ne_alias.CoordIndex.push_back(coord_num1);
+					ne_alias.CoordIndex.push_back(coord_num2);
+				}
+				else
+				{
+					// 0 1
+					//  2
+					ne_alias.CoordIndex.push_back(coord_num0);
+					ne_alias.CoordIndex.push_back(coord_num2);
+					ne_alias.CoordIndex.push_back(coord_num1);
+				}// if(ccw) else
+
+				ne_alias.CoordIndex.push_back(-1);// add face delimiter.
+				// prepare values for next triangle
+				if(odd_tri)
+				{
+					coord_num0 = coord_num2;
+					coord_num2++;
+				}
+				else
+				{
+					coord_num1 = coord_num2;
+					coord_num2 = coord_num1 + 1;
+				}
+
+				odd_tri = !odd_tri;
+				coord_num_sb = coord_num2;// that index will be start of next strip
+			}// for(int32_t vc = 2; vc < *vc_it; vc++)
+		}// for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("TriangleStripSet");
+				// check for X3DComposedGeometryNodes
+				if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
+				if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
+				if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
+				if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
+				if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("TriangleStripSet");
+
+			MACRO_NODECHECK_LOOPEND("TriangleStripSet");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Normal
+// DEF=""      ID
+// USE=""      IDREF
+// vector=""   MFVec3f [inputOutput]
+// />
+void X3DImporter::ParseNode_Rendering_Normal()
+{
+std::string use, def;
+std::list<aiVector3D> vector;
+CX3DImporter_NodeElement* ne;
+LogInfo("TRACE: scene rendering Normal b");
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("vector", vector, XML_ReadNode_GetAttrVal_AsListVec3f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Normal, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Normal(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_Normal*)ne)->Value = vector;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Normal");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+LogInfo("TRACE: scene rendering Normal e");
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 209 - 0
code/X3DImporter_Shape.cpp

@@ -0,0 +1,209 @@
+/// \file X3DImporter_Shape.cpp
+/// \brief Parsing data from nodes of "Shape" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+// <Shape
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// >
+//    <!-- ShapeChildContentModel -->
+// "ShapeChildContentModel is the child-node content model corresponding to X3DShapeNode. ShapeChildContentModel can contain a single Appearance node and a
+// single geometry node, in any order.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model."
+// </Shape>
+// A Shape node is unlit if either of the following is true:
+//     The shape's appearance field is NULL (default).
+//     The material field in the Appearance node is NULL (default).
+// NOTE Geometry nodes that represent lines or points do not support lighting.
+void X3DImporter::ParseNode_Shape_Shape()
+{
+std::string use, def;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Shape, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Shape(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("Shape");
+				// check for appearance node
+				if(XML_CheckNode_NameEqual("Appearance")) { ParseNode_Shape_Appearance(); continue; }
+				// check for X3DGeometryNodes
+				if(XML_CheckNode_NameEqual("Arc2D")) { ParseNode_Geometry2D_Arc2D(); continue; }
+				if(XML_CheckNode_NameEqual("ArcClose2D")) { ParseNode_Geometry2D_ArcClose2D(); continue; }
+				if(XML_CheckNode_NameEqual("Circle2D")) { ParseNode_Geometry2D_Circle2D(); continue; }
+				if(XML_CheckNode_NameEqual("Disk2D")) { ParseNode_Geometry2D_Disk2D(); continue; }
+				if(XML_CheckNode_NameEqual("Polyline2D")) { ParseNode_Geometry2D_Polyline2D(); continue; }
+				if(XML_CheckNode_NameEqual("Polypoint2D")) { ParseNode_Geometry2D_Polypoint2D(); continue; }
+				if(XML_CheckNode_NameEqual("Rectangle2D")) { ParseNode_Geometry2D_Rectangle2D(); continue; }
+				if(XML_CheckNode_NameEqual("TriangleSet2D")) { ParseNode_Geometry2D_TriangleSet2D(); continue; }
+				if(XML_CheckNode_NameEqual("Box")) { ParseNode_Geometry3D_Box(); continue; }
+				if(XML_CheckNode_NameEqual("Cone")) { ParseNode_Geometry3D_Cone(); continue; }
+				if(XML_CheckNode_NameEqual("Cylinder")) { ParseNode_Geometry3D_Cylinder(); continue; }
+				if(XML_CheckNode_NameEqual("ElevationGrid")) { ParseNode_Geometry3D_ElevationGrid(); continue; }
+				if(XML_CheckNode_NameEqual("Extrusion")) { ParseNode_Geometry3D_Extrusion(); continue; }
+				if(XML_CheckNode_NameEqual("IndexedFaceSet")) { ParseNode_Geometry3D_IndexedFaceSet(); continue; }
+				if(XML_CheckNode_NameEqual("Sphere")) { ParseNode_Geometry3D_Sphere(); continue; }
+				if(XML_CheckNode_NameEqual("IndexedLineSet")) { ParseNode_Rendering_IndexedLineSet(); continue; }
+				if(XML_CheckNode_NameEqual("LineSet")) { ParseNode_Rendering_LineSet(); continue; }
+				if(XML_CheckNode_NameEqual("PointSet")) { ParseNode_Rendering_PointSet(); continue; }
+				if(XML_CheckNode_NameEqual("IndexedTriangleFanSet")) { ParseNode_Rendering_IndexedTriangleFanSet(); continue; }
+				if(XML_CheckNode_NameEqual("IndexedTriangleSet")) { ParseNode_Rendering_IndexedTriangleSet(); continue; }
+				if(XML_CheckNode_NameEqual("IndexedTriangleStripSet")) { ParseNode_Rendering_IndexedTriangleStripSet(); continue; }
+				if(XML_CheckNode_NameEqual("TriangleFanSet")) { ParseNode_Rendering_TriangleFanSet(); continue; }
+				if(XML_CheckNode_NameEqual("TriangleSet")) { ParseNode_Rendering_TriangleSet(); continue; }
+				if(XML_CheckNode_NameEqual("TriangleStripSet")) { ParseNode_Rendering_TriangleStripSet(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("Shape");
+
+			MACRO_NODECHECK_LOOPEND("Shape");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Appearance
+// DEF="" ID
+// USE="" IDREF
+// >
+// <!-- AppearanceChildContentModel -->
+// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and
+// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader,
+// PackagedShader, ProgramShader).
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model."
+// </Appearance>
+void X3DImporter::ParseNode_Shape_Appearance()
+{
+std::string use, def;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Appearance, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Appearance(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+        // check for child nodes
+        if(!mReader->isEmptyElement())
+        {
+			ParseHelper_Node_Enter(ne);
+			MACRO_NODECHECK_LOOPBEGIN("Appearance");
+				if(XML_CheckNode_NameEqual("Material")) { ParseNode_Shape_Material(); continue; }
+				if(XML_CheckNode_NameEqual("ImageTexture")) { ParseNode_Texturing_ImageTexture(); continue; }
+				if(XML_CheckNode_NameEqual("TextureTransform")) { ParseNode_Texturing_TextureTransform(); continue; }
+				// check for X3DMetadataObject
+				if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("Appearance");
+
+			MACRO_NODECHECK_LOOPEND("Appearance");
+			ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Material
+// DEF=""                     ID
+// USE=""                     IDREF
+// ambientIntensity="0.2"     SFFloat [inputOutput]
+// diffuseColor="0.8 0.8 0.8" SFColor [inputOutput]
+// emissiveColor="0 0 0"      SFColor [inputOutput]
+// shininess="0.2"            SFFloat [inputOutput]
+// specularColor="0 0 0"      SFColor [inputOutput]
+// transparency="0"           SFFloat [inputOutput]
+// />
+void X3DImporter::ParseNode_Shape_Material()
+{
+std::string use, def;
+float ambientIntensity = 0.2;
+float shininess = 0.2;
+float transparency = 0;
+aiColor3D diffuseColor(0.8, 0.8, 0.8);
+aiColor3D emissiveColor(0, 0, 0);
+aiColor3D specularColor(0, 0, 0);
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("shininess", shininess, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_RET("transparency", transparency, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("diffuseColor", diffuseColor, XML_ReadNode_GetAttrVal_AsCol3f);
+		MACRO_ATTRREAD_CHECK_REF("emissiveColor", emissiveColor, XML_ReadNode_GetAttrVal_AsCol3f);
+		MACRO_ATTRREAD_CHECK_REF("specularColor", specularColor, XML_ReadNode_GetAttrVal_AsCol3f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_Material, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_Material(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_Material*)ne)->AmbientIntensity = ambientIntensity;
+		((CX3DImporter_NodeElement_Material*)ne)->Shininess = shininess;
+		((CX3DImporter_NodeElement_Material*)ne)->Transparency = transparency;
+		((CX3DImporter_NodeElement_Material*)ne)->DiffuseColor = diffuseColor;
+		((CX3DImporter_NodeElement_Material*)ne)->EmissiveColor = emissiveColor;
+		((CX3DImporter_NodeElement_Material*)ne)->SpecularColor = specularColor;
+        // check for child nodes
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "Material");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 156 - 0
code/X3DImporter_Texturing.cpp

@@ -0,0 +1,156 @@
+/// \file X3DImporter_Texturing.cpp
+/// \brief Parsing data from nodes of "Texturing" set of X3D.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+// <ImageTexture
+// DEF=""         ID
+// USE=""         IDREF
+// repeatS="true" SFBool
+// repeatT="true" SFBool
+// url=""         MFString
+// />
+// When the url field contains no values ([]), texturing is disabled.
+void X3DImporter::ParseNode_Texturing_ImageTexture()
+{
+std::string use, def;
+bool repeatS = true;
+bool repeatT = true;
+std::list<std::string> url;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_RET("repeatS", repeatS, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_RET("repeatT", repeatT, XML_ReadNode_GetAttrVal_AsBool);
+		MACRO_ATTRREAD_CHECK_REF("url", url, XML_ReadNode_GetAttrVal_AsListS);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_ImageTexture, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_ImageTexture(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_ImageTexture*)ne)->RepeatS = repeatS;
+		((CX3DImporter_NodeElement_ImageTexture*)ne)->RepeatT = repeatT;
+		// Attribute "url" can contain list of strings. But we need only one - first.
+		if(url.size() > 0)
+			((CX3DImporter_NodeElement_ImageTexture*)ne)->URL = url.front();
+		else
+			((CX3DImporter_NodeElement_ImageTexture*)ne)->URL = "";
+
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "ImageTexture");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <TextureCoordinate
+// DEF=""      ID
+// USE=""      IDREF
+// point=""    MFVec3f [inputOutput]
+// />
+void X3DImporter::ParseNode_Texturing_TextureCoordinate()
+{
+std::string use, def;
+std::list<aiVector2D> point;
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_TextureCoordinate, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_TextureCoordinate(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_TextureCoordinate*)ne)->Value = point;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "TextureCoordinate");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <TextureTransform
+// DEF=""            ID
+// USE=""            IDREF
+// center="0 0"      SFVec2f [inputOutput]
+// rotation="0"      SFFloat [inputOutput]
+// scale="1 1"       SFVec2f [inputOutput]
+// translation="0 0" SFVec2f [inputOutput]
+// />
+void X3DImporter::ParseNode_Texturing_TextureTransform()
+{
+std::string use, def;
+aiVector2D center(0, 0);
+float rotation = 0;
+aiVector2D scale(1, 1);
+aiVector2D translation(0, 0);
+CX3DImporter_NodeElement* ne;
+
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
+		MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec2f);
+		MACRO_ATTRREAD_CHECK_RET("rotation", rotation, XML_ReadNode_GetAttrVal_AsFloat);
+		MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec2f);
+		MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec2f);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(def, use, ENET_TextureTransform, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new CX3DImporter_NodeElement_TextureTransform(NodeElement_Cur);
+		if(!def.empty()) ne->ID = def;
+
+		((CX3DImporter_NodeElement_TextureTransform*)ne)->Center = center;
+		((CX3DImporter_NodeElement_TextureTransform*)ne)->Rotation = rotation;
+		((CX3DImporter_NodeElement_TextureTransform*)ne)->Scale = scale;
+		((CX3DImporter_NodeElement_TextureTransform*)ne)->Translation = translation;
+		// check for X3DMetadataObject childs.
+		if(!mReader->isEmptyElement())
+			ParseNode_Metadata(ne, "TextureTransform");
+		else
+			NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER