Merge branch 'master' into new_obj_stream_handling

This commit is contained in:
Kim Kulling
2016-10-10 10:19:45 +02:00
37 changed files with 8510 additions and 119 deletions

View File

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

View File

@@ -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]))

5658
code/Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -182,10 +182,15 @@ void ObjFileParser::parseFile()
case 'm': // Parse a material library or merging group ('mg')
{
if (*(m_DataIt + 1) == 'g')
std::string name;
getName(m_DataIt, m_DataItEnd, name);
if (name == "mg")
getGroupNumberAndResolution();
else
else if(name == "mtllib")
getMaterialLib();
else
goto pf_skip_line;
}
break;
@@ -209,6 +214,8 @@ void ObjFileParser::parseFile()
default:
{
pf_skip_line:
m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
}
break;
@@ -625,6 +632,10 @@ void ObjFileParser::getMaterialLib()
// Check for existence
const std::string strMatName(pStart, &(*m_DataIt));
std::string absName;
// Check if directive is valid.
if(!strMatName.length()) throw DeadlyImportError("File name of the material is absent.");
if ( m_pIO->StackSize() > 0 ) {
std::string path = m_pIO->CurrentDirectory();
if ( '/' != *path.rbegin() ) {

View File

@@ -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
code/X3DExporter.cpp Normal file
View File

@@ -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 <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
code/X3DExporter.hpp Normal file
View File

@@ -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 <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

View File

@@ -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()

View File

@@ -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 *************/
/***********************************************/

View File

@@ -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
{

View File

@@ -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

View File

@@ -128,6 +128,7 @@ namespace glTF
struct BufferView; // here due to cross-reference
struct Texture;
struct Light;
struct Skin;
// Vec/matrix types, as raw float arrays
@@ -455,25 +456,6 @@ namespace glTF
void Read(Value& obj, Asset& r);
};
struct Animation : public Object
{
struct Channel
{
};
struct Target
{
};
struct Sampler
{
};
};
//! A buffer points to binary geometry, animation, or skins.
struct Buffer : public Object
{
@@ -825,6 +807,10 @@ namespace glTF
Ref<Camera> camera;
Ref<Light> light;
std::vector< Ref<Node> > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy.
Ref<Skin> skin; //!< The ID of the skin referenced by this node.
std::string jointName; //!< Name used when this node is a joint in a skin.
Node() {}
void Read(Value& obj, Asset& r);
};
@@ -864,6 +850,11 @@ namespace glTF
struct Skin : public Object
{
Nullable<mat4> bindShapeMatrix; //!< Floating-point 4x4 transformation matrix stored in column-major order.
Ref<Accessor> inverseBindMatrices; //!< The ID of the accessor containing the floating-point 4x4 inverse-bind matrices.
std::vector<std::string/*Ref<Node>*/> jointNames; //!< Joint names of the joints (nodes with a jointName property) in this skin.
std::string name; //!< The user-defined name of this object.
Skin() {}
void Read(Value& obj, Asset& r);
};
@@ -934,6 +925,44 @@ namespace glTF
void SetDefaults();
};
struct Animation : public Object
{
struct AnimSampler {
std::string id; //!< The ID of this sampler.
std::string input; //!< The ID of a parameter in this animation to use as key-frame input.
std::string interpolation; //!< Type of interpolation algorithm to use between key-frames.
std::string output; //!< The ID of a parameter in this animation to use as key-frame output.
};
struct AnimChannel {
std::string sampler; //!< The ID of one sampler present in the containing animation's samplers property.
struct AnimTarget {
Ref<Node> id; //!< The ID of the node to animate.
std::string path; //!< The name of property of the node to animate ("translation", "rotation", or "scale").
} target;
};
struct AnimParameters {
Ref<Accessor> TIME; //!< Accessor reference to a buffer storing a array of floating point scalar values.
Ref<Accessor> rotation; //!< Accessor reference to a buffer storing a array of four-component floating-point vectors.
Ref<Accessor> scale; //!< Accessor reference to a buffer storing a array of three-component floating-point vectors.
Ref<Accessor> translation; //!< Accessor reference to a buffer storing a array of three-component floating-point vectors.
};
// AnimChannel Channels[3]; //!< Connect the output values of the key-frame animation to a specific node in the hierarchy.
// AnimParameters Parameters; //!< The samplers that interpolate between the key-frames.
// AnimSampler Samplers[3]; //!< The parameterized inputs representing the key-frame data.
std::vector<AnimChannel> Channels; //!< Connect the output values of the key-frame animation to a specific node in the hierarchy.
AnimParameters Parameters; //!< The samplers that interpolate between the key-frames.
std::vector<AnimSampler> Samplers; //!< The parameterized inputs representing the key-frame data.
Animation() {}
void Read(Value& obj, Asset& r);
};
//! Base class for LazyDict that acts as an interface
class LazyDictBase
{
@@ -966,7 +995,7 @@ namespace glTF
typedef typename std::gltf_unordered_map< std::string, unsigned int > Dict;
std::vector<T*> mObjs; //! The read objects
Dict mObjsById; //! The read objects accesible by id
Dict mObjsById; //! The read objects accessible by id
const char* mDictId; //! ID of the dictionary object
const char* mExtId; //! ID of the extension defining the dictionary
Value* mDict; //! JSON dictionary object
@@ -986,7 +1015,7 @@ namespace glTF
Ref<T> Get(const char* id);
Ref<T> Get(unsigned int i);
Ref<T> Get(const std::string& pID) { return Get(pID.c_str()); }
Ref<T> Get(const std::string& pID) { return Get(pID.c_str()); }
Ref<T> Create(const char* id);
Ref<T> Create(const std::string& id)
@@ -1084,7 +1113,7 @@ namespace glTF
LazyDict<Sampler> samplers;
LazyDict<Scene> scenes;
//LazyDict<Shader> shaders;
//LazyDict<Skin> skins;
LazyDict<Skin> skins;
//LazyDict<Technique> techniques;
LazyDict<Texture> textures;
@@ -1109,7 +1138,7 @@ namespace glTF
, samplers (*this, "samplers")
, scenes (*this, "scenes")
//, shaders (*this, "shaders")
//, skins (*this, "skins")
, skins (*this, "skins")
//, techniques (*this, "techniques")
, textures (*this, "textures")
, lights (*this, "lights", "KHR_materials_common")

View File

@@ -102,7 +102,65 @@ namespace glTF {
inline void Write(Value& obj, Animation& a, AssetWriter& w)
{
/****************** Channels *******************/
Value channels;
channels.SetArray();
channels.Reserve(unsigned(a.Channels.size()), w.mAl);
for (size_t i = 0; i < unsigned(a.Channels.size()); ++i) {
Animation::AnimChannel& c = a.Channels[i];
Value valChannel;
valChannel.SetObject();
{
valChannel.AddMember("sampler", c.sampler, w.mAl);
Value valTarget;
valTarget.SetObject();
{
valTarget.AddMember("id", StringRef(c.target.id->id), w.mAl);
valTarget.AddMember("path", c.target.path, w.mAl);
}
valChannel.AddMember("target", valTarget, w.mAl);
}
channels.PushBack(valChannel, w.mAl);
}
obj.AddMember("channels", channels, w.mAl);
/****************** Parameters *******************/
Value valParameters;
valParameters.SetObject();
{
if (a.Parameters.TIME) {
valParameters.AddMember("TIME", StringRef(a.Parameters.TIME->id), w.mAl);
}
if (a.Parameters.rotation) {
valParameters.AddMember("rotation", StringRef(a.Parameters.rotation->id), w.mAl);
}
if (a.Parameters.scale) {
valParameters.AddMember("scale", StringRef(a.Parameters.scale->id), w.mAl);
}
if (a.Parameters.translation) {
valParameters.AddMember("translation", StringRef(a.Parameters.translation->id), w.mAl);
}
}
obj.AddMember("parameters", valParameters, w.mAl);
/****************** Samplers *******************/
Value valSamplers;
valSamplers.SetObject();
for (size_t i = 0; i < unsigned(a.Samplers.size()); ++i) {
Animation::AnimSampler& s = a.Samplers[i];
Value valSampler;
valSampler.SetObject();
{
valSampler.AddMember("input", s.input, w.mAl);
valSampler.AddMember("interpolation", s.interpolation, w.mAl);
valSampler.AddMember("output", s.output, w.mAl);
}
valSamplers.AddMember(StringRef(s.id), valSampler, w.mAl);
}
obj.AddMember("samplers", valSamplers, w.mAl);
}
inline void Write(Value& obj, Buffer& b, AssetWriter& w)
@@ -327,6 +385,16 @@ namespace glTF {
AddRefsVector(obj, "children", n.children, w.mAl);
AddRefsVector(obj, "meshes", n.meshes, w.mAl);
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
if (n.skin) {
obj.AddMember("skin", Value(n.skin->id, w.mAl).Move(), w.mAl);
}
if (!n.jointName.empty()) {
obj.AddMember("jointName", n.jointName, w.mAl);
}
}
inline void Write(Value& obj, Program& b, AssetWriter& w)
@@ -362,6 +430,24 @@ namespace glTF {
inline void Write(Value& obj, Skin& b, AssetWriter& w)
{
/****************** jointNames *******************/
Value vJointNames;
vJointNames.SetArray();
vJointNames.Reserve(unsigned(b.jointNames.size()), w.mAl);
for (size_t i = 0; i < unsigned(b.jointNames.size()); ++i) {
vJointNames.PushBack(StringRef(b.jointNames[i]), w.mAl);
}
obj.AddMember("jointNames", vJointNames, w.mAl);
if (b.bindShapeMatrix.isPresent) {
Value val;
obj.AddMember("bindShapeMatrix", MakeValue(val, b.bindShapeMatrix.value, w.mAl).Move(), w.mAl);
}
if (b.inverseBindMatrices) {
obj.AddMember("inverseBindMatrices", Value(b.inverseBindMatrices->id, w.mAl).Move(), w.mAl);
}
}

View File

@@ -80,7 +80,6 @@ namespace Assimp {
// Worker function for exporting a scene to GLTF. Prototyped and registered in Exporter.cpp
void ExportSceneGLTF(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
{
// invoke the exporter
glTFExporter exporter(pFile, pIOSystem, pScene, pProperties, false);
}
@@ -126,26 +125,24 @@ glTFExporter::glTFExporter(const char* filename, IOSystem* pIOSystem, const aiSc
ExportMetadata();
//for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {}
//for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {}
//for (unsigned int i = 0; i < pScene->mNumLights; ++i) {}
ExportMaterials();
ExportMeshes();
//for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {}
if (mScene->mRootNode) {
ExportNode(mScene->mRootNode);
}
ExportMeshes();
//for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {}
ExportScene();
ExportAnimations();
glTF::AssetWriter writer(*mAsset);
if (isBinary) {
@@ -164,6 +161,14 @@ static void CopyValue(const aiMatrix4x4& v, glTF::mat4& o)
o[12] = v.a4; o[13] = v.b4; o[14] = v.c4; o[15] = v.d4;
}
static void IdentityMatrix4(glTF::mat4& o)
{
o[ 0] = 1; o[ 1] = 0; o[ 2] = 0; o[ 3] = 0;
o[ 4] = 0; o[ 5] = 1; o[ 6] = 0; o[ 7] = 0;
o[ 8] = 0; o[ 9] = 0; o[10] = 1; o[11] = 0;
o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1;
}
inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
unsigned int count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
{
@@ -359,6 +364,78 @@ void glTFExporter::ExportMaterials()
}
}
void ExportSkin(Asset& mAsset, const aiMesh* aim, Ref<Mesh>& meshRef, Ref<Buffer>& bufferRef)
{
std::string skinName = aim->mName.C_Str();
skinName = mAsset.FindUniqueID(skinName, "skin");
Ref<Skin> skinRef = mAsset.skins.Create(skinName);
skinRef->name = skinName;
mat4* inverseBindMatricesData = new mat4[aim->mNumBones];
//-------------------------------------------------------
// Store the vertex joint and weight data.
vec4* vertexJointData = new vec4[aim->mNumVertices];
vec4* vertexWeightData = new vec4[aim->mNumVertices];
unsigned int* jointsPerVertex = new unsigned int[aim->mNumVertices];
for (size_t i = 0; i < aim->mNumVertices; ++i) {
jointsPerVertex[i] = 0;
for (size_t j = 0; j < 4; ++j) {
vertexJointData[i][j] = 0;
vertexWeightData[i][j] = 0;
}
}
for (unsigned int idx_bone = 0; idx_bone < aim->mNumBones; ++idx_bone) {
const aiBone* aib = aim->mBones[idx_bone];
// aib->mName =====> skinRef->jointNames
// Find the node with id = mName.
Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str());
nodeRef->jointName = "joint_" + std::to_string(idx_bone);
skinRef->jointNames.push_back("joint_" + std::to_string(idx_bone));
// Identity Matrix =====> skinRef->bindShapeMatrix
// Temporary. Hard-coded identity matrix here
skinRef->bindShapeMatrix.isPresent = true;
IdentityMatrix4(skinRef->bindShapeMatrix.value);
// aib->mOffsetMatrix =====> skinRef->inverseBindMatrices
CopyValue(aib->mOffsetMatrix, inverseBindMatricesData[idx_bone]);
// aib->mWeights =====> vertexWeightData
for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) {
aiVertexWeight tmpVertWeight = aib->mWeights[idx_weights];
vertexJointData[tmpVertWeight.mVertexId][jointsPerVertex[tmpVertWeight.mVertexId]] = idx_bone;
vertexWeightData[tmpVertWeight.mVertexId][jointsPerVertex[tmpVertWeight.mVertexId]] = tmpVertWeight.mWeight;
jointsPerVertex[tmpVertWeight.mVertexId] += 1;
}
} // End: for-loop mNumMeshes
// Create the Accessor for skinRef->inverseBindMatrices
Ref<Accessor> invBindMatrixAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumBones, inverseBindMatricesData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT);
if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor;
Mesh::Primitive& p = meshRef->primitives.back();
Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumVertices, vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexJointAccessor) p.attributes.joint.push_back(vertexJointAccessor);
Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinName, bufferRef, aim->mNumVertices, vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexWeightAccessor) p.attributes.weight.push_back(vertexWeightAccessor);
// Create the skinned mesh instance node.
Ref<Node> node = mAsset.nodes.Create(mAsset.FindUniqueID(skinName, "node"));
// Ref<Node> node = mAsset.nodes.Get(aim->mBones[0]->mName.C_Str());
node->meshes.push_back(meshRef);
node->name = node->id;
node->skeletons.push_back(mAsset.nodes.Get(aim->mBones[0]->mName.C_Str()));
node->skin = skinRef;
}
void glTFExporter::ExportMeshes()
{
// Not for
@@ -369,7 +446,7 @@ void glTFExporter::ExportMeshes()
typedef unsigned short IndicesType;
// Variables needed for compression. BEGIN.
// Indices, not pointers - because pointer to buffer is changin while writing to it.
// Indices, not pointers - because pointer to buffer is changing while writing to it.
size_t idx_srcdata_begin;// Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals.
std::vector<size_t> idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer.
@@ -480,6 +557,12 @@ void glTFExporter::ExportMeshes()
p.mode = PrimitiveMode_TRIANGLES;
}
/*************** Skins ****************/
///TODO: Fix skinning animation
// if(aim->HasBones()) {
// ExportSkin(*mAsset, aim, m, b);
// }
/****************** Compression ******************/
///TODO: animation: weights, joints.
if(comp_allow)
@@ -571,7 +654,7 @@ void glTFExporter::ExportMeshes()
m->Extension.push_back(ext);
#endif
}// if(comp_allow)
}// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) {
}// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i)
}
unsigned int glTFExporter::ExportNode(const aiNode* n)
@@ -622,10 +705,134 @@ void glTFExporter::ExportMetadata()
asset.generator = buffer;
}
inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref<Animation>& animRef, Ref<Buffer>& buffer, const aiNodeAnim* nodeChannel)
{
// Loop over the data and check to see if it exactly matches an existing buffer.
// If yes, then reference the existing corresponding accessor.
// Otherwise, add to the buffer and create a new accessor.
//-------------------------------------------------------
// Extract TIME parameter data.
// Check if the timeStamps are the same for mPositionKeys, mRotationKeys, and mScalingKeys.
if(nodeChannel->mNumPositionKeys > 0) {
typedef float TimeType;
std::vector<TimeType> timeData;
timeData.resize(nodeChannel->mNumPositionKeys);
for (size_t i = 0; i < nodeChannel->mNumPositionKeys; ++i) {
timeData[i] = nodeChannel->mPositionKeys[i].mTime; // Check if we have to cast type here. e.g. uint16_t()
}
Ref<Accessor> timeAccessor = ExportData(mAsset, animId, buffer, nodeChannel->mNumPositionKeys, &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT);
if (timeAccessor) animRef->Parameters.TIME = timeAccessor;
}
//-------------------------------------------------------
// Extract translation parameter data
if(nodeChannel->mNumPositionKeys > 0) {
C_STRUCT aiVector3D* translationData = new aiVector3D[nodeChannel->mNumPositionKeys];
for (size_t i = 0; i < nodeChannel->mNumPositionKeys; ++i) {
translationData[i] = nodeChannel->mPositionKeys[i].mValue;
}
Ref<Accessor> tranAccessor = ExportData(mAsset, animId, buffer, nodeChannel->mNumPositionKeys, translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
if (tranAccessor) animRef->Parameters.translation = tranAccessor;
}
//-------------------------------------------------------
// Extract scale parameter data
if(nodeChannel->mNumScalingKeys > 0) {
C_STRUCT aiVector3D* scaleData = new aiVector3D[nodeChannel->mNumScalingKeys];
for (size_t i = 0; i < nodeChannel->mNumScalingKeys; ++i) {
scaleData[i] = nodeChannel->mScalingKeys[i].mValue;
}
Ref<Accessor> scaleAccessor = ExportData(mAsset, animId, buffer, nodeChannel->mNumScalingKeys, scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
if (scaleAccessor) animRef->Parameters.scale = scaleAccessor;
}
//-------------------------------------------------------
// Extract rotation parameter data
if(nodeChannel->mNumRotationKeys > 0) {
C_STRUCT aiQuaternion* rotationData = new aiQuaternion[nodeChannel->mNumRotationKeys];
for (size_t i = 0; i < nodeChannel->mNumRotationKeys; ++i) {
rotationData[i] = nodeChannel->mRotationKeys[i].mValue;
}
Ref<Accessor> rotAccessor = ExportData(mAsset, animId, buffer, nodeChannel->mNumRotationKeys, rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (rotAccessor) animRef->Parameters.rotation = rotAccessor;
}
}
void glTFExporter::ExportAnimations()
{
Ref<Buffer> bufferRef = mAsset->buffers.Get(unsigned (0));
for (unsigned int i = 0; i < mScene->mNumAnimations; ++i) {
const aiAnimation* anim = mScene->mAnimations[i];
std::string nameAnim = "anim";
if (anim->mName.length > 0) {
nameAnim = anim->mName.C_Str();
}
for (unsigned int channelIndex = 0; channelIndex < anim->mNumChannels; ++channelIndex) {
const aiNodeAnim* nodeChannel = anim->mChannels[channelIndex];
// It appears that assimp stores this type of animation as multiple animations.
// where each aiNodeAnim in mChannels animates a specific node.
std::string name = nameAnim + "_" + std::to_string(channelIndex);
name = mAsset->FindUniqueID(name, "animation");
Ref<Animation> animRef = mAsset->animations.Create(name);
/******************* Parameters ********************/
ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel);
for (unsigned int j = 0; j < 3; ++j) {
std::string channelType;
int channelSize;
switch (j) {
case 0:
channelType = "rotation";
channelSize = nodeChannel->mNumRotationKeys;
break;
case 1:
channelType = "scale";
channelSize = nodeChannel->mNumScalingKeys;
break;
case 2:
channelType = "translation";
channelSize = nodeChannel->mNumPositionKeys;
break;
}
if (channelSize < 1) { continue; }
Animation::AnimChannel tmpAnimChannel;
Animation::AnimSampler tmpAnimSampler;
tmpAnimChannel.sampler = name + "_" + channelType;
tmpAnimChannel.target.path = channelType;
tmpAnimSampler.output = channelType;
tmpAnimSampler.id = name + "_" + channelType;
tmpAnimChannel.target.id = mAsset->nodes.Get(nodeChannel->mNodeName.C_Str());
tmpAnimSampler.input = "TIME";
tmpAnimSampler.interpolation = "LINEAR";
animRef->Channels.push_back(tmpAnimChannel);
animRef->Samplers.push_back(tmpAnimSampler);
}
}
// Assimp documentation staes this is not used (not implemented)
// for (unsigned int channelIndex = 0; channelIndex < anim->mNumMeshChannels; ++channelIndex) {
// const aiMeshAnim* meshChannel = anim->mMeshChannels[channelIndex];
// }
} // End: for-loop mNumAnimations
}
#endif // ASSIMP_BUILD_NO_GLTF_EXPORTER

View File

@@ -59,7 +59,6 @@ struct aiMaterial;
namespace glTF
{
class Asset;
struct TexProperty;
}
@@ -101,6 +100,7 @@ namespace Assimp
void ExportMeshes();
unsigned int ExportNode(const aiNode* node);
void ExportScene();
void ExportAnimations();
};
}