diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index d71a1893d..10979df5c 100644 --- a/code/CMakeLists.txt +++ b/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 diff --git a/code/Exporter.cpp b/code/Exporter.cpp index f6d1316fe..26cb5e998 100644 --- a/code/Exporter.cpp +++ b/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])) diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 68c595b26..e167a3600 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -1237,8 +1237,8 @@ void SceneCombiner::Copy (aiMetadata** _dest, const aiMetadata* src) case AI_BOOL: out.mData = new bool(*static_cast(in.mData)); break; - case AI_INT: - out.mData = new int(*static_cast(in.mData)); + case AI_INT32: + out.mData = new int32_t(*static_cast(in.mData)); break; case AI_UINT64: out.mData = new uint64_t(*static_cast(in.mData)); diff --git a/code/X3DExporter.cpp b/code/X3DExporter.cpp new file mode 100644 index 000000000..05128880f --- /dev/null +++ b/code/X3DExporter.cpp @@ -0,0 +1,730 @@ +/// \file X3DExporter.cpp +/// \brief X3D-format files exporter for Assimp. Implementation. +/// \date 2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER + +#include "X3DExporter.hpp" + +// Header files, Assimp. +#include "Exceptional.h" +#include +#include + +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 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::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 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 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& 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 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("\n"); +} + +void X3DExporter::Export_Node(const aiNode *pNode, const size_t pTabLevel) +{ +bool transform = false; +list 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 node against . + 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(entry->mData), pTabLevel + 1); + break; + case AI_DOUBLE: + Export_MetadataDouble(*key, *static_cast(entry->mData), pTabLevel + 1); + break; + case AI_FLOAT: + Export_MetadataFloat(*key, *static_cast(entry->mData), pTabLevel + 1); + break; + case AI_INT32: + Export_MetadataInteger(*key, *static_cast(entry->mData), pTabLevel + 1); + break; + case AI_AISTRING: + Export_MetadataString(*key, *static_cast(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 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 + 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 + 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 + 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 + 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 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 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 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 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 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 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 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 attr_list; + + mOutFile = pIOSystem->Open(pFileName, "wt"); + if(mOutFile == nullptr) throw DeadlyExportError("Could not open output .x3d file: " + string(pFileName)); + + // Begin document + XML_Write("\n"); + XML_Write("\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(); + // : meta data. + NodeHelper_OpenNode("head", 1); + XML_Write(mIndentationString + "\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 diff --git a/code/X3DExporter.hpp b/code/X3DExporter.hpp new file mode 100644 index 000000000..20aec951b --- /dev/null +++ b/code/X3DExporter.hpp @@ -0,0 +1,235 @@ +/// \file X3DExporter.hpp +/// \brief X3D-format files exporter for Assimp. +/// \date 2016 +/// \author smal.root@gmail.com +// Thanks to acorn89 for support. + +#ifndef INCLUDED_AI_X3D_EXPORTER_H +#define INCLUDED_AI_X3D_EXPORTER_H + +// Header files, Assimp. +#include +#include +#include +#include + +// Header files, stdlib. +#include +#include + +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 mDEF_Map_Mesh; + std::map 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 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 pList, const std::string& pName, const float pValue, const float pDefaultValue); + + /// \fn void AttrHelper_Color3ToAttrList(std::list 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 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& 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& 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& 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 diff --git a/code/X3DImporter.cpp b/code/X3DImporter.cpp index dd53bd314..024eedb28 100644 --- a/code/X3DImporter.cpp +++ b/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 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() diff --git a/code/X3DImporter.hpp b/code/X3DImporter.hpp index bd18a1fd7..f19fc0195 100644 --- a/code/X3DImporter.hpp +++ b/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 *************/ /***********************************************/ diff --git a/code/X3DImporter_Postprocess.cpp b/code/X3DImporter_Postprocess.cpp index 2b2a2595b..37acb0fe8 100644 --- a/code/X3DImporter_Postprocess.cpp +++ b/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 { diff --git a/code/X3DImporter_Rendering.cpp b/code/X3DImporter_Rendering.cpp index daafa0611..ce0d7b607 100644 --- a/code/X3DImporter_Rendering.cpp +++ b/code/X3DImporter_Rendering.cpp @@ -941,7 +941,6 @@ void X3DImporter::ParseNode_Rendering_Normal() std::string use, def; std::list 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 diff --git a/include/assimp/matrix4x4.h b/include/assimp/matrix4x4.h index b32ac7376..3f7eb3adf 100644 --- a/include/assimp/matrix4x4.h +++ b/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& scaling, aiQuaterniont& rotation, aiVector3t& position) const; + // ------------------------------------------------------------------- + /** @fn void Decompose(aiVector3t& pScaling, aiVector3t& pRotation, aiVector3t& 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& pScaling, aiVector3t& pRotation, aiVector3t& pPosition) const; + + // ------------------------------------------------------------------- + /** @fn void Decompose(aiVector3t& pScaling, aiVector3t& pRotationAxis, TReal& pRotationAngle, aiVector3t& 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& pScaling, aiVector3t& pRotationAxis, TReal& pRotationAngle, aiVector3t& pPosition) const; + // ------------------------------------------------------------------- /** @brief Decompose a trafo matrix with no scaling into its * original components diff --git a/include/assimp/matrix4x4.inl b/include/assimp/matrix4x4.inl index 2ccd557d8..2f579906e 100644 --- a/include/assimp/matrix4x4.inl +++ b/include/assimp/matrix4x4.inl @@ -299,57 +299,126 @@ inline bool aiMatrix4x4t::Equal(const aiMatrix4x4t& m, TReal epsil } // ---------------------------------------------------------------------------------------- + +#define ASSIMP_MATRIX4_4_DECOMPOSE_PART \ + const aiMatrix4x4t& _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 vCols[3] = { \ + aiVector3t(_this[0][0],_this[1][0],_this[2][0]), \ + aiVector3t(_this[0][1],_this[1][1],_this[2][1]), \ + aiVector3t(_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) + + + + template -inline void aiMatrix4x4t::Decompose (aiVector3t& scaling, aiQuaterniont& rotation, - aiVector3t& position) const +inline void aiMatrix4x4t::Decompose (aiVector3t& pScaling, aiQuaterniont& pRotation, + aiVector3t& pPosition) const { - const aiMatrix4x4t& _this = *this; - - // extract translation - position.x = _this[0][3]; - position.y = _this[1][3]; - position.z = _this[2][3]; - - // extract the rows of the matrix - aiVector3t vRows[3] = { - aiVector3t(_this[0][0],_this[1][0],_this[2][0]), - aiVector3t(_this[0][1],_this[1][1],_this[2][1]), - aiVector3t(_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; - } + ASSIMP_MATRIX4_4_DECOMPOSE_PART; // build a 3x3 rotation matrix - aiMatrix3x3t 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 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(m); + pRotation = aiQuaterniont(m); +} + +template +inline void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3t& pRotation, aiVector3t& 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 +inline void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3t& pRotationAxis, TReal& pRotationAngle, + aiVector3t& pPosition) const +{ +aiQuaterniont 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; } // ---------------------------------------------------------------------------------------- diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 258bf329c..068bbbe5e 100644 --- a/include/assimp/metadata.h +++ b/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(data); break; - case AI_INT: - delete static_cast(data); + case AI_INT32: + delete static_cast(data); break; case AI_UINT64: delete static_cast(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 }; diff --git a/include/assimp/texture.h b/include/assimp/texture.h index b445fd83b..ab52c79a2 100644 --- a/include/assimp/texture.h +++ b/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