Browse Source

Merge pull request #1021 from smalcom/export_x3d

Export x3d
Kim Kulling 9 năm trước cách đây
mục cha
commit
6f6dd69270

+ 2 - 0
code/CMakeLists.txt

@@ -611,6 +611,8 @@ ADD_ASSIMP_IMPORTER( X
 )
 
 ADD_ASSIMP_IMPORTER(X3D
+  X3DExporter.cpp
+  X3DExporter.hpp
   X3DImporter.cpp
   X3DImporter.hpp
   X3DImporter_Geometry2D.cpp

+ 5 - 0
code/Exporter.cpp

@@ -91,6 +91,7 @@ void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportPropert
 void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 
 // ------------------------------------------------------------------------------------------------
 // global array of all export formats which Assimp supports in its current build
@@ -151,6 +152,10 @@ Exporter::ExportFormatEntry gExporters[] =
 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
     Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0),
 #endif
+
+#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
+	Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0),
+#endif
 };
 
 #define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))

+ 2 - 2
code/SceneCombiner.cpp

@@ -1237,8 +1237,8 @@ void SceneCombiner::Copy (aiMetadata** _dest, const aiMetadata* src)
         case AI_BOOL:
             out.mData = new bool(*static_cast<bool*>(in.mData));
             break;
-        case AI_INT:
-            out.mData = new int(*static_cast<int*>(in.mData));
+        case AI_INT32:
+            out.mData = new int32_t(*static_cast<int32_t*>(in.mData));
             break;
         case AI_UINT64:
             out.mData = new uint64_t(*static_cast<uint64_t*>(in.mData));

+ 730 - 0
code/X3DExporter.cpp

@@ -0,0 +1,730 @@
+/// \file   X3DExporter.cpp
+/// \brief  X3D-format files exporter for Assimp. Implementation.
+/// \date   2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
+
+#include "X3DExporter.hpp"
+
+// Header files, Assimp.
+#include "Exceptional.h"
+#include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
+
+using namespace std;
+
+namespace Assimp
+{
+
+void ExportSceneX3D(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+{
+	X3DExporter exporter(pFile, pIOSystem, pScene, pProperties);
+}
+
+}// namespace Assimp
+
+namespace Assimp
+{
+
+void X3DExporter::IndentationStringSet(const size_t pNewLevel)
+{
+	if(pNewLevel > mIndentationString.size())
+	{
+		if(pNewLevel > mIndentationString.capacity()) mIndentationString.reserve(pNewLevel + 1);
+
+		for(size_t i = 0, i_e = pNewLevel - mIndentationString.size(); i < i_e; i++) mIndentationString.push_back('\t');
+	}
+	else if(pNewLevel < mIndentationString.size())
+	{
+		mIndentationString.resize(pNewLevel);
+	}
+}
+
+void X3DExporter::XML_Write(const string& pData)
+{
+	if(pData.size() == 0) return;
+	if(mOutFile->Write((void*)pData.data(), pData.length(), 1) != 1) throw DeadlyExportError("Failed to write scene data!");
+}
+
+aiMatrix4x4 X3DExporter::Matrix_GlobalToCurrent(const aiNode& pNode) const
+{
+aiNode* cur_node;
+std::list<aiMatrix4x4> matr;
+aiMatrix4x4 out_matr;
+
+	// starting walk from current element to root
+	matr.push_back(pNode.mTransformation);
+	cur_node = pNode.mParent;
+	if(cur_node != nullptr)
+	{
+		do
+		{
+			matr.push_back(cur_node->mTransformation);
+			cur_node = cur_node->mParent;
+		} while(cur_node != nullptr);
+	}
+
+	// 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 X3DExporter::AttrHelper_FloatToString(const float pValue, std::string& pTargetString)
+{
+	pTargetString = to_string(pValue);
+	AttrHelper_CommaToPoint(pTargetString);
+}
+
+void X3DExporter::AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, string& pTargetString)
+{
+	pTargetString.clear();
+	pTargetString.reserve(pArray_Size * 6);// (Number + space) * 3.
+	for(size_t idx = 0; idx < pArray_Size; idx++)
+		pTargetString.append(to_string(pArray[idx].x) + " " + to_string(pArray[idx].y) + " " + to_string(pArray[idx].z) + " ");
+
+	// remove last space symbol.
+	pTargetString.resize(pTargetString.length() - 1);
+	AttrHelper_CommaToPoint(pTargetString);
+}
+
+void X3DExporter::AttrHelper_Vec2DArrToString(const aiVector2D* pArray, const size_t pArray_Size, std::string& pTargetString)
+{
+	pTargetString.clear();
+	pTargetString.reserve(pArray_Size * 4);// (Number + space) * 2.
+	for(size_t idx = 0; idx < pArray_Size; idx++)
+		pTargetString.append(to_string(pArray[idx].x) + " " + to_string(pArray[idx].y) + " ");
+
+	// remove last space symbol.
+	pTargetString.resize(pTargetString.length() - 1);
+	AttrHelper_CommaToPoint(pTargetString);
+}
+
+void X3DExporter::AttrHelper_Vec3DAsVec2fArrToString(const aiVector3D* pArray, const size_t pArray_Size, string& pTargetString)
+{
+	pTargetString.clear();
+	pTargetString.reserve(pArray_Size * 4);// (Number + space) * 2.
+	for(size_t idx = 0; idx < pArray_Size; idx++)
+		pTargetString.append(to_string(pArray[idx].x) + " " + to_string(pArray[idx].y) + " ");
+
+	// remove last space symbol.
+	pTargetString.resize(pTargetString.length() - 1);
+	AttrHelper_CommaToPoint(pTargetString);
+}
+
+void X3DExporter::AttrHelper_Col4DArrToString(const aiColor4D* pArray, const size_t pArray_Size, string& pTargetString)
+{
+	pTargetString.clear();
+	pTargetString.reserve(pArray_Size * 8);// (Number + space) * 4.
+	for(size_t idx = 0; idx < pArray_Size; idx++)
+		pTargetString.append(to_string(pArray[idx].r) + " " + to_string(pArray[idx].g) + " " + to_string(pArray[idx].b) + " " +
+								to_string(pArray[idx].a) + " ");
+
+	// remove last space symbol.
+	pTargetString.resize(pTargetString.length() - 1);
+	AttrHelper_CommaToPoint(pTargetString);
+}
+
+void X3DExporter::AttrHelper_Col3DArrToString(const aiColor3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+{
+	pTargetString.clear();
+	pTargetString.reserve(pArray_Size * 6);// (Number + space) * 3.
+	for(size_t idx = 0; idx < pArray_Size; idx++)
+		pTargetString.append(to_string(pArray[idx].r) + " " + to_string(pArray[idx].g) + " " + to_string(pArray[idx].b) + " ");
+
+	// remove last space symbol.
+	pTargetString.resize(pTargetString.length() - 1);
+	AttrHelper_CommaToPoint(pTargetString);
+}
+
+void X3DExporter::AttrHelper_Color3ToAttrList(std::list<SAttribute> pList, const std::string& pName, const aiColor3D& pValue, const aiColor3D& pDefaultValue)
+{
+string tstr;
+
+	if(pValue == pDefaultValue) return;
+
+	AttrHelper_Col3DArrToString(&pValue, 1, tstr);
+	pList.push_back({pName, tstr});
+}
+
+void X3DExporter::AttrHelper_FloatToAttrList(std::list<SAttribute> pList, const string& pName, const float pValue, const float pDefaultValue)
+{
+string tstr;
+
+	if(pValue == pDefaultValue) return;
+
+	AttrHelper_FloatToString(pValue, tstr);
+	pList.push_back({pName, tstr});
+};
+
+void X3DExporter::NodeHelper_OpenNode(const string& pNodeName, const size_t pTabLevel, const bool pEmptyElement, const list<SAttribute>& pAttrList)
+{
+	// Write indentation.
+	IndentationStringSet(pTabLevel);
+	XML_Write(mIndentationString);
+	// Begin of the element
+	XML_Write("<" + pNodeName);
+	// Write attributes
+	for(const SAttribute& attr: pAttrList) { XML_Write(" " + attr.Name + "='" + attr.Value + "'"); }
+
+	// End of the element
+	if(pEmptyElement)
+	{
+		XML_Write("/>\n");
+	}
+	else
+	{
+		XML_Write(">\n");
+	}
+}
+
+void X3DExporter::NodeHelper_OpenNode(const string& pNodeName, const size_t pTabLevel, const bool pEmptyElement)
+{
+const list<SAttribute> attr_list;
+
+	NodeHelper_OpenNode(pNodeName, pTabLevel, pEmptyElement, attr_list);
+}
+
+void X3DExporter::NodeHelper_CloseNode(const string& pNodeName, const size_t pTabLevel)
+{
+	// Write indentation.
+	IndentationStringSet(pTabLevel);
+	XML_Write(mIndentationString);
+	// Write element
+	XML_Write("</" + pNodeName + ">\n");
+}
+
+void X3DExporter::Export_Node(const aiNode *pNode, const size_t pTabLevel)
+{
+bool transform = false;
+list<SAttribute> attr_list;
+
+	// In Assimp lights is stored in next way: light source store in mScene->mLights and in node tree must present aiNode with name same as
+	// light source has. Considering it we must compare every aiNode name with light sources names. Why not to look where ligths is present
+	// and save them to fili? Because corresponding aiNode can be already written to file and we can only add information to file not to edit.
+	if(CheckAndExport_Light(*pNode, pTabLevel)) return;
+
+	// Check if need DEF.
+	if(pNode->mName.length) attr_list.push_back({"DEF", pNode->mName.C_Str()});
+
+	// Check if need <Transformation> node against <Group>.
+	if(!pNode->mTransformation.IsIdentity())
+	{
+		auto Vector2String = [this](const aiVector3D pVector) -> string
+		{
+			string tstr = to_string(pVector.x) + " " + to_string(pVector.y) + " " + to_string(pVector.z);
+
+			AttrHelper_CommaToPoint(tstr);
+
+			return tstr;
+		};
+
+		auto Rotation2String = [this](const aiVector3D pAxis, const ai_real pAngle) -> string
+		{
+			string tstr = to_string(pAxis.x) + " " + to_string(pAxis.y) + " " + to_string(pAxis.z) + " " + to_string(pAngle);
+
+			AttrHelper_CommaToPoint(tstr);
+
+			return tstr;
+		};
+
+		aiVector3D scale, translate, rotate_axis;
+		ai_real rotate_angle;
+
+		transform = true;
+		pNode->mTransformation.Decompose(scale, rotate_axis, rotate_angle, translate);
+		// Check if values different from default
+		if((rotate_angle != 0) && (rotate_axis.Length() > 0))
+			attr_list.push_back({"rotation", Rotation2String(rotate_axis, rotate_angle)});
+
+		if(!scale.Equal({1, 1, 1})) attr_list.push_back({"scale", Vector2String(scale)});
+		if(translate.Length() > 0) attr_list.push_back({"translation", Vector2String(translate)});
+	}
+
+	// Begin node if need.
+	if(transform)
+		NodeHelper_OpenNode("Transform", pTabLevel, false, attr_list);
+	else
+		NodeHelper_OpenNode("Group", pTabLevel);
+
+	// Export metadata
+	if(pNode->mMetaData != nullptr)
+	{
+		for(size_t idx_prop = 0; idx_prop < pNode->mMetaData->mNumProperties; idx_prop++)
+		{
+			const aiString* key;
+			const aiMetadataEntry* entry;
+
+			if(pNode->mMetaData->Get(idx_prop, key, entry))
+			{
+				switch(entry->mType)
+				{
+					case AI_BOOL:
+						Export_MetadataBoolean(*key, *static_cast<bool*>(entry->mData), pTabLevel + 1);
+						break;
+					case AI_DOUBLE:
+						Export_MetadataDouble(*key, *static_cast<double*>(entry->mData), pTabLevel + 1);
+						break;
+					case AI_FLOAT:
+						Export_MetadataFloat(*key, *static_cast<float*>(entry->mData), pTabLevel + 1);
+						break;
+					case AI_INT32:
+						Export_MetadataInteger(*key, *static_cast<int32_t*>(entry->mData), pTabLevel + 1);
+						break;
+					case AI_AISTRING:
+						Export_MetadataString(*key, *static_cast<aiString*>(entry->mData), pTabLevel + 1);
+						break;
+					default:
+						LogError("Unsupported metadata type: " + to_string(entry->mType));
+						break;
+				}// switch(entry->mType)
+			}
+		}
+	}// if(pNode->mMetaData != nullptr)
+
+	// Export meshes.
+	for(size_t idx_mesh = 0; idx_mesh < pNode->mNumMeshes; idx_mesh++) Export_Mesh(pNode->mMeshes[idx_mesh], pTabLevel + 1);
+	// Export children.
+	for(size_t idx_node = 0; idx_node < pNode->mNumChildren; idx_node++) Export_Node(pNode->mChildren[idx_node], pTabLevel + 1);
+
+	// End node if need.
+	if(transform)
+		NodeHelper_CloseNode("Transform", pTabLevel);
+	else
+		NodeHelper_CloseNode("Group", pTabLevel);
+}
+
+void X3DExporter::Export_Mesh(const size_t pIdxMesh, const size_t pTabLevel)
+{
+const char* NodeName_IFS = "IndexedFaceSet";
+const char* NodeName_Shape = "Shape";
+
+list<SAttribute> attr_list;
+aiMesh& mesh = *mScene->mMeshes[pIdxMesh];// create alias for conveniance.
+
+	// Check if mesh already defined early.
+	if(mDEF_Map_Mesh.find(pIdxMesh) != mDEF_Map_Mesh.end())
+	{
+		// Mesh already defined, just refer to it
+		attr_list.push_back({"USE", mDEF_Map_Mesh.at(pIdxMesh)});
+		NodeHelper_OpenNode(NodeName_Shape, pTabLevel, true, attr_list);
+
+		return;
+	}
+
+	string mesh_name(mesh.mName.C_Str() + string("_IDX_") + to_string(pIdxMesh));// Create mesh name
+
+	// Define mesh name.
+	attr_list.push_back({"DEF", mesh_name});
+	mDEF_Map_Mesh[pIdxMesh] = mesh_name;
+
+	//
+	// "Shape" node.
+	//
+	NodeHelper_OpenNode(NodeName_Shape, pTabLevel, false, attr_list);
+	attr_list.clear();
+
+	//
+	// "Appearance" node.
+	//
+	Export_Material(mesh.mMaterialIndex, pTabLevel + 1);
+
+	//
+	// "IndexedFaceSet" node.
+	//
+	// Fill attributes which differ from default. In Assimp for colors, vertices and normals used one indices set. So, only "coordIndex" must be set.
+	string coordIndex;
+
+	// fill coordinates index.
+	coordIndex.reserve(mesh.mNumVertices * 4);// Index + space + Face delimiter
+	for(size_t idx_face = 0; idx_face < mesh.mNumFaces; idx_face++)
+	{
+		const aiFace& face_cur = mesh.mFaces[idx_face];
+
+		for(size_t idx_vert = 0; idx_vert < face_cur.mNumIndices; idx_vert++)
+		{
+			coordIndex.append(to_string(face_cur.mIndices[idx_vert]) + " ");
+		}
+
+		coordIndex.append("-1 ");// face delimiter.
+	}
+
+	// remove last space symbol.
+	coordIndex.resize(coordIndex.length() - 1);
+	attr_list.push_back({"coordIndex", coordIndex});
+	// create node
+	NodeHelper_OpenNode(NodeName_IFS, pTabLevel + 1, false, attr_list);
+	attr_list.clear();
+	// Child nodes for "IndexedFaceSet" needed when used colors, textures or normals.
+	string attr_value;
+
+	// Export <Coordinate>
+	AttrHelper_Vec3DArrToString(mesh.mVertices, mesh.mNumVertices, attr_value);
+	attr_list.push_back({"point", attr_value});
+	NodeHelper_OpenNode("Coordinate", pTabLevel + 2, true, attr_list);
+	attr_list.clear();
+
+	// Export <ColorRGBA>
+	if(mesh.HasVertexColors(0))
+	{
+		AttrHelper_Col4DArrToString(mesh.mColors[0], mesh.mNumVertices, attr_value);
+		attr_list.push_back({"color", attr_value});
+		NodeHelper_OpenNode("ColorRGBA", pTabLevel + 2, true, attr_list);
+		attr_list.clear();
+	}
+
+	// Export <TextureCoordinate>
+	if(mesh.HasTextureCoords(0))
+	{
+		AttrHelper_Vec3DAsVec2fArrToString(mesh.mTextureCoords[0], mesh.mNumVertices, attr_value);
+		attr_list.push_back({"point", attr_value});
+		NodeHelper_OpenNode("TextureCoordinate", pTabLevel + 2, true, attr_list);
+		attr_list.clear();
+	}
+
+	// Export <Normal>
+	if(mesh.HasNormals())
+	{
+		AttrHelper_Vec3DArrToString(mesh.mNormals, mesh.mNumVertices, attr_value);
+		attr_list.push_back({"vector", attr_value});
+		NodeHelper_OpenNode("Normal", pTabLevel + 2, true, attr_list);
+		attr_list.clear();
+	}
+
+	//
+	// Close opened nodes.
+	//
+	NodeHelper_CloseNode(NodeName_IFS, pTabLevel + 1);
+	NodeHelper_CloseNode(NodeName_Shape, pTabLevel);
+}
+
+void X3DExporter::Export_Material(const size_t pIdxMaterial, const size_t pTabLevel)
+{
+const char* NodeName_A = "Appearance";
+
+list<SAttribute> attr_list;
+aiMaterial& material = *mScene->mMaterials[pIdxMaterial];// create alias for conveniance.
+
+	// Check if material already defined early.
+	if(mDEF_Map_Material.find(pIdxMaterial) != mDEF_Map_Material.end())
+	{
+		// Material already defined, just refer to it
+		attr_list.push_back({"USE", mDEF_Map_Material.at(pIdxMaterial)});
+		NodeHelper_OpenNode(NodeName_A, pTabLevel, true, attr_list);
+
+		return;
+	}
+
+	string material_name(string("_IDX_") + to_string(pIdxMaterial));// Create material name
+	aiString ai_mat_name;
+
+	if(material.Get(AI_MATKEY_NAME, ai_mat_name) == AI_SUCCESS) material_name.insert(0, ai_mat_name.C_Str());
+
+	// Define material name.
+	attr_list.push_back({"DEF", material_name});
+	mDEF_Map_Material[pIdxMaterial] = material_name;
+
+	//
+	// "Appearance" node.
+	//
+	NodeHelper_OpenNode(NodeName_A, pTabLevel, false, attr_list);
+	attr_list.clear();
+
+	//
+	// "Material" node.
+	//
+	{
+		auto Color4ToAttrList = [&](const string& pAttrName, const aiColor4D& pAttrValue, const aiColor3D& pAttrDefaultValue)
+		{
+			string tstr;
+
+			if(aiColor3D(pAttrValue.r, pAttrValue.g, pAttrValue.b) != pAttrDefaultValue)
+			{
+				AttrHelper_Col4DArrToString(&pAttrValue, 1, tstr);
+				attr_list.push_back({pAttrName, tstr});
+			}
+		};
+
+		float tvalf;
+		aiColor3D color3;
+		aiColor4D color4;
+
+		// ambientIntensity="0.2"     SFFloat [inputOutput]
+		if(material.Get(AI_MATKEY_COLOR_AMBIENT, color3) == AI_SUCCESS)
+			AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", (color3.r + color3.g + color3.b) / 3.0f, 0.2f);
+		else if(material.Get(AI_MATKEY_COLOR_AMBIENT, color4) == AI_SUCCESS)
+			AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", (color4.r + color4.g + color4.b) / 3.0f, 0.2f);
+
+		// diffuseColor="0.8 0.8 0.8" SFColor [inputOutput]
+		if(material.Get(AI_MATKEY_COLOR_DIFFUSE, color3) == AI_SUCCESS)
+			AttrHelper_Color3ToAttrList(attr_list, "diffuseColor", color3, aiColor3D(0.8f, 0.8f, 0.8f));
+		else if(material.Get(AI_MATKEY_COLOR_DIFFUSE, color4) == AI_SUCCESS)
+			Color4ToAttrList("diffuseColor", color4, aiColor3D(0.8f, 0.8f, 0.8f));
+
+		// emissiveColor="0 0 0"      SFColor [inputOutput]
+		if(material.Get(AI_MATKEY_COLOR_EMISSIVE, color3) == AI_SUCCESS)
+			AttrHelper_Color3ToAttrList(attr_list, "emissiveColor", color3, aiColor3D(0, 0, 0));
+		else if(material.Get(AI_MATKEY_COLOR_EMISSIVE, color4) == AI_SUCCESS)
+			Color4ToAttrList("emissiveColor", color4, aiColor3D(0, 0, 0));
+
+		// shininess="0.2"            SFFloat [inputOutput]
+		if(material.Get(AI_MATKEY_SHININESS, tvalf) == AI_SUCCESS) AttrHelper_FloatToAttrList(attr_list, "shininess", tvalf, 0.2f);
+
+		// specularColor="0 0 0"      SFColor [inputOutput]
+		if(material.Get(AI_MATKEY_COLOR_SPECULAR, color3) == AI_SUCCESS)
+			AttrHelper_Color3ToAttrList(attr_list, "specularColor", color3, aiColor3D(0, 0, 0));
+		else if(material.Get(AI_MATKEY_COLOR_SPECULAR, color4) == AI_SUCCESS)
+			Color4ToAttrList("specularColor", color4, aiColor3D(0, 0, 0));
+
+		// transparency="0"           SFFloat [inputOutput]
+		if(material.Get(AI_MATKEY_OPACITY, tvalf) == AI_SUCCESS)
+		{
+			if(tvalf > 1) tvalf = 1;
+
+			tvalf = 1.0f - tvalf;
+			AttrHelper_FloatToAttrList(attr_list, "transparency", tvalf, 0);
+		}
+
+		NodeHelper_OpenNode("Material", pTabLevel + 1, true, attr_list);
+		attr_list.clear();
+	}// "Material" node. END.
+
+	//
+	// "ImageTexture" node.
+	//
+	{
+		auto RepeatToAttrList = [&](const string& pAttrName, const bool pAttrValue)
+		{
+			if(!pAttrValue) attr_list.push_back({pAttrName, "false"});
+		};
+
+		bool tvalb;
+		aiString tstring;
+
+		// url=""         MFString
+		if(material.Get(AI_MATKEY_TEXTURE_DIFFUSE(0), tstring) == AI_SUCCESS)
+		{
+			if(strncmp(tstring.C_Str(), AI_EMBEDDED_TEXNAME_PREFIX, strlen(AI_EMBEDDED_TEXNAME_PREFIX)) == 0)
+				LogError("Embedded texture is not supported");
+			else
+				attr_list.push_back({"url", string("\"") + tstring.C_Str() + "\""});
+		}
+
+		// repeatS="true" SFBool
+		if(material.Get(AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0), tvalb) == AI_SUCCESS) RepeatToAttrList("repeatS", tvalb);
+
+		// repeatT="true" SFBool
+		if(material.Get(AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0), tvalb) == AI_SUCCESS) RepeatToAttrList("repeatT", tvalb);
+
+		NodeHelper_OpenNode("ImageTexture", pTabLevel + 1, true, attr_list);
+		attr_list.clear();
+	}// "ImageTexture" node. END.
+
+	//
+	// "TextureTransform" node.
+	//
+	{
+		auto Vec2ToAttrList = [&](const string& pAttrName, const aiVector2D& pAttrValue, const aiVector2D& pAttrDefaultValue)
+		{
+			string tstr;
+
+			if(pAttrValue != pAttrDefaultValue)
+			{
+				AttrHelper_Vec2DArrToString(&pAttrValue, 1, tstr);
+				attr_list.push_back({pAttrName, tstr});
+			}
+		};
+
+		aiUVTransform transform;
+
+		if(material.Get(AI_MATKEY_UVTRANSFORM_DIFFUSE(0), transform) == AI_SUCCESS)
+		{
+			Vec2ToAttrList("translation", transform.mTranslation, aiVector2D(0, 0));
+			AttrHelper_FloatToAttrList(attr_list, "rotation", transform.mRotation, 0);
+			Vec2ToAttrList("scale", transform.mScaling, aiVector2D(1, 1));
+
+			NodeHelper_OpenNode("TextureTransform", pTabLevel + 1, true, attr_list);
+			attr_list.clear();
+		}
+	}// "TextureTransform" node. END.
+
+	//
+	// Close opened nodes.
+	//
+	NodeHelper_CloseNode(NodeName_A, pTabLevel);
+
+}
+
+void X3DExporter::Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
+{
+list<SAttribute> attr_list;
+
+	attr_list.push_back({"name", pKey.C_Str()});
+	attr_list.push_back({"value", pValue ? "true" : "false"});
+	NodeHelper_OpenNode("MetadataBoolean", pTabLevel, true, attr_list);
+}
+
+void X3DExporter::Export_MetadataDouble(const aiString& pKey, const double pValue, const size_t pTabLevel)
+{
+list<SAttribute> attr_list;
+
+	attr_list.push_back({"name", pKey.C_Str()});
+	attr_list.push_back({"value", to_string(pValue)});
+	NodeHelper_OpenNode("MetadataDouble", pTabLevel, true, attr_list);
+}
+
+void X3DExporter::Export_MetadataFloat(const aiString& pKey, const float pValue, const size_t pTabLevel)
+{
+list<SAttribute> attr_list;
+
+	attr_list.push_back({"name", pKey.C_Str()});
+	attr_list.push_back({"value", to_string(pValue)});
+	NodeHelper_OpenNode("MetadataFloat", pTabLevel, true, attr_list);
+}
+
+void X3DExporter::Export_MetadataInteger(const aiString& pKey, const int32_t pValue, const size_t pTabLevel)
+{
+list<SAttribute> attr_list;
+
+	attr_list.push_back({"name", pKey.C_Str()});
+	attr_list.push_back({"value", to_string(pValue)});
+	NodeHelper_OpenNode("MetadataInteger", pTabLevel, true, attr_list);
+}
+
+void X3DExporter::Export_MetadataString(const aiString& pKey, const aiString& pValue, const size_t pTabLevel)
+{
+list<SAttribute> attr_list;
+
+	attr_list.push_back({"name", pKey.C_Str()});
+	attr_list.push_back({"value", pValue.C_Str()});
+	NodeHelper_OpenNode("MetadataString", pTabLevel, true, attr_list);
+}
+
+bool X3DExporter::CheckAndExport_Light(const aiNode& pNode, const size_t pTabLevel)
+{
+list<SAttribute> attr_list;
+
+auto Vec3ToAttrList = [&](const string& pAttrName, const aiVector3D& pAttrValue, const aiVector3D& pAttrDefaultValue)
+{
+	string tstr;
+
+	if(pAttrValue != pAttrDefaultValue)
+	{
+		AttrHelper_Vec3DArrToString(&pAttrValue, 1, tstr);
+		attr_list.push_back({pAttrName, tstr});
+	}
+};
+
+size_t idx_light;
+bool found = false;
+
+	// Name of the light source can not be empty.
+	if(pNode.mName.length == 0) return false;
+
+	// search for light with name like node has.
+	for(idx_light = 0; mScene->mNumLights; idx_light++)
+	{
+		if(pNode.mName == mScene->mLights[idx_light]->mName)
+		{
+			found = true;
+			break;
+		}
+	}
+
+	if(!found) return false;
+
+	// Light source is found.
+	const aiLight& light = *mScene->mLights[idx_light];// Alias for conveniance.
+
+	aiMatrix4x4 trafo_mat = Matrix_GlobalToCurrent(pNode).Inverse();
+
+	attr_list.push_back({"DEF", light.mName.C_Str()});
+	attr_list.push_back({"global", "true"});// "false" is not supported.
+	// ambientIntensity="0" SFFloat [inputOutput]
+	AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", aiVector3D(light.mColorAmbient.r, light.mColorAmbient.g, light.mColorAmbient.b).Length(), 0);
+	// color="1 1 1"        SFColor [inputOutput]
+	AttrHelper_Color3ToAttrList(attr_list, "color", light.mColorDiffuse, aiColor3D(1, 1, 1));
+
+	switch(light.mType)
+	{
+		case aiLightSource_DIRECTIONAL:
+			{
+				aiVector3D direction = trafo_mat * light.mDirection;
+
+				Vec3ToAttrList("direction", direction, aiVector3D(0, 0, -1));
+				NodeHelper_OpenNode("DirectionalLight", pTabLevel, true, attr_list);
+			}
+
+			break;
+		case aiLightSource_POINT:
+			{
+				aiVector3D attenuation(light.mAttenuationConstant, light.mAttenuationLinear, light.mAttenuationQuadratic);
+				aiVector3D location = trafo_mat * light.mPosition;
+
+				Vec3ToAttrList("attenuation", attenuation, aiVector3D(1, 0, 0));
+				Vec3ToAttrList("location", location, aiVector3D(0, 0, 0));
+				NodeHelper_OpenNode("PointLight", pTabLevel, true, attr_list);
+			}
+
+			break;
+		case aiLightSource_SPOT:
+			{
+				aiVector3D attenuation(light.mAttenuationConstant, light.mAttenuationLinear, light.mAttenuationQuadratic);
+				aiVector3D location = trafo_mat * light.mPosition;
+				aiVector3D direction = trafo_mat * light.mDirection;
+
+				Vec3ToAttrList("attenuation", attenuation, aiVector3D(1, 0, 0));
+				Vec3ToAttrList("location", location, aiVector3D(0, 0, 0));
+				Vec3ToAttrList("direction", direction, aiVector3D(0, 0, -1));
+				AttrHelper_FloatToAttrList(attr_list, "beamWidth", light.mAngleInnerCone, 0.7854f);
+				AttrHelper_FloatToAttrList(attr_list, "cutOffAngle", light.mAngleOuterCone, 1.570796f);
+				NodeHelper_OpenNode("SpotLight", pTabLevel, true, attr_list);
+			}
+
+			break;
+		default:
+			throw DeadlyExportError("Unknown light type: " + to_string(light.mType));
+	}// switch(light.mType)
+
+	return true;
+}
+
+X3DExporter::X3DExporter(const char* pFileName, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+	: mScene(pScene)
+{
+list<SAttribute> attr_list;
+
+	mOutFile = pIOSystem->Open(pFileName, "wt");
+	if(mOutFile == nullptr) throw DeadlyExportError("Could not open output .x3d file: " + string(pFileName));
+
+	// Begin document
+	XML_Write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+	XML_Write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.3//EN\" \"http://www.web3d.org/specifications/x3d-3.3.dtd\">\n");
+	// Root node
+	attr_list.push_back({"profile", "Interchange"});
+	attr_list.push_back({"version", "3.3"});
+	attr_list.push_back({"xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance"});
+	attr_list.push_back({"xsd:noNamespaceSchemaLocation", "http://www.web3d.org/specifications/x3d-3.3.xsd"});
+	NodeHelper_OpenNode("X3D", 0, false, attr_list);
+	attr_list.clear();
+	// <head>: meta data.
+	NodeHelper_OpenNode("head", 1);
+	XML_Write(mIndentationString + "<!-- All \"meta\" from this section tou will found in <Scene> node as MetadataString nodes. -->\n");
+	NodeHelper_CloseNode("head", 1);
+	// Scene node.
+	NodeHelper_OpenNode("Scene", 1);
+	Export_Node(mScene->mRootNode, 2);
+	NodeHelper_CloseNode("Scene", 1);
+	// Close Root node.
+	NodeHelper_CloseNode("X3D", 0);
+	// Cleanup
+	pIOSystem->Close(mOutFile);
+	mOutFile = nullptr;
+}
+
+}// namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_X3D_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 235 - 0
code/X3DExporter.hpp

@@ -0,0 +1,235 @@
+/// \file   X3DExporter.hpp
+/// \brief  X3D-format files exporter for Assimp.
+/// \date   2016
+/// \author [email protected]
+// Thanks to acorn89 for support.
+
+#ifndef INCLUDED_AI_X3D_EXPORTER_H
+#define INCLUDED_AI_X3D_EXPORTER_H
+
+// Header files, Assimp.
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/material.h>
+#include <assimp/scene.h>
+
+// Header files, stdlib.
+#include <list>
+#include <string>
+
+namespace Assimp
+{
+
+/// \class X3DExporter
+/// Class which export aiScene to X3D file.
+///
+/// Limitations.
+///
+/// Pay attention that X3D is format for interactive graphic and simulations for web browsers. aiScene can not contain all features of the X3D format.
+/// Also, aiScene contain rasterized-like data. For example, X3D can describe circle all cylinder with one tag, but aiScene contain result of tesselation:
+/// vertices, faces etc. Yes, you can use algorithm for detecting figures or shapes, but thats not good idea at all.
+///
+/// Supported nodes:
+///		Core component:
+///			"MetadataBoolean", "MetadataDouble", "MetadataFloat", "MetadataInteger", "MetadataSet", "MetadataString"
+///		Geometry3D component:
+///			"IndexedFaceSet"
+///		Grouping component:
+///			"Group", "Transform"
+///		Lighting component:
+///			"DirectionalLight", "PointLight", "SpotLight"
+///		Rendering component:
+///			"ColorRGBA", "Coordinate", "Normal"
+///		Shape component:
+///			"Shape", "Appearance", "Material"
+///		Texturing component:
+///			"ImageTexture", "TextureCoordinate", "TextureTransform"
+///
+class X3DExporter
+{
+	/***********************************************/
+	/******************** Types ********************/
+	/***********************************************/
+
+	struct SAttribute
+	{
+		const std::string Name;
+		const std::string Value;
+	};
+
+	/***********************************************/
+	/****************** Constants ******************/
+	/***********************************************/
+
+	const aiScene* const mScene;
+
+	/***********************************************/
+	/****************** Variables ******************/
+	/***********************************************/
+
+	IOStream* mOutFile;
+	std::map<size_t, std::string> mDEF_Map_Mesh;
+	std::map<size_t, std::string> mDEF_Map_Material;
+
+private:
+
+	std::string mIndentationString;
+
+	/***********************************************/
+	/****************** Functions ******************/
+	/***********************************************/
+
+	/// \fn void IndentationStringSet(const size_t pNewLevel)
+	/// Set value of the indentation string.
+	/// \param [in] pNewLevel - new level of the indentation.
+	void IndentationStringSet(const size_t pNewLevel);
+
+	/// \fn void XML_Write(const std::string& pData)
+	/// Write data to XML-file.
+	/// \param [in] pData - reference to string which must be written.
+	void XML_Write(const std::string& pData);
+
+	/// \fn aiMatrix4x4 Matrix_GlobalToCurrent(const aiNode& pNode) const
+	/// Calculate transformation matrix for transformation from global coordinate system to pointed aiNode.
+	/// \param [in] pNode - reference to local node.
+	/// \return calculated matrix.
+	aiMatrix4x4 Matrix_GlobalToCurrent(const aiNode& pNode) const;
+
+	/// \fn void AttrHelper_CommaToPoint(std::string& pStringWithComma)
+	/// Convert commas in string to points. Thats need because "std::to_string" result depend on locale (regional settings).
+	/// \param [in, out] pStringWithComma - reference to string, which must be modified.
+	void AttrHelper_CommaToPoint(std::string& pStringWithComma) { for(char& c: pStringWithComma) { if(c == ',') c = '.'; } }
+
+	/// \fn void AttrHelper_FloatToString(const float pValue, std::string& pTargetString)
+	/// Converts float to string.
+	/// \param [in] pValue - value for converting.
+	/// \param [out] pTargetString - reference to string where result will be placed. Will be cleared before using.
+	void AttrHelper_FloatToString(const float pValue, std::string& pTargetString);
+
+	/// \fn void AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// Converts array of vectors to string.
+	/// \param [in] pArray - pointer to array of vectors.
+	/// \param [in] pArray_Size - count of elements in array.
+	/// \param [out] pTargetString - reference to string where result will be placed. Will be cleared before using.
+	void AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString);
+
+	/// \fn void AttrHelper_Vec2DArrToString(const aiVector2D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// \overload void AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	void AttrHelper_Vec2DArrToString(const aiVector2D* pArray, const size_t pArray_Size, std::string& pTargetString);
+
+	/// \fn void AttrHelper_Vec3DAsVec2fArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// \overload void AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// Only x, y is used from aiVector3D.
+	void AttrHelper_Vec3DAsVec2fArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString);
+
+	/// \fn void AttrHelper_Col4DArrToString(const aiColor4D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// \overload void AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// Converts array of colors to string.
+	void AttrHelper_Col4DArrToString(const aiColor4D* pArray, const size_t pArray_Size, std::string& pTargetString);
+
+	/// \fn void AttrHelper_Col3DArrToString(const aiColor3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// \overload void AttrHelper_Col4DArrToString(const aiColor4D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	/// Converts array of colors to string.
+	void AttrHelper_Col3DArrToString(const aiColor3D* pArray, const size_t pArray_Size, std::string& pTargetString);
+
+	/// \fn void AttrHelper_FloatToAttrList(std::list<SAttribute> pList, const std::string& pName, const float pValue, const float pDefaultValue)
+	/// \overload void AttrHelper_Col3DArrToString(const aiColor3D* pArray, const size_t pArray_Size, std::string& pTargetString)
+	void AttrHelper_FloatToAttrList(std::list<SAttribute> pList, const std::string& pName, const float pValue, const float pDefaultValue);
+
+	/// \fn void AttrHelper_Color3ToAttrList(std::list<SAttribute> pList, const std::string& pName, const aiColor3D& pValue, const aiColor3D& pDefaultValue)
+	/// Add attribute to list if value not equal to default.
+	/// \param [in] pList - target list of the attributes.
+	/// \param [in] pName - name of new attribute.
+	/// \param [in] pValue - value of the new attribute.
+	/// \param [in] pDefaultValue - default value for checking: if pValue is equal to pDefaultValue then attribute will not be added.
+	void AttrHelper_Color3ToAttrList(std::list<SAttribute> pList, const std::string& pName, const aiColor3D& pValue, const aiColor3D& pDefaultValue);
+
+	/// \fn void NodeHelper_OpenNode(const std::string& pNodeName, const size_t pTabLevel, const bool pEmptyElement, const std::list<SAttribute>& pAttrList)
+	/// Begin new XML-node element.
+	/// \param [in] pNodeName - name of the element.
+	/// \param [in] pTabLevel - indentation level.
+	/// \param [in] pEmtyElement - if true then empty element will be created.
+	/// \param [in] pAttrList - list of the attributes for element.
+	void NodeHelper_OpenNode(const std::string& pNodeName, const size_t pTabLevel, const bool pEmptyElement, const std::list<SAttribute>& pAttrList);
+
+	/// \fn void NodeHelper_OpenNode(const std::string& pNodeName, const size_t pTabLevel, const bool pEmptyElement = false)
+	/// \overload void NodeHelper_OpenNode(const std::string& pNodeName, const size_t pTabLevel, const bool pEmptyElement, const std::list<SAttribute>& pAttrList)
+	void NodeHelper_OpenNode(const std::string& pNodeName, const size_t pTabLevel, const bool pEmptyElement = false);
+
+	/// \fn void NodeHelper_CloseNode(const std::string& pNodeName, const size_t pTabLevel)
+	/// End XML-node element.
+	/// \param [in] pNodeName - name of the element.
+	/// \param [in] pTabLevel - indentation level.
+	void NodeHelper_CloseNode(const std::string& pNodeName, const size_t pTabLevel);
+
+	/// \fn void Export_Node(const aiNode* pNode, const size_t pTabLevel)
+	/// Export data from scene to XML-file: aiNode.
+	/// \param [in] pNode - source aiNode.
+	/// \param [in] pTabLevel - indentation level.
+	void Export_Node(const aiNode* pNode, const size_t pTabLevel);
+
+	/// \fn void Export_Mesh(const size_t pIdxMesh, const size_t pTabLevel)
+	/// Export data from scene to XML-file: aiMesh.
+	/// \param [in] pMesh - index of the source aiMesh.
+	/// \param [in] pTabLevel - indentation level.
+	void Export_Mesh(const size_t pIdxMesh, const size_t pTabLevel);
+
+	/// \fn void Export_Material(const size_t pIdxMaterial, const size_t pTabLevel)
+	/// Export data from scene to XML-file: aiMaterial.
+	/// \param [in] pIdxMaterial - index of the source aiMaterial.
+	/// \param [in] pTabLevel - indentation level.
+	void Export_Material(const size_t pIdxMaterial, const size_t pTabLevel);
+
+	/// \fn void Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
+	/// Export data from scene to XML-file: aiMetadata.
+	/// \param [in] pKey - source data: value of the metadata key.
+	/// \param [in] pValue - source data: value of the metadata value.
+	/// \param [in] pTabLevel - indentation level.
+	void Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel);
+
+	/// \fn void Export_MetadataDouble(const aiString& pKey, const double pValue, const size_t pTabLevel)
+	/// \overload void Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
+	void Export_MetadataDouble(const aiString& pKey, const double pValue, const size_t pTabLevel);
+
+	/// \fn void Export_MetadataFloat(const aiString& pKey, const float pValue, const size_t pTabLevel)
+	/// \overload void Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
+	void Export_MetadataFloat(const aiString& pKey, const float pValue, const size_t pTabLevel);
+
+	/// \fn void Export_MetadataInteger(const aiString& pKey, const int32_t pValue, const size_t pTabLevel)
+	/// \overload void Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
+	void Export_MetadataInteger(const aiString& pKey, const int32_t pValue, const size_t pTabLevel);
+
+	/// \fn void Export_MetadataString(const aiString& pKey, const aiString& pValue, const size_t pTabLevel)
+	/// \overload void Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
+	void Export_MetadataString(const aiString& pKey, const aiString& pValue, const size_t pTabLevel);
+
+	/// \fn bool CheckAndExport_Light(const aiNode& pNode, const size_t pTabLevel)
+	/// Check if node point to light source. If yes then export light source.
+	/// \param [in] pNode - reference to node for checking.
+	/// \param [in] pTabLevel - indentation level.
+	/// \return true - if node assigned with light and it was exported, else - return false.
+	bool CheckAndExport_Light(const aiNode& pNode, const size_t pTabLevel);
+
+	/***********************************************/
+	/************** Functions: LOG set *************/
+	/***********************************************/
+
+	/// \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); }
+
+public:
+
+	/// \fn X3DExporter()
+	/// Default constructor.
+	X3DExporter(const char* pFileName, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties);
+
+	/// \fn ~X3DExporter()
+	/// Default destructor.
+	~X3DExporter() {}
+
+};// class X3DExporter
+
+}// namespace Assimp
+
+#endif // INCLUDED_AI_X3D_EXPORTER_H

+ 0 - 4
code/X3DImporter.cpp

@@ -1418,7 +1418,6 @@ void X3DImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
 
 void X3DImporter::ParseNode_Root()
 {
-    LogInfo("ParseNode_Root b");
 	// search for root tag <X3D>
     if ( !XML_SearchNode( "X3D" ) )
     {
@@ -1427,7 +1426,6 @@ void X3DImporter::ParseNode_Root()
 
 	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 )
@@ -1442,11 +1440,9 @@ void X3DImporter::ParseNode_Root()
 		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()

+ 0 - 6
code/X3DImporter.hpp

@@ -397,12 +397,6 @@ private:
 	/// Short variant for calling \ref DefaultLogger::get()->info()
 	void LogInfo(const std::string& pMessage) { DefaultLogger::get()->info(pMessage); }
 
-	/// Short variant for calling \ref DefaultLogger::get()->warn()
-	void LogWarning(const std::string& pMessage) { DefaultLogger::get()->warn(pMessage); }
-
-	/// Short variant for calling \ref DefaultLogger::get()->error()
-	void LogError(const std::string& pMessage) { DefaultLogger::get()->error(pMessage); }
-
 	/***********************************************/
 	/************** Functions: XML set *************/
 	/***********************************************/

+ 9 - 6
code/X3DImporter_Postprocess.cpp

@@ -780,24 +780,27 @@ void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pN
 			}
 			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)
+				if(((CX3DImporter_NodeElement_MetaDouble*)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)
+				if(((CX3DImporter_NodeElement_MetaFloat*)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)
+				if(((CX3DImporter_NodeElement_MetaInteger*)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());
+				if(((CX3DImporter_NodeElement_MetaString*)cur_meta)->Value.size() > 0)
+				{
+					aiString tstr(((CX3DImporter_NodeElement_MetaString*)cur_meta)->Value.begin()->data());
+
+					pSceneNode.mMetaData->Set(meta_idx, cur_meta->Name, tstr);
+				}
 			}
 			else
 			{

+ 0 - 2
code/X3DImporter_Rendering.cpp

@@ -941,7 +941,6 @@ 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);
@@ -968,7 +967,6 @@ LogInfo("TRACE: scene rendering Normal b");
 
 		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

+ 29 - 0
include/assimp/matrix4x4.h

@@ -93,7 +93,15 @@ public:
 public:
 
     // array access operators
+	/** @fn TReal* operator[] (unsigned int p_iIndex)
+	 *  @param [in] p_iIndex - index of the row.
+	 *  @return pointer to pointed row.
+	 */
     TReal* operator[]       (unsigned int p_iIndex);
+
+	/** @fn const TReal* operator[] (unsigned int p_iIndex) const
+	 *  @overload TReal* operator[] (unsigned int p_iIndex)
+	 */
     const TReal* operator[] (unsigned int p_iIndex) const;
 
     // comparison operators
@@ -140,6 +148,27 @@ public:
     void Decompose (aiVector3t<TReal>& scaling, aiQuaterniont<TReal>& rotation,
         aiVector3t<TReal>& position) const;
 
+	// -------------------------------------------------------------------
+	/** @fn void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const
+     *  @brief Decompose a trafo matrix into its original components.
+     * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat
+     *  @param [out] pScaling - Receives the output scaling for the x,y,z axes.
+     *  @param [out] pRotation - Receives the output rotation as a Euler angles.
+     *  @param [out] pPosition - Receives the output position for the x,y,z axes.
+     */
+    void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const;
+
+	// -------------------------------------------------------------------
+	/** @fn void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, aiVector3t<TReal>& pPosition) const
+     *  @brief Decompose a trafo matrix into its original components
+	 * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat
+     *  @param [out] pScaling - Receives the output scaling for the x,y,z axes.
+     *  @param [out] pRotationAxis - Receives the output rotation axis.
+	 *  @param [out] pRotationAngle - Receives the output rotation angle for @ref pRotationAxis.
+     *  @param [out] pPosition - Receives the output position for the x,y,z axes.
+     */
+    void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, aiVector3t<TReal>& pPosition) const;
+
     // -------------------------------------------------------------------
     /** @brief Decompose a trafo matrix with no scaling into its
      *    original components

+ 113 - 44
include/assimp/matrix4x4.inl

@@ -299,57 +299,126 @@ inline bool aiMatrix4x4t<TReal>::Equal(const aiMatrix4x4t<TReal>& m, TReal epsil
 }
 
 // ----------------------------------------------------------------------------------------
-template <typename TReal>
-inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& scaling, aiQuaterniont<TReal>& rotation,
-    aiVector3t<TReal>& position) const
-{
-    const aiMatrix4x4t<TReal>& _this = *this;
 
-    // extract translation
-    position.x = _this[0][3];
-    position.y = _this[1][3];
-    position.z = _this[2][3];
+#define ASSIMP_MATRIX4_4_DECOMPOSE_PART		\
+	const aiMatrix4x4t<TReal>& _this = *this;/* Create alias for conveniance. */ \
+	\
+	/* extract translation */ \
+	pPosition.x = _this[0][3]; \
+	pPosition.y = _this[1][3]; \
+	pPosition.z = _this[2][3]; \
+	\
+	/* extract the columns of the matrix. */ \
+	aiVector3t<TReal> vCols[3] = { \
+		aiVector3t<TReal>(_this[0][0],_this[1][0],_this[2][0]), \
+		aiVector3t<TReal>(_this[0][1],_this[1][1],_this[2][1]), \
+		aiVector3t<TReal>(_this[0][2],_this[1][2],_this[2][2]) \
+	}; \
+	\
+	/* extract the scaling factors */ \
+	pScaling.x = vCols[0].Length(); \
+	pScaling.y = vCols[1].Length(); \
+	pScaling.z = vCols[2].Length(); \
+	\
+	/* and the sign of the scaling */ \
+	if (Determinant() < 0) pScaling = -pScaling; \
+	\
+	/* and remove all scaling from the matrix */ \
+	if(pScaling.x) vCols[0] /= pScaling.x; \
+	if(pScaling.y) vCols[1] /= pScaling.y; \
+	if(pScaling.z) vCols[2] /= pScaling.z; \
+	\
+	do {} while(false)
 
-    // extract the rows of the matrix
-    aiVector3t<TReal> vRows[3] = {
-        aiVector3t<TReal>(_this[0][0],_this[1][0],_this[2][0]),
-        aiVector3t<TReal>(_this[0][1],_this[1][1],_this[2][1]),
-        aiVector3t<TReal>(_this[0][2],_this[1][2],_this[2][2])
-    };
-
-    // extract the scaling factors
-    scaling.x = vRows[0].Length();
-    scaling.y = vRows[1].Length();
-    scaling.z = vRows[2].Length();
-
-    // and the sign of the scaling
-    if (Determinant() < 0) {
-        scaling.x = -scaling.x;
-        scaling.y = -scaling.y;
-        scaling.z = -scaling.z;
-    }
 
-    // and remove all scaling from the matrix
-    if(scaling.x)
-    {
-        vRows[0] /= scaling.x;
-    }
-    if(scaling.y)
-    {
-        vRows[1] /= scaling.y;
-    }
-    if(scaling.z)
-    {
-        vRows[2] /= scaling.z;
-    }
+
+
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& pScaling, aiQuaterniont<TReal>& pRotation,
+    aiVector3t<TReal>& pPosition) const
+{
+	ASSIMP_MATRIX4_4_DECOMPOSE_PART;
 
     // build a 3x3 rotation matrix
-    aiMatrix3x3t<TReal> m(vRows[0].x,vRows[1].x,vRows[2].x,
-        vRows[0].y,vRows[1].y,vRows[2].y,
-        vRows[0].z,vRows[1].z,vRows[2].z);
+    aiMatrix3x3t<TReal> m(vCols[0].x,vCols[1].x,vCols[2].x,
+        vCols[0].y,vCols[1].y,vCols[2].y,
+        vCols[0].z,vCols[1].z,vCols[2].z);
 
     // and generate the rotation quaternion from it
-    rotation = aiQuaterniont<TReal>(m);
+    pRotation = aiQuaterniont<TReal>(m);
+}
+
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const
+{
+	ASSIMP_MATRIX4_4_DECOMPOSE_PART;
+
+	/*
+	    |  CE     -CF      D   0 |
+	M = |  BDE+AF -BDF+AE -BC  0 |
+	    | -ADE+BF -ADF+BE  AC  0 |
+	    |  0       0       0   1 |
+
+	A = cos(angle_x), B = sin(angle_x);
+	C = cos(angle_y), D = sin(angle_y);
+	E = cos(angle_z), F = sin(angle_z);
+	*/
+
+	// Use a small epsilon to solve floating-point inaccuracies
+    const TReal epsilon = 10e-3f;
+
+	pRotation.y  = asin(vCols[2].x);// D. Angle around oY.
+
+	TReal C = cos(pRotation.y);
+
+	if(fabs(C) > epsilon)
+	{
+		// Finding angle around oX.
+		TReal tan_x =  vCols[2].z / C;// A
+		TReal tan_y = -vCols[2].y / C;// B
+
+		pRotation.x = atan2(tan_y, tan_x);
+		// Finding angle around oZ.
+		tan_x =  vCols[0].x / C;// E
+		tan_y = -vCols[1].x / C;// F
+		pRotation.z = atan2(tan_y, tan_x);
+	}
+	else
+	{// oY is fixed.
+		pRotation.x = 0;// Set angle around oX to 0. => A == 1, B == 0, C == 0, D == 1.
+
+		// And finding angle around oZ.
+		TReal tan_x = vCols[1].y;// -BDF+AE => E
+		TReal tan_y = vCols[0].y;//  BDE+AF => F
+
+		pRotation.z = atan2(tan_y, tan_x);
+	}
+}
+
+#undef ASSIMP_MATRIX4_4_DECOMPOSE_PART
+
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle,
+											aiVector3t<TReal>& pPosition) const
+{
+aiQuaterniont<TReal> pRotation;
+
+	Decompose(pScaling, pRotation, pPosition);
+	pRotation.Normalize();
+
+	TReal angle_cos = pRotation.w;
+	TReal angle_sin = sqrt(1.0f - angle_cos * angle_cos);
+
+	pRotationAngle = acos(angle_cos) * 2;
+
+	// Use a small epsilon to solve floating-point inaccuracies
+    const TReal epsilon = 10e-3f;
+
+	if(fabs(angle_sin) < epsilon) angle_sin = 1;
+
+	pRotationAxis.x = pRotation.x / angle_sin;
+	pRotationAxis.y = pRotation.y / angle_sin;
+	pRotationAxis.z = pRotation.z / angle_sin;
 }
 
 // ----------------------------------------------------------------------------------------

+ 25 - 9
include/assimp/metadata.h

@@ -64,12 +64,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  // -------------------------------------------------------------------------------
 typedef enum aiMetadataType
 {
-    AI_BOOL = 0,
-    AI_INT = 1,
-    AI_UINT64 = 2,
-    AI_FLOAT = 3,
-    AI_DOUBLE = 4,
-    AI_AISTRING = 5,
+    AI_BOOL       = 0,
+    AI_INT32      = 1,
+    AI_UINT64     = 2,
+    AI_FLOAT      = 3,
+    AI_DOUBLE     = 4,
+    AI_AISTRING   = 5,
     AI_AIVECTOR3D = 6,
 
 #ifndef SWIG
@@ -106,7 +106,7 @@ struct aiMetadataEntry
   */
  // -------------------------------------------------------------------------------
 inline aiMetadataType GetAiType( bool ) { return AI_BOOL; }
-inline aiMetadataType GetAiType( int ) { return AI_INT; }
+inline aiMetadataType GetAiType( int32_t ) { return AI_INT32; }
 inline aiMetadataType GetAiType( uint64_t ) { return AI_UINT64; }
 inline aiMetadataType GetAiType( float ) { return AI_FLOAT; }
 inline aiMetadataType GetAiType( double ) { return AI_DOUBLE; }
@@ -165,8 +165,8 @@ struct aiMetadata
                 case AI_BOOL:
                     delete static_cast<bool*>(data);
                     break;
-                case AI_INT:
-                    delete static_cast<int*>(data);
+                case AI_INT32:
+                    delete static_cast<int32_t*>(data);
                     break;
                 case AI_UINT64:
                     delete static_cast<uint64_t*>(data);
@@ -248,6 +248,22 @@ struct aiMetadata
         return Get(aiString(key), value);
     }
 
+	/// \fn inline bool Get(size_t pIndex, const aiString*& pKey, const aiMetadataEntry*& pEntry)
+	/// Return metadata entry for analyzing it by user.
+	/// \param [in] pIndex - index of the entry.
+	/// \param [out] pKey - pointer to the key value.
+	/// \param [out] pEntry - pointer to the entry: type and value.
+	/// \return false - if pIndex is out of range, else - true.
+	inline bool Get(size_t pIndex, const aiString*& pKey, const aiMetadataEntry*& pEntry)
+	{
+		if(pIndex >= mNumProperties) return false;
+
+		pKey = &mKeys[pIndex];
+		pEntry = &mValues[pIndex];
+
+		return true;
+	}
+
 #endif // __cplusplus
 
 };

+ 2 - 2
include/assimp/texture.h

@@ -155,8 +155,8 @@ struct aiTexture
      * absent color channel and just use 0 for bitness. For example:
      * 1. Image contain RGBA and 8 bit per channel, achFormatHint == "rgba8888";
      * 2. Image contain ARGB and 8 bit per channel, achFormatHint == "argb8888";
-     * 2. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, achFormatHint == "rgba5650";
-     * 3. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010";
+     * 3. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, achFormatHint == "rgba5650";
+     * 4. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010";
      * If mHeight == 0 then achFormatHint is set set to '\\0\\0\\0\\0' if the loader has no additional
      * information about the texture file format used OR the
      * file extension of the format without a trailing dot. If there