diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/X3D')
20 files changed, 7729 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/X3D/X3DExporter.cpp b/libs/assimp/code/AssetLib/X3D/X3DExporter.cpp new file mode 100644 index 0000000..b3278a5 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DExporter.cpp @@ -0,0 +1,675 @@ +/// \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 <assimp/Exceptional.h> +#include <assimp/StringUtils.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.0, 1.0, 1.0 })) { + 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 convenience. + + // 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 convenience. + + // 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 convenience. + + 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 diff --git a/libs/assimp/code/AssetLib/X3D/X3DExporter.hpp b/libs/assimp/code/AssetLib/X3D/X3DExporter.hpp new file mode 100644 index 0000000..fefaba9 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DExporter.hpp @@ -0,0 +1,250 @@ +/// \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/material.h> +#include <assimp/scene.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/Exporter.hpp> + +// 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 tessellation: +/// vertices, faces etc. Yes, you can use algorithm for detecting figures or shapes, but that's not a 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; + SAttribute() : + Name(), + Value() { + // empty + } + SAttribute(const std::string &name, const std::string &value) : + Name(name), + Value(value) { + // empty + } + + SAttribute(SAttribute &&rhs) AI_NO_EXCEPT : + Name(rhs.Name), + Value(rhs.Value) { + // empty + } + }; + + /***********************************************/ + /****************** 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. That's needed because "std::to_string" result depends 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 diff --git a/libs/assimp/code/AssetLib/X3D/X3DGeoHelper.cpp b/libs/assimp/code/AssetLib/X3D/X3DGeoHelper.cpp new file mode 100644 index 0000000..a9ac57e --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DGeoHelper.cpp @@ -0,0 +1,531 @@ +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +#include <assimp/vector3.h> +#include <assimp/Exceptional.h> +#include <assimp/StringUtils.h> + +#include <vector> + +namespace Assimp { + +aiVector3D X3DGeoHelper::make_point2D(float angle, float radius) { + return aiVector3D(radius * std::cos(angle), radius * std::sin(angle), 0); +} + +void X3DGeoHelper::make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list<aiVector3D> &pVertices) { + // check argument values ranges. + if ((pStartAngle < -AI_MATH_TWO_PI_F) || (pStartAngle > AI_MATH_TWO_PI_F)) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pStartAngle"); + } + if ((pEndAngle < -AI_MATH_TWO_PI_F) || (pEndAngle > AI_MATH_TWO_PI_F)) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pEndAngle"); + } + if (pRadius <= 0) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pRadius"); + } + + // calculate arc angle and check type of arc + float angle_full = std::fabs(pEndAngle - pStartAngle); + if ((angle_full > AI_MATH_TWO_PI_F) || (angle_full == 0.0f)) { + angle_full = AI_MATH_TWO_PI_F; + } + + // calculate angle for one step - angle to next point of line. + float angle_step = angle_full / (float)numSegments; + // make points + for (size_t pi = 0; pi <= numSegments; pi++) { + float tangle = pStartAngle + pi * angle_step; + pVertices.emplace_back(make_point2D(tangle, pRadius)); + } // for(size_t pi = 0; pi <= pNumSegments; pi++) + + // if we making full circle then add last vertex equal to first vertex + if (angle_full == AI_MATH_TWO_PI_F) pVertices.push_back(*pVertices.begin()); +} + +void X3DGeoHelper::extend_point_to_line(const std::list<aiVector3D> &pPoint, std::list<aiVector3D> &pLine) { + std::list<aiVector3D>::const_iterator pit = pPoint.begin(); + std::list<aiVector3D>::const_iterator pit_last = pPoint.end(); + + --pit_last; + + if (pPoint.size() < 2) { + throw DeadlyImportError("GeometryHelper_Extend_PointToLine.pPoint.size() can not be less than 2."); + } + + // add first point of first line. + pLine.push_back(*pit++); + // add internal points + while (pit != pit_last) { + pLine.push_back(*pit); // second point of previous line + pLine.push_back(*pit); // first point of next line + ++pit; + } + // add last point of last line + pLine.push_back(*pit); +} + +void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list<int32_t> &pPolylineCoordIdx, std::list<int32_t> &pLineCoordIdx) { + std::list<int32_t>::const_iterator plit = pPolylineCoordIdx.begin(); + + while (plit != pPolylineCoordIdx.end()) { + // add first point of polyline + pLineCoordIdx.push_back(*plit++); + while ((*plit != (-1)) && (plit != pPolylineCoordIdx.end())) { + std::list<int32_t>::const_iterator plit_next; + + plit_next = plit, ++plit_next; + pLineCoordIdx.push_back(*plit); // second point of previous line. + pLineCoordIdx.push_back(-1); // delimiter + if ((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break; // current polyline is finished + + pLineCoordIdx.push_back(*plit); // first point of next line. + plit = plit_next; + } // while((*plit != (-1)) && (plit != pPolylineCoordIdx.end())) + } // while(plit != pPolylineCoordIdx.end()) +} + +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +#define MESH_RectParallelepiped_CREATE_VERT \ + aiVector3D vert_set[8]; \ + float x1, x2, y1, y2, z1, z2, hs; \ + \ + hs = pSize.x / 2, x1 = -hs, x2 = hs; \ + hs = pSize.y / 2, y1 = -hs, y2 = hs; \ + hs = pSize.z / 2, z1 = -hs, z2 = hs; \ + vert_set[0].Set(x2, y1, z2); \ + vert_set[1].Set(x2, y2, z2); \ + vert_set[2].Set(x2, y2, z1); \ + vert_set[3].Set(x2, y1, z1); \ + vert_set[4].Set(x1, y1, z2); \ + vert_set[5].Set(x1, y2, z2); \ + vert_set[6].Set(x1, y2, z1); \ + vert_set[7].Set(x1, y1, z1) + +void X3DGeoHelper::rect_parallel_epiped(const aiVector3D &pSize, std::list<aiVector3D> &pVertices) { + MESH_RectParallelepiped_CREATE_VERT; + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0); // front + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5); // back + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 3, 0, 4); // left + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 2, 6, 5, 1); // right + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 0, 1, 5, 4); // top + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 6, 2, 3); // bottom +} + +#undef MESH_RectParallelepiped_CREATE_VERT + +void X3DGeoHelper::coordIdx_str2faces_arr(const std::vector<int32_t> &pCoordIdx, std::vector<aiFace> &pFaces, unsigned int &pPrimitiveTypes) { + std::vector<int32_t> f_data(pCoordIdx); + std::vector<unsigned int> inds; + unsigned int prim_type = 0; + + if (f_data.back() != (-1)) { + f_data.push_back(-1); + } + + // reserve average size. + pFaces.reserve(f_data.size() / 3); + inds.reserve(4); + //PrintVectorSet("build. ci", pCoordIdx); + for (std::vector<int32_t>::iterator it = f_data.begin(); it != f_data.end(); ++it) { + // when face is got count how many indices in it. + if (*it == (-1)) { + aiFace tface; + size_t ts; + + ts = inds.size(); + switch (ts) { + case 0: + goto mg_m_err; + case 1: + prim_type |= aiPrimitiveType_POINT; + break; + case 2: + prim_type |= aiPrimitiveType_LINE; + break; + case 3: + prim_type |= aiPrimitiveType_TRIANGLE; + break; + default: + prim_type |= aiPrimitiveType_POLYGON; + break; + } + + tface.mNumIndices = static_cast<unsigned int>(ts); + tface.mIndices = new unsigned int[ts]; + memcpy(tface.mIndices, inds.data(), ts * sizeof(unsigned int)); + pFaces.push_back(tface); + inds.clear(); + } // if(*it == (-1)) + else { + inds.push_back(*it); + } // if(*it == (-1)) else + } // for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++) + //PrintVectorSet("build. faces", pCoordIdx); + + pPrimitiveTypes = prim_type; + + return; + +mg_m_err: + for (size_t i = 0, i_e = pFaces.size(); i < i_e; i++) + delete[] pFaces.at(i).mIndices; + + pFaces.clear(); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::list<aiColor3D> &pColors, const bool pColorPerVertex) { + std::list<aiColor4D> tcol; + + // create RGBA array from RGB. + for (std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it) + tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1)); + + // call existing function for adding RGBA colors + add_color(pMesh, tcol, pColorPerVertex); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::list<aiColor4D> &pColors, const bool pColorPerVertex) { + std::list<aiColor4D>::const_iterator col_it = pColors.begin(); + + if (pColorPerVertex) { + if (pColors.size() < pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + ai_to_string(pColors.size()) + ") can not be less than Vertices count(" + + ai_to_string(pMesh.mNumVertices) + ")."); + } + + // copy colors to mesh + pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices]; + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mColors[0][i] = *col_it++; + } // if(pColorPerVertex) + else { + if (pColors.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + ai_to_string(pColors.size()) + ") can not be less than Faces count(" + + ai_to_string(pMesh.mNumFaces) + ")."); + } + + // copy colors to mesh + pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + // apply color to all vertices of face + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) { + pMesh.mColors[0][pMesh.mFaces[fi].mIndices[vi]] = *col_it; + } + + ++col_it; + } + } // if(pColorPerVertex) else +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pColorIdx, + const std::list<aiColor3D> &pColors, const bool pColorPerVertex) { + std::list<aiColor4D> tcol; + + // create RGBA array from RGB. + for (std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it) { + tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1)); + } + + // call existing function for adding RGBA colors + add_color(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::vector<int32_t> &coordIdx, const std::vector<int32_t> &colorIdx, + const std::list<aiColor4D> &colors, bool pColorPerVertex) { + std::vector<aiColor4D> col_tgt_arr; + std::list<aiColor4D> col_tgt_list; + std::vector<aiColor4D> col_arr_copy; + + if (coordIdx.size() == 0) { + throw DeadlyImportError("MeshGeometry_AddColor2. pCoordIdx can not be empty."); + } + + // copy list to array because we are need indexed access to colors. + col_arr_copy.reserve(colors.size()); + for (std::list<aiColor4D>::const_iterator it = colors.begin(); it != colors.end(); ++it) { + col_arr_copy.push_back(*it); + } + + if (pColorPerVertex) { + if (colorIdx.size() > 0) { + // check indices array count. + if (colorIdx.size() < coordIdx.size()) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + ai_to_string(colorIdx.size()) + + ") can not be less than Coords indices count(" + ai_to_string(coordIdx.size()) + ")."); + } + // create list with colors for every vertex. + col_tgt_arr.resize(pMesh.mNumVertices); + for (std::vector<int32_t>::const_iterator colidx_it = colorIdx.begin(), coordidx_it = coordIdx.begin(); colidx_it != colorIdx.end(); ++colidx_it, ++coordidx_it) { + if (*colidx_it == (-1)) { + continue; // skip faces delimiter + } + if ((unsigned int)(*coordidx_it) > pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Coordinate idx is out of range."); + } + if ((unsigned int)*colidx_it > pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Color idx is out of range."); + } + + col_tgt_arr[*coordidx_it] = col_arr_copy[*colidx_it]; + } + } // if(pColorIdx.size() > 0) + else { + // when color indices list is absent use CoordIdx. + // check indices array count. + if (colors.size() < pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + ai_to_string(colors.size()) + ") can not be less than Vertices count(" + + ai_to_string(pMesh.mNumVertices) + ")."); + } + // create list with colors for every vertex. + col_tgt_arr.resize(pMesh.mNumVertices); + for (size_t i = 0; i < pMesh.mNumVertices; i++) { + col_tgt_arr[i] = col_arr_copy[i]; + } + } // if(pColorIdx.size() > 0) else + } // if(pColorPerVertex) + else { + if (colorIdx.size() > 0) { + // check indices array count. + if (colorIdx.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + ai_to_string(colorIdx.size()) + + ") can not be less than Faces count(" + ai_to_string(pMesh.mNumFaces) + ")."); + } + // create list with colors for every vertex using faces indices. + col_tgt_arr.resize(pMesh.mNumFaces); + + std::vector<int32_t>::const_iterator colidx_it = colorIdx.begin(); + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + if ((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range."); + + col_tgt_arr[fi] = col_arr_copy[*colidx_it++]; + } + } // if(pColorIdx.size() > 0) + else { + // when color indices list is absent use CoordIdx. + // check indices array count. + if (colors.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + ai_to_string(colors.size()) + ") can not be less than Faces count(" + + ai_to_string(pMesh.mNumFaces) + ")."); + } + // create list with colors for every vertex using faces indices. + col_tgt_arr.resize(pMesh.mNumFaces); + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) + col_tgt_arr[fi] = col_arr_copy[fi]; + + } // if(pColorIdx.size() > 0) else + } // if(pColorPerVertex) else + + // copy array to list for calling function that add colors. + for (std::vector<aiColor4D>::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); ++it) + col_tgt_list.push_back(*it); + // add prepared colors list to mesh. + add_color(pMesh, col_tgt_list, pColorPerVertex); +} + +void X3DGeoHelper::add_normal(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pNormalIdx, + const std::list<aiVector3D> &pNormals, const bool pNormalPerVertex) { + std::vector<size_t> tind; + std::vector<aiVector3D> norm_arr_copy; + + // copy list to array because we are need indexed access to normals. + norm_arr_copy.reserve(pNormals.size()); + for (std::list<aiVector3D>::const_iterator it = pNormals.begin(); it != pNormals.end(); ++it) { + norm_arr_copy.push_back(*it); + } + + if (pNormalPerVertex) { + if (pNormalIdx.size() > 0) { + // check indices array count. + if (pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal."); + + tind.reserve(pNormalIdx.size()); + for (std::vector<int32_t>::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); ++it) { + if (*it != (-1)) tind.push_back(*it); + } + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++) { + if (tind[i] >= norm_arr_copy.size()) + throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(" + ai_to_string(tind[i]) + + ") is out of range. Normals count: " + ai_to_string(norm_arr_copy.size()) + "."); + + pMesh.mNormals[i] = norm_arr_copy[tind[i]]; + } + } else { + if (pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + std::list<aiVector3D>::const_iterator norm_it = pNormals.begin(); + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mNormals[i] = *norm_it++; + } + } // if(pNormalPerVertex) + else { + if (pNormalIdx.size() > 0) { + if (pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count."); + + std::vector<int32_t>::const_iterator normidx_it = pNormalIdx.begin(); + + tind.reserve(pNormalIdx.size()); + for (size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) + tind.push_back(*normidx_it++); + + } else { + tind.reserve(pMesh.mNumFaces); + for (size_t i = 0; i < pMesh.mNumFaces; i++) + tind.push_back(i); + } + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + aiVector3D tnorm; + + tnorm = norm_arr_copy[tind[fi]]; + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) + pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = tnorm; + } + } // if(pNormalPerVertex) else +} + +void X3DGeoHelper::add_normal(aiMesh &pMesh, const std::list<aiVector3D> &pNormals, const bool pNormalPerVertex) { + std::list<aiVector3D>::const_iterator norm_it = pNormals.begin(); + + if (pNormalPerVertex) { + if (pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mNormals[i] = *norm_it++; + } // if(pNormalPerVertex) + else { + if (pNormals.size() != pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and faces count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + // apply color to all vertices of face + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) + pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it; + + ++norm_it; + } + } // if(pNormalPerVertex) else +} + +void X3DGeoHelper::add_tex_coord(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pTexCoordIdx, + const std::list<aiVector2D> &pTexCoords) { + std::vector<aiVector3D> texcoord_arr_copy; + std::vector<aiFace> faces; + unsigned int prim_type; + + // copy list to array because we are need indexed access to normals. + texcoord_arr_copy.reserve(pTexCoords.size()); + for (std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it) { + texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0)); + } + + if (pTexCoordIdx.size() > 0) { + coordIdx_str2faces_arr(pTexCoordIdx, faces, prim_type); + if (faces.empty()) { + throw DeadlyImportError("Failed to add texture coordinates to mesh, faces list is empty."); + } + if (faces.size() != pMesh.mNumFaces) { + throw DeadlyImportError("Texture coordinates faces count must be equal to mesh faces count."); + } + } else { + coordIdx_str2faces_arr(pCoordIdx, faces, prim_type); + } + + pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices]; + pMesh.mNumUVComponents[0] = 2; + for (size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++) { + if (pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices) + throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: " + ai_to_string(fi) + "."); + + for (size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++) { + size_t vert_idx = pMesh.mFaces[fi].mIndices[ii]; + size_t tc_idx = faces.at(fi).mIndices[ii]; + + pMesh.mTextureCoords[0][vert_idx] = texcoord_arr_copy.at(tc_idx); + } + } // for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++) +} + +void X3DGeoHelper::add_tex_coord(aiMesh &pMesh, const std::list<aiVector2D> &pTexCoords) { + std::vector<aiVector3D> tc_arr_copy; + + if (pTexCoords.size() != pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddTexCoord. Texture coordinates and vertices count must be equal."); + } + + // copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus. + tc_arr_copy.reserve(pTexCoords.size()); + for (std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it) { + tc_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0)); + } + + // copy texture coordinates to mesh + pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices]; + pMesh.mNumUVComponents[0] = 2; + for (size_t i = 0; i < pMesh.mNumVertices; i++) { + pMesh.mTextureCoords[0][i] = tc_arr_copy[i]; + } +} + +aiMesh *X3DGeoHelper::make_mesh(const std::vector<int32_t> &pCoordIdx, const std::list<aiVector3D> &pVertices) { + std::vector<aiFace> faces; + unsigned int prim_type = 0; + + // create faces array from input string with vertices indices. + X3DGeoHelper::coordIdx_str2faces_arr(pCoordIdx, faces, prim_type); + if (!faces.size()) { + throw DeadlyImportError("Failed to create mesh, faces list is empty."); + } + + // + // Create new mesh and copy geometry data. + // + aiMesh *tmesh = new aiMesh; + size_t ts = faces.size(); + // faces + tmesh->mFaces = new aiFace[ts]; + tmesh->mNumFaces = static_cast<unsigned int>(ts); + for (size_t i = 0; i < ts; i++) + tmesh->mFaces[i] = faces.at(i); + + // vertices + std::list<aiVector3D>::const_iterator vit = pVertices.begin(); + + ts = pVertices.size(); + tmesh->mVertices = new aiVector3D[ts]; + tmesh->mNumVertices = static_cast<unsigned int>(ts); + for (size_t i = 0; i < ts; i++) { + tmesh->mVertices[i] = *vit++; + } + + // set primitives type and return result. + tmesh->mPrimitiveTypes = prim_type; + + return tmesh; +} + +} // namespace Assimp diff --git a/libs/assimp/code/AssetLib/X3D/X3DGeoHelper.h b/libs/assimp/code/AssetLib/X3D/X3DGeoHelper.h new file mode 100644 index 0000000..78e57f9 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DGeoHelper.h @@ -0,0 +1,39 @@ +#pragma once + +#include <assimp/vector2.h> +#include <assimp/vector3.h> +#include <assimp/color4.h> +#include <assimp/types.h> + +#include <list> +#include <vector> + +struct aiFace; +struct aiMesh; + +namespace Assimp { + +class X3DGeoHelper { +public: + static aiVector3D make_point2D(float angle, float radius); + static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list<aiVector3D> &pVertices); + static void extend_point_to_line(const std::list<aiVector3D> &pPoint, std::list<aiVector3D> &pLine); + static void polylineIdx_to_lineIdx(const std::list<int32_t> &pPolylineCoordIdx, std::list<int32_t> &pLineCoordIdx); + static void rect_parallel_epiped(const aiVector3D &pSize, std::list<aiVector3D> &pVertices); + static void coordIdx_str2faces_arr(const std::vector<int32_t> &pCoordIdx, std::vector<aiFace> &pFaces, unsigned int &pPrimitiveTypes); + static void add_color(aiMesh &pMesh, const std::list<aiColor3D> &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::list<aiColor4D> &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pColorIdx, + const std::list<aiColor3D> &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pColorIdx, + const std::list<aiColor4D> &pColors, const bool pColorPerVertex); + static void add_normal(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pNormalIdx, + const std::list<aiVector3D> &pNormals, const bool pNormalPerVertex); + static void add_normal(aiMesh &pMesh, const std::list<aiVector3D> &pNormals, const bool pNormalPerVertex); + static void add_tex_coord(aiMesh &pMesh, const std::vector<int32_t> &pCoordIdx, const std::vector<int32_t> &pTexCoordIdx, + const std::list<aiVector2D> &pTexCoords); + static void add_tex_coord(aiMesh &pMesh, const std::list<aiVector2D> &pTexCoords); + static aiMesh *make_mesh(const std::vector<int32_t> &pCoordIdx, const std::list<aiVector3D> &pVertices); +}; + +} // namespace Assimp diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter.cpp new file mode 100644 index 0000000..b34f361 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter.cpp @@ -0,0 +1,488 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter.cpp +/// \brief X3D-format files importer for Assimp: main algorithm implementation. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" + +#include <assimp/DefaultIOSystem.h> + +// Header files, stdlib. +#include <iterator> +#include <memory> + +namespace Assimp { + +/// Constant which holds the importer description +const aiImporterDesc X3DImporter::Description = { + "Extensible 3D(X3D) Importer", + "smalcom", + "", + "See documentation in source code. Chapter: Limitations.", + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, + 0, + 0, + 0, + 0, + "x3d x3db" +}; + +bool X3DImporter::isNodeEmpty(XmlNode &node) { + return node.first_child().empty(); +} + +void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) { + if (!isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty."); +} + +void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { + static const size_t Uns_Skip_Len = 192; + static const char *Uns_Skip[Uns_Skip_Len] = { + // CAD geometry component + "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet", + // Core + "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo", + // Distributed interactive simulation (DIS) component + "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu", + // Cube map environmental texturing component + "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture", + // Environmental effects component + "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground", + // Environmental sensor component + "ProximitySensor", "TransformSensor", "VisibilitySensor", + // Followers component + "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D", + "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D", + // Geospatial component + "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor", + "GeoTouchSensor", "GeoTransform", "GeoViewpoint", + // Humanoid Animation (H-Anim) component + "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite", + // Interpolation component + "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator", + "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D", + "SplineScalarInterpolator", "SquadOrientationInterpolator", + // Key device sensor component + "KeySensor", "StringSensor", + // Layering component + "Layer", "LayerSet", "Viewport", + // Layout component + "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup", + // Navigation component + "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup", + // Networking component + "EXPORT", "IMPORT", "Anchor", "LoadSensor", + // NURBS component + "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface", + "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate", + "NurbsTrimmedSurface", + // Particle systems component + "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter", + "VolumeEmitter", "WindPhysicsModel", + // Picking component + "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor", + // Pointing device sensor component + "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor", + // Rendering component + "ClipPlane", + // Rigid body physics + "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint", + "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint", + // Scripting component + "Script", + // Programmable shaders component + "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart", + "ShaderProgram", + // Shape component + "FillProperties", "LineProperties", "TwoSidedMaterial", + // Sound component + "AudioClip", "Sound", + // Text component + "FontStyle", "Text", + // Texturing3D Component + "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D", + // Texturing component + "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties", + // Time component + "TimeSensor", + // Event Utilities component + "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger", + // Volume rendering component + "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData", + "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle", + "VolumeData" + }; + + const std::string nn = node.name(); + + if (nn.empty()) { + const std::string nv = node.value(); + if (!nv.empty()) { + LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + "."); + return; + } + } + + bool found = false; + + for (size_t i = 0; i < Uns_Skip_Len; i++) { + if (nn == Uns_Skip[i]) { + found = true; + } + } + + if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + "."); + + LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + "."); +} + +X3DImporter::X3DImporter() : + mNodeElementCur(nullptr), + mScene(nullptr), + mpIOHandler(nullptr) { + // empty +} + +X3DImporter::~X3DImporter() { + // Clear() is accounting if data already is deleted. So, just check again if all data is deleted. + Clear(); +} + +void X3DImporter::Clear() { + mNodeElementCur = nullptr; + // Delete all elements + if (!NodeElement_List.empty()) { + for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) { + delete *it; + } + NodeElement_List.clear(); + } +} + +void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { + ai_assert(nullptr != pIOHandler); + + static const std::string mode = "rb"; + std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode)); + if (!fileStream.get()) { + throw DeadlyImportError("Failed to open file " + file + "."); + } + + XmlParser theParser; + if (!theParser.parse(fileStream.get())) { + return; + } + + XmlNode *node = theParser.findNode("X3D"); + if (nullptr == node) { + return; + } + + for (auto ¤tNode : node->children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "head") { + readHead(currentNode); + } else if (currentName == "Scene") { + readScene(currentNode); + } else { + skipUnsupportedNode("X3D", currentNode); + } + } +} + +bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig) const { + if (checkSig) { + if (GetExtension(pFile) == "x3d") + return true; + } + + return false; +} + +void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + mpIOHandler = pIOHandler; + + Clear(); + std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb")); + if (!stream) { + throw DeadlyImportError("Could not open file for reading"); + } + std::string::size_type slashPos = pFile.find_last_of("\\/"); + + mScene = pScene; + pScene->mRootNode = new aiNode(pFile); + pScene->mRootNode->mParent = nullptr; + pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; + + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + + //search for root node element + + mNodeElementCur = NodeElement_List.front(); + if (mNodeElementCur == nullptr) { + return; + } + while (mNodeElementCur->Parent != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; + } + + { // fill aiScene with objects. + std::list<aiMesh *> mesh_list; + std::list<aiMaterial *> mat_list; + std::list<aiLight *> light_list; + + // create nodes tree + Postprocess_BuildNode(*mNodeElementCur, *pScene->mRootNode, mesh_list, mat_list, light_list); + // copy needed data to scene + if (!mesh_list.empty()) { + std::list<aiMesh *>::const_iterator it = mesh_list.begin(); + + pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size()); + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + for (size_t i = 0; i < pScene->mNumMeshes; i++) + pScene->mMeshes[i] = *it++; + } + + if (!mat_list.empty()) { + std::list<aiMaterial *>::const_iterator it = mat_list.begin(); + + pScene->mNumMaterials = static_cast<unsigned int>(mat_list.size()); + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (size_t i = 0; i < pScene->mNumMaterials; i++) + pScene->mMaterials[i] = *it++; + } + + if (!light_list.empty()) { + std::list<aiLight *>::const_iterator it = light_list.begin(); + + pScene->mNumLights = static_cast<unsigned int>(light_list.size()); + pScene->mLights = new aiLight *[pScene->mNumLights]; + for (size_t i = 0; i < pScene->mNumLights; i++) + pScene->mLights[i] = *it++; + } + } +} + +const aiImporterDesc *X3DImporter::GetInfo() const { + return &Description; +} + +struct meta_entry { + std::string name; + std::string value; +}; + +void X3DImporter::readHead(XmlNode &node) { + std::vector<meta_entry> metaArray; + for (auto currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "meta") { + //checkNodeMustBeEmpty(node); + meta_entry entry; + if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) { + XmlParser::getStdStrAttribute(currentNode, "content", entry.value); + metaArray.emplace_back(entry); + } + } + // TODO: check if other node types in head should be supported + } + mScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metaArray.size())); + unsigned int i = 0; + for (auto currentMeta : metaArray) { + mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value)); + ++i; + } +} + +void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) { + if (node.empty()) { + return; + } + for (auto currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "Shape") + readShape(currentNode); + else if (currentName == "Group") { + startReadGroup(currentNode); + readChildNodes(currentNode, "Group"); + endReadGroup(); + } else if (currentName == "StaticGroup") { + startReadStaticGroup(currentNode); + readChildNodes(currentNode, "StaticGroup"); + endReadStaticGroup(); + } else if (currentName == "Transform") { + startReadTransform(currentNode); + readChildNodes(currentNode, "Transform"); + endReadTransform(); + } else if (currentName == "Switch") { + startReadSwitch(currentNode); + readChildNodes(currentNode, "Switch"); + endReadSwitch(); + } else if (currentName == "DirectionalLight") { + readDirectionalLight(currentNode); + } else if (currentName == "PointLight") { + readPointLight(currentNode); + } else if (currentName == "SpotLight") { + readSpotLight(currentNode); + } else if (currentName == "Inline") { + readInline(currentNode); + } else if (!checkForMetadataNode(currentNode)) { + skipUnsupportedNode(pParentNodeName, currentNode); + } + } +} + +void X3DImporter::readScene(XmlNode &node) { + ParseHelper_Group_Begin(true); + readChildNodes(node, "Scene"); + ParseHelper_Node_Exit(); +} + +/*********************************************************************************************************************************************/ +/************************************************************ Functions: find set ************************************************************/ +/*********************************************************************************************************************************************/ + +bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) { + if (((*it)->Type == pType) && ((*it)->ID == pID)) { + if (pElement != nullptr) *pElement = *it; + + return true; + } + } // for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++) + + return false; +} + +bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement) { + bool found = false; // flag: true - if requested element is found. + + // Check if pStartNode - this is the element, we are looking for. + if ((pStartNode->Type == pType) && (pStartNode->ID == pID)) { + found = true; + if (pElement != nullptr) { + *pElement = pStartNode; + } + + goto fne_fn_end; + } // if((pStartNode->Type() == pType) && (pStartNode->ID() == pID)) + + // Check childs of pStartNode. + for (std::list<X3DNodeElementBase *>::iterator ch_it = pStartNode->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) { + found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement); + if (found) { + break; + } + } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++) + +fne_fn_end: + + return found; +} + +bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + X3DNodeElementBase *tnd = mNodeElementCur; // temporary pointer to node. + bool static_search = false; // flag: true if searching in static node. + + // At first check if we have deal with static node. Go up through parent nodes and check flag. + while (tnd != nullptr) { + if (tnd->Type == X3DElemType::ENET_Group) { + if (((X3DNodeElementGroup *)tnd)->Static) { + static_search = true; // Flag found, stop walking up. Node with static flag will holded in tnd variable. + break; + } + } + + tnd = tnd->Parent; // go up in graph. + } // while (tnd != nullptr) + + // at now call appropriate search function. + if (static_search) { + return FindNodeElement_FromNode(tnd, pID, pType, pElement); + } else { + return FindNodeElement_FromRoot(pID, pType, pElement); + } +} + +/*********************************************************************************************************************************************/ +/************************************************************ Functions: parse set ***********************************************************/ +/*********************************************************************************************************************************************/ + +void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) { + X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent. + + // if we are adding not the root element then add new element to current element child list. + if (mNodeElementCur != nullptr) { + mNodeElementCur->Children.push_back(new_group); + } + + NodeElement_List.push_back(new_group); // it's a new element - add it to list. + mNodeElementCur = new_group; // switch current element to new one. +} + +void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) { + ai_assert(nullptr != pNode); + + mNodeElementCur->Children.push_back(pNode); // add new element to current element child list. + mNodeElementCur = pNode; // switch current element to new one. +} + +void X3DImporter::ParseHelper_Node_Exit() { + // check if we can walk up. + if (mNodeElementCur != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; + } else { + int i = 0; + ++i; + } +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter.hpp b/libs/assimp/code/AssetLib/X3D/X3DImporter.hpp new file mode 100644 index 0000000..d9aed70 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter.hpp @@ -0,0 +1,383 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef INCLUDED_AI_X3D_IMPORTER_H +#define INCLUDED_AI_X3D_IMPORTER_H + +#include "X3DImporter_Node.hpp" + +#include <assimp/BaseImporter.h> +#include <assimp/XmlParser.h> +#include <assimp/importerdesc.h> +#include <assimp/scene.h> +#include <assimp/types.h> +#include <assimp/DefaultLogger.hpp> +#include <assimp/ProgressHandler.hpp> + +#include <list> +#include <string> + +namespace Assimp { + +inline void Throw_ArgOutOfRange(const std::string &argument) { + throw DeadlyImportError("Argument value is out of range for: \"" + argument + "\"."); +} + +inline void Throw_CloseNotFound(const std::string &node) { + throw DeadlyImportError("Close tag for node <" + node + "> not found. Seems file is corrupt."); +} + +inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of floats."); +} + +inline void Throw_ConvertFail_Str2ArrD(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of doubles."); +} + +inline void Throw_ConvertFail_Str2ArrB(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of booleans."); +} + +inline void Throw_ConvertFail_Str2ArrI(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of integers."); +} + +inline void Throw_DEF_And_USE(const std::string &nodeName) { + throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">."); +} + +inline void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName) { + throw DeadlyImportError("Node <" + nodeName + "> has incorrect attribute \"" + pAttrName + "\"."); +} + +inline void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName) { + throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + nodeName + "> has incorrect value."); +} + +inline void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) { + throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + nodeName + ". Description: " + pDescription); +} + +inline void Throw_TagCountIncorrect(const std::string &pNode) { + throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt."); +} + +inline void Throw_USE_NotFound(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("Not found node with name \"" + pAttrValue + "\" in <" + nodeName + ">."); +} + +inline void LogInfo(const std::string &message) { + DefaultLogger::get()->info(message); +} + +/// \class X3DImporter +/// Class that holding scene graph which include: groups, geometry, metadata etc. +/// +/// Limitations. +/// +/// Pay attention that X3D is format for interactive graphic and simulations for web browsers. +/// So not all features can be imported using Assimp. +/// +/// Unsupported nodes: +/// CAD geometry component: +/// "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet" +/// Core component: +/// "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo" +/// Distributed interactive simulation (DIS) component: +/// "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu" +/// Cube map environmental texturing component: +/// "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture" +/// Environmental effects component: +/// "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground" +/// Environmental sensor component: +/// "ProximitySensor", "TransformSensor", "VisibilitySensor" +/// Followers component: +/// "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", +/// "PositionChaser2D", "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D" +/// Geospatial component: +/// "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor", +/// "GeoTouchSensor", "GeoTransform", "GeoViewpoint" +/// Humanoid Animation (H-Anim) component: +/// "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite" +/// Interpolation component: +/// "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator", +/// "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D", +/// "SplineScalarInterpolator", "SquadOrientationInterpolator", +/// Key device sensor component: +/// "KeySensor", "StringSensor" +/// Layering component: +/// "Layer", "LayerSet", "Viewport" +/// Layout component: +/// "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup" +/// Navigation component: +/// "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup" +/// Networking component: +/// "EXPORT", "IMPORT", "Anchor", "LoadSensor" +/// NURBS component: +/// "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface", +/// "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate", +/// "NurbsTrimmedSurface" +/// Particle systems component: +/// "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", +/// "SurfaceEmitter", "VolumeEmitter", "WindPhysicsModel" +/// Picking component: +/// "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor" +/// Pointing device sensor component: +/// "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor" +/// Rendering component: +/// "ClipPlane" +/// Rigid body physics: +/// "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint", +/// "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint" +/// Scripting component: +/// "Script" +/// Programmable shaders component: +/// "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart", +/// "ShaderProgram", +/// Shape component: +/// "FillProperties", "LineProperties", "TwoSidedMaterial" +/// Sound component: +/// "AudioClip", "Sound" +/// Text component: +/// "FontStyle", "Text" +/// Texturing3D Component: +/// "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", +/// "TextureTransform3D" +/// Texturing component: +/// "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", +/// "TextureProperties", +/// Time component: +/// "TimeSensor" +/// Event Utilities component: +/// "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger", +/// Volume rendering component: +/// "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", +/// "IsoSurfaceVolumeData", "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", +/// "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle", "VolumeData" +/// +/// Supported nodes: +/// Core component: +/// "MetadataBoolean", "MetadataDouble", "MetadataFloat", "MetadataInteger", "MetadataSet", "MetadataString" +/// Geometry2D component: +/// "Arc2D", "ArcClose2D", "Circle2D", "Disk2D", "Polyline2D", "Polypoint2D", "Rectangle2D", "TriangleSet2D" +/// Geometry3D component: +/// "Box", "Cone", "Cylinder", "ElevationGrid", "Extrusion", "IndexedFaceSet", "Sphere" +/// Grouping component: +/// "Group", "StaticGroup", "Switch", "Transform" +/// Lighting component: +/// "DirectionalLight", "PointLight", "SpotLight" +/// Networking component: +/// "Inline" +/// Rendering component: +/// "Color", "ColorRGBA", "Coordinate", "IndexedLineSet", "IndexedTriangleFanSet", "IndexedTriangleSet", "IndexedTriangleStripSet", "LineSet", +/// "PointSet", "TriangleFanSet", "TriangleSet", "TriangleStripSet", "Normal" +/// Shape component: +/// "Shape", "Appearance", "Material" +/// Texturing component: +/// "ImageTexture", "TextureCoordinate", "TextureTransform" +/// +/// Limitations of attribute "USE". +/// If "USE" is set then node must be empty, like that: +/// <Node USE='name'/> +/// not the +/// <Node USE='name'><!-- something --> </Node> +/// +/// Ignored attributes: "creaseAngle", "convex", "solid". +/// +/// Texture coordinates generating: only for Sphere, Cone, Cylinder. In all other case used PLANE mapping. +/// It's better that Assimp main code has powerful texture coordinates generator. Then is not needed to +/// duplicate this code in every importer. +/// +/// Lighting limitations. +/// If light source placed in some group with "DEF" set. And after that some node is use it group with "USE" attribute then +/// you will get error about duplicate light sources. That's happening because Assimp require names for lights but do not like +/// duplicates of it )). +/// +/// Color for faces. +/// That's happening when attribute "colorPerVertex" is set to "false". But Assimp do not hold how many colors has mesh and require +/// equal length for mVertices and mColors. You will see the colors but vertices will use call which last used in "colorIdx". +/// +/// That's all for now. Enjoy +/// + +using X3DElementList = std::list<X3DNodeElementBase *>; + +class X3DImporter : public BaseImporter { +public: + std::list<X3DNodeElementBase *> NodeElement_List; ///< All elements of scene graph. + +public: + /// Default constructor. + X3DImporter(); + + /// Default destructor. + ~X3DImporter(); + + /***********************************************/ + /******** Functions: parse set, public *********/ + /***********************************************/ + + /// Parse X3D file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph. + /// Also exception can be thrown if trouble will found. + /// \param [in] pFile - name of file to be parsed. + /// \param [in] pIOHandler - pointer to IO helper object. + void ParseFile(const std::string &pFile, IOSystem *pIOHandler); + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const; + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); + const aiImporterDesc *GetInfo() const; + void Clear(); + +private: + X3DNodeElementBase *MACRO_USE_CHECKANDAPPLY(XmlNode &node, std::string pDEF, std::string pUSE, X3DElemType pType, X3DNodeElementBase *pNE); + bool isNodeEmpty(XmlNode &node); + void checkNodeMustBeEmpty(XmlNode &node); + void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node); + void readHead(XmlNode &node); + void readChildNodes(XmlNode &node, const std::string &pParentNodeName); + void readScene(XmlNode &node); + + bool FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + void ParseHelper_Group_Begin(const bool pStatic = false); + void ParseHelper_Node_Enter(X3DNodeElementBase *pNode); + void ParseHelper_Node_Exit(); + + // 2D geometry + void readArc2D(XmlNode &node); + void readArcClose2D(XmlNode &node); + void readCircle2D(XmlNode &node); + void readDisk2D(XmlNode &node); + void readPolyline2D(XmlNode &node); + void readPolypoint2D(XmlNode &node); + void readRectangle2D(XmlNode &node); + void readTriangleSet2D(XmlNode &node); + + // 3D geometry + void readBox(XmlNode &node); + void readCone(XmlNode &node); + void readCylinder(XmlNode &node); + void readElevationGrid(XmlNode &node); + void readExtrusion(XmlNode &node); + void readIndexedFaceSet(XmlNode &node); + void readSphere(XmlNode &node); + + // group + void startReadGroup(XmlNode &node); + void endReadGroup(); + void startReadStaticGroup(XmlNode &node); + void endReadStaticGroup(); + void startReadSwitch(XmlNode &node); + void endReadSwitch(); + void startReadTransform(XmlNode &node); + void endReadTransform(); + + // light + void readDirectionalLight(XmlNode &node); + void readPointLight(XmlNode &node); + void readSpotLight(XmlNode &node); + + // metadata + bool checkForMetadataNode(XmlNode &node); + void childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName); + void readMetadataBoolean(XmlNode &node); + void readMetadataDouble(XmlNode &node); + void readMetadataFloat(XmlNode &node); + void readMetadataInteger(XmlNode &node); + void readMetadataSet(XmlNode &node); + void readMetadataString(XmlNode &node); + + // networking + void readInline(XmlNode &node); + + // postprocessing + aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const; + void PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list<X3DNodeElementBase *> &pList) const; + bool PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const; + bool PostprocessHelper_ElementIsMesh(const X3DElemType pType) const; + void Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list<aiLight *> &pSceneLightList) const; + void Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const; + void Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const; + void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list<aiMesh *> &pSceneMeshList, + std::list<aiMaterial *> &pSceneMaterialList, std::list<aiLight *> &pSceneLightList) const; + void Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list<unsigned int> &pNodeMeshInd, + std::list<aiMesh *> &pSceneMeshList, std::list<aiMaterial *> &pSceneMaterialList) const; + void Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const; + + // rendering + void readColor(XmlNode &node); + void readColorRGBA(XmlNode &node); + void readCoordinate(XmlNode &node); + void readIndexedLineSet(XmlNode &node); + void readIndexedTriangleFanSet(XmlNode &node); + void readIndexedTriangleSet(XmlNode &node); + void readIndexedTriangleStripSet(XmlNode &node); + void readLineSet(XmlNode &node); + void readPointSet(XmlNode &node); + void readTriangleFanSet(XmlNode &node); + void readTriangleSet(XmlNode &node); + void readTriangleStripSet(XmlNode &node); + void readNormal(XmlNode &node); + + // shape + void readShape(XmlNode &node); + void readAppearance(XmlNode &node); + void readMaterial(XmlNode &node); + + // texturing + void readImageTexture(XmlNode &node); + void readTextureCoordinate(XmlNode &node); + void readTextureTransform(XmlNode &node); + + static const aiImporterDesc Description; + X3DNodeElementBase *mNodeElementCur; + aiScene *mScene; + IOSystem *mpIOHandler; +}; // class X3DImporter + +} // namespace Assimp + +#endif // INCLUDED_AI_X3D_IMPORTER_H diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp new file mode 100644 index 0000000..8d0f5ba --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp @@ -0,0 +1,467 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Geometry2D.cpp +/// \brief Parsing data from nodes of "Geometry2D" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +namespace Assimp { + +// <Arc2D +// DEF="" ID +// USE="" IDREF +// endAngle="1.570796" SFFloat [initializeOnly] +// radius="1" SFFloat [initializeOnly] +// startAngle="0" SFFloat [initializeOnly] +// /> +// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle +// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different +// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified. +void X3DImporter::readArc2D(XmlNode &node) { + std::string def, use; + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Arc2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Arc2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list<aiVector3D> tlist; + + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Arc2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <ArcClose2D +// DEF="" ID +// USE="" IDREF +// closureType="PIE" SFString [initializeOnly], {"PIE", "CHORD"} +// endAngle="1.570796" SFFloat [initializeOnly] +// radius="1" SFFloat [initializeOnly] +// solid="false" SFBool [initializeOnly] +// startAngle="0" SFFloat [initializeOnly] +// /> +// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius +// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater +// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has +// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between +// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. +// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then +// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point +// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when +// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readArcClose2D(XmlNode &node) { + std::string def, use; + std::string closureType("PIE"); + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + bool solid = false; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "closureType", closureType); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ArcClose2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_ArcClose2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // create point list of geometry object. + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, ((X3DNodeElementGeometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg + // add chord or two radiuses only if not a circle was defined + if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { + std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + if ((closureType == "PIE") || (closureType == "\"PIE\"")) + vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line + else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) + Throw_IncorrectAttrValue("ArcClose2D", "closureType"); + + vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = ((X3DNodeElementGeometry2D *)ne)->Vertices.size(); + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ArcClose2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Circle2D +// DEF="" ID +// USE="" IDREF +// radius="1" SFFloat [initializeOnly] +// /> +void X3DImporter::readCircle2D(XmlNode &node) { + std::string def, use; + float radius = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Circle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Circle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list<aiVector3D> tlist; + + X3DGeoHelper::make_arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Circle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Disk2D +// DEF="" ID +// USE="" IDREF +// innerRadius="0" SFFloat [initializeOnly] +// outerRadius="1" SFFloat [initializeOnly] +// solid="false" SFBool [initializeOnly] +// /> +// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the +// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. +// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely +// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall +// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of +// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readDisk2D(XmlNode &node) { + std::string def, use; + float innerRadius = 0; + float outerRadius = 1; + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "innerRadius", innerRadius); + XmlParser::getFloatAttribute(node, "outerRadius", outerRadius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Disk2D, ne); + } else { + std::list<aiVector3D> tlist_o, tlist_i; + + if (innerRadius > outerRadius) Throw_IncorrectAttrValue("Disk2D", "innerRadius"); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Disk2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object. + ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::make_arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle + if (innerRadius == 0.0f) { // make filled disk + // in tlist_o we already have points of circle. just copy it and sign as polygon. + ((X3DNodeElementGeometry2D *)ne)->Vertices = tlist_o; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = tlist_o.size(); + } else if (innerRadius == outerRadius) { // make circle + // in tlist_o we already have points of circle. convert it to line set. + X3DGeoHelper::extend_point_to_line(tlist_o, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + } else { // make disk + std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + X3DGeoHelper::make_arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle + // + // create quad list from two point lists + // + if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. + + // add all quads except last + for (std::list<aiVector3D>::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { + // do not forget - CCW direction + vlist.push_back(*it_i++); // 1st point + vlist.push_back(*it_o++); // 2nd point + vlist.push_back(*it_o); // 3rd point + vlist.push_back(*it_i); // 4th point + } + + // add last quad + vlist.push_back(*tlist_i.end()); // 1st point + vlist.push_back(*tlist_o.end()); // 2nd point + vlist.push_back(*tlist_o.begin()); // 3rd point + vlist.push_back(*tlist_o.begin()); // 4th point + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Disk2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Polyline2D +// DEF="" ID +// USE="" IDREF +// lineSegments="" MFVec2F [intializeOnly] +// /> +void X3DImporter::readPolyline2D(XmlNode &node) { + std::string def, use; + std::list<aiVector2D> lineSegments; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "lineSegments", lineSegments); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polyline2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polyline2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // + // convert read point list of geometry object to line set. + // + std::list<aiVector3D> tlist; + + // convert vec2 to vec3 + for (std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) + tlist.push_back(aiVector3D(it2->x, it2->y, 0)); + + // convert point set to line set + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polyline2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Polypoint2D +// DEF="" ID +// USE="" IDREF +// point="" MFVec2F [inputOutput] +// /> +void X3DImporter::readPolypoint2D(XmlNode &node) { + std::string def, use; + std::list<aiVector2D> point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polypoint2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polypoint2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 1; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polypoint2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Rectangle2D +// DEF="" ID +// USE="" IDREF +// size="2 2" SFVec2f [initializeOnly] +// solid="false" SFBool [initializeOnly] +// /> +void X3DImporter::readRectangle2D(XmlNode &node) { + std::string def, use; + aiVector2D size(2, 2); + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Rectangle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Rectangle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + float x1 = -size.x / 2.0f; + float x2 = size.x / 2.0f; + float y1 = -size.y / 2.0f; + float y2 = size.y / 2.0f; + std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point + vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point + vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point + vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Rectangle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <TriangleSet2D +// DEF="" ID +// USE="" IDREF +// solid="false" SFBool [initializeOnly] +// vertices="" MFVec2F [inputOutput] +// /> +void X3DImporter::readTriangleSet2D(XmlNode &node) { + std::string def, use; + bool solid = false; + std::list<aiVector2D> vertices; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "vertices", vertices); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet2D, ne); + } else { + if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_TriangleSet2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TriangleSet2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp new file mode 100644 index 0000000..b9fc2a4 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp @@ -0,0 +1,918 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Geometry3D.cpp +/// \brief Parsing data from nodes of "Geometry3D" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +// Header files, Assimp. +#include <assimp/StandardShapes.h> + +namespace Assimp { + +// <Box +// DEF="" ID +// USE="" IDREF +// size="2 2 2" SFVec3f [initializeOnly] +// solid="true" SFBool [initializeOnly] +// /> +// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. +// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes +// respectively and each component value shall be greater than zero. +void X3DImporter::readBox(XmlNode &node) { + std::string def, use; + bool solid = true; + aiVector3D size(2, 2, 2); + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Box, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Box, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DGeoHelper::rect_parallel_epiped(size, ((X3DNodeElementGeometry3D *)ne)->Vertices); // get quad list + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Box"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Cone +// DEF="" ID +// USE="" IDREF +// bottom="true" SFBool [initializeOnly] +// bottomRadius="1" SFloat [initializeOnly] +// height="2" SFloat [initializeOnly] +// side="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// /> +void X3DImporter::readCone(XmlNode &node) { + std::string use, def; + bool bottom = true; + float bottomRadius = 1; + float height = 2; + bool side = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getFloatAttribute(node, "height", height); + XmlParser::getFloatAttribute(node, "bottomRadius", bottomRadius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cone, ne); + } else { + const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property + + std::vector<aiVector3D> tvec; // temp array for vertices. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cone, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // make cone or parts according to flags. + if (side) { + StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); + } else if (bottom) { + StandardShapes::MakeCircle(bottomRadius, tess, tvec); + height = -(height / 2); + for (std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) + it->y = height; // y - because circle made in oXZ. + } + + // copy data from temp array + for (std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) + ((X3DNodeElementGeometry3D *)ne)->Vertices.push_back(*it); + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cone"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Cylinder +// DEF="" ID +// USE="" IDREF +// bottom="true" SFBool [initializeOnly] +// height="2" SFloat [initializeOnly] +// radius="1" SFloat [initializeOnly] +// side="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// top="true" SFBool [initializeOnly] +// /> +void X3DImporter::readCylinder(XmlNode &node) { + std::string use, def; + bool bottom = true; + float height = 2; + float radius = 1; + bool side = true; + bool solid = true; + bool top = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getBoolAttribute(node, "top", top); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getFloatAttribute(node, "height", height); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cylinder, ne); + } else { + const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property + + std::vector<aiVector3D> tside; // temp array for vertices of side. + std::vector<aiVector3D> tcir; // temp array for vertices of circle. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cylinder, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // make cilynder or parts according to flags. + if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); + + height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height. + if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); + // copy data from temp arrays + std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry3D *)ne)->Vertices; // just short alias. + + for (std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); ++it) + vlist.push_back(*it); + + if (top) { + for (std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it) { + (*it).y = height; // y - because circle made in oXZ. + vlist.push_back(*it); + } + } // if(top) + + if (bottom) { + for (std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it) { + (*it).y = -height; // y - because circle made in oXZ. + vlist.push_back(*it); + } + } // if(top) + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cylinder"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <ElevationGrid +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// creaseAngle="0" SFloat [initializeOnly] +// height="" MFloat [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// xDimension="0" SFInt32 [initializeOnly] +// xSpacing="1.0" SFloat [initializeOnly] +// zDimension="0" SFInt32 [initializeOnly] +// zSpacing="1.0" SFloat [initializeOnly] +// > +// <!-- ColorNormalTexCoordContentModel --> +// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single +// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </ElevationGrid> +// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described +// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate +// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. +// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. +void X3DImporter::readElevationGrid(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + float creaseAngle = 0; + std::vector<float> height; + bool normalPerVertex = true; + bool solid = true; + int32_t xDimension = 0; + float xSpacing = 1; + int32_t zDimension = 0; + float zSpacing = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getFloatArrayAttribute(node, "height", height); + XmlParser::getIntAttribute(node, "xDimension", xDimension); + XmlParser::getFloatAttribute(node, "xSpacing", xSpacing); + XmlParser::getIntAttribute(node, "zDimension", zDimension); + XmlParser::getFloatAttribute(node, "zSpacing", zSpacing); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ElevationGrid, ne); + } else { + if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in <ElevationGrid> must be grater than zero."); + if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in <ElevationGrid> must be grater than zero."); + if ((size_t)(xDimension * zDimension) != height.size()) DeadlyImportError("Heights count must be equal to \"xDimension * zDimension\" in <ElevationGrid>"); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementElevationGrid(X3DElemType::ENET_ElevationGrid, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementElevationGrid &grid_alias = *((X3DNodeElementElevationGrid *)ne); // create alias for conveience + + { // create grid vertices list + std::vector<float>::const_iterator he_it = height.begin(); + + for (int32_t zi = 0; zi < zDimension; zi++) // rows + { + for (int32_t xi = 0; xi < xDimension; xi++) // columns + { + aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); + + grid_alias.Vertices.push_back(tvec); + ++he_it; + } + } + } // END: create grid vertices list + // + // create faces list. In "coordIdx" format + // + // check if we have quads + if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. + for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) { + grid_alias.CoordIdx.push_back(static_cast<int32_t>(i)); + grid_alias.CoordIdx.push_back(static_cast<int32_t>(i + 1)); + grid_alias.CoordIdx.push_back(-1); + } + } else // two or more elements in every dimension is set. create quad set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 4; + for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows + { + for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns + { + // points direction in face. + if (ccw) { + // CCW: + // 3 2 + // 0 1 + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + } else { + // CW: + // 0 1 + // 3 2 + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + } // if(ccw) else + + grid_alias.CoordIdx.push_back(-1); + } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) + } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) + } // if((xDimension < 2) || (zDimension < 2)) else + + grid_alias.ColorPerVertex = colorPerVertex; + grid_alias.NormalPerVertex = normalPerVertex; + grid_alias.CreaseAngle = creaseAngle; + grid_alias.Solid = solid; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("ElevationGrid", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!mReader->isEmptyElement()) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } // if(!mReader->isEmptyElement()) else + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +template <typename TVector> +static void GeometryHelper_Extrusion_CurveIsClosed(std::vector<TVector> &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) { + size_t cur_sz = pCurve.size(); + + pCurveIsClosed = false; + // for curve with less than four points checking is have no sense, + if (cur_sz < 4) return; + + for (size_t s = 3, s_e = cur_sz; s < s_e; s++) { + // search for first point of duplicated part. + if (pCurve[0] == pCurve[s]) { + bool found = true; + + // check if tail(indexed by b2) is duplicate of head(indexed by b1). + for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) { + if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop. + found = false; + + break; + } + } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + + // if duplicate tail is found then drop or not it depending on flags. + if (found) { + pCurveIsClosed = true; + if (pDropTail) { + if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics. + + pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail + } + + break; + } // if(found) + } // if(pCurve[0] == pCurve[s]) + } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) +} + +static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector<aiVector3D> &pSpine, const bool pSpine_Closed) { + const size_t spine_idx_last = pSpine.size() - 1; + aiVector3D tvec; + + if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases + { + if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. + // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) + // in tail are removed. + // So, last point in pSpine is a spine[n - 2] + tvec = pSpine[1] - pSpine[spine_idx_last]; + } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1] + tvec = pSpine[1] - pSpine[0]; + } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is + // the spine[0]. + tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; + } + } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) + else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). + tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; + } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else + + return tvec.Normalize(); +} + +static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector<aiVector3D> &pSpine, const bool pSpine_Closed, + const aiVector3D pVecZ_Prev) { + const aiVector3D zero_vec(0); + const size_t spine_idx_last = pSpine.size() - 1; + + aiVector3D tvec; + + // at first special cases + if (pSpine.size() < 3) // spine have not enough points for vector calculations. + { + tvec.Set(0, 0, 1); + } else if (pSpine_PointIdx == 0) // special case: first point + { + if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. + { + tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); + } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. + { + bool found = false; + + // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) + // then the Z-axis for the first spine point with a defined Z-axis is used." + // Walk through spine and find Z. + for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) { + // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) + tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); + found = !tvec.Equal(zero_vec); + } + + // if entire spine are collinear then use OZ axis. + if (!found) tvec.Set(0, 0, 1); + } // if(pSpine_Closed) else + } // else if(pSpine_PointIdx == 0) + else if (pSpine_PointIdx == spine_idx_last) // special case: last point + { + if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. + tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } else { // vector Z for last point of not closed curve is previous vector Z. + tvec = pVecZ_Prev; + } + } else // regular point + { + tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + + // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis + // is flipped (multiplied by -1). + if ((tvec * pVecZ_Prev) < 0) tvec = -tvec; + + return tvec.Normalize(); +} + +// <Extrusion +// DEF="" ID +// USE="" IDREF +// beginCap="true" SFBool [initializeOnly] +// ccw="true" SFBool [initializeOnly] +// convex="true" SFBool [initializeOnly] +// creaseAngle="0.0" SFloat [initializeOnly] +// crossSection="1 1 1 -1 -1 -1 -1 1 1 1" MFVec2f [initializeOnly] +// endCap="true" SFBool [initializeOnly] +// orientation="0 0 1 0" MFRotation [initializeOnly] +// scale="1 1" MFVec2f [initializeOnly] +// solid="true" SFBool [initializeOnly] +// spine="0 0 0 0 1 0" MFVec3f [initializeOnly] +// /> +void X3DImporter::readExtrusion(XmlNode &node) { + std::string use, def; + bool beginCap = true; + bool ccw = true; + bool convex = true; + float creaseAngle = 0; + std::vector<aiVector2D> crossSection; + bool endCap = true; + std::vector<float> orientation; + std::vector<aiVector2D> scale; + bool solid = true; + std::vector<aiVector3D> spine; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "beginCap", beginCap); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "convex", convex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getVector2DArrayAttribute(node, "crossSection", crossSection); + XmlParser::getBoolAttribute(node, "endCap", endCap); + X3DXmlHelper::getFloatArrayAttribute(node, "orientation", orientation); + X3DXmlHelper::getVector2DArrayAttribute(node, "scale", scale); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getVector3DArrayAttribute(node, "spine", spine); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Extrusion, ne); + } else { + // + // check if default values must be assigned + // + if (spine.size() == 0) { + spine.resize(2); + spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); + } else if (spine.size() == 1) { + throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); + } + + if (crossSection.size() == 0) { + crossSection.resize(5); + crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); + } + + { // orientation + size_t ori_size = orientation.size() / 4; + + if (ori_size < spine.size()) { + float add_ori[4]; // values that will be added + + if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. + { + add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; + } else // else - use default values + { + add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; + } + + orientation.reserve(spine.size() * 4); + for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) + orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); + } + + if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in <Extrusion> must has multiple four quantity of numbers."); + } // END: orientation + + { // scale + if (scale.size() < spine.size()) { + aiVector2D add_sc; + + if (scale.size() == 1) // if "scale" has one element then use it value for all spine points. + add_sc = scale[0]; + else // else - use default values + add_sc.Set(1, 1); + + scale.reserve(spine.size()); + for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) + scale.push_back(add_sc); + } + } // END: scale + // + // create and if needed - define new geometry object. + // + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_Extrusion, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ext_alias = *((X3DNodeElementIndexedSet *)ne); // create alias for conveience + // assign part of input data + ext_alias.CCW = ccw; + ext_alias.Convex = convex; + ext_alias.CreaseAngle = creaseAngle; + ext_alias.Solid = solid; + + // + // How we done it at all? + // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector + // are applied vor every basis. + // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position + // using relative spine point. + // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if + // needed. While createing CootdIdx is taking in account CCW flag. + // 4. The last step: create Vertices list. + // + bool spine_closed; // flag: true if spine curve is closed. + bool cross_closed; // flag: true if cross curve is closed. + std::vector<aiMatrix3x3> basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. + std::vector<std::vector<aiVector3D>> pointset_arr; // array of point sets: cross curves. + + // detect closed curves + GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end. + GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end. + // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. + if (spine_closed) { + beginCap |= endCap; + endCap = false; + } + + { // 1. Calculate array of basises. + aiMatrix4x4 rotmat; + aiVector3D vecX(0), vecY(0), vecZ(0); + + basis_arr.resize(spine.size()); + for (size_t i = 0, i_e = spine.size(); i < i_e; i++) { + aiVector3D tvec; + + // get axises of basis. + vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); + vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); + vecX = (vecY ^ vecZ).Normalize(); + // get rotation matrix and apply "orientation" to basis + aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); + tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; + tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; + tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; + } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + } // END: 1. Calculate array of basises + + { // 2. Create array of point sets. + aiMatrix4x4 scmat; + std::vector<aiVector3D> tcross(crossSection.size()); + + pointset_arr.resize(spine.size()); + for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { + aiVector3D tc23vec; + + tc23vec.Set(scale[spi].x, 0, scale[spi].y); + aiMatrix4x4::Scaling(tc23vec, scmat); + for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { + aiVector3D tvecX, tvecY, tvecZ; + + tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); + // apply scaling to point + tcross[cri] = scmat * tc23vec; + // + // transfer point to new basis + // calculate coordinate in new basis + tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; + tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; + tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; + // apply new coordinates and translate it to spine point. + tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; + } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) + + pointset_arr[spi] = tcross; // store transferred point set + } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) + } // END: 2. Create array of point sets. + + { // 3. Create CoordIdx. + // add caps if needed + if (beginCap) { + // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. + for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++) + ext_alias.CoordIndex.push_back(static_cast<int32_t>(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } // if(beginCap) + + if (endCap) { + // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. + size_t beg = (pointset_arr.size() - 1) * crossSection.size(); + + for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) + ext_alias.CoordIndex.push_back(static_cast<int32_t>(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } // if(beginCap) + + // add quads + for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) { + const size_t cr_sz = crossSection.size(); + const size_t cr_last = crossSection.size() - 1; + + size_t right_col; // hold index basis for points of quad placed in right column; + + if (spi != spi_e) + right_col = spi + 1; + else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve. + right_col = 0; + else + break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine. + + for (size_t cri = 0; cri < cr_sz; cri++) { + if (cri != cr_last) { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast<int32_t>(spi * cr_sz + cri), + static_cast<int32_t>(right_col * cr_sz + cri), + static_cast<int32_t>(right_col * cr_sz + cri + 1), + static_cast<int32_t>(spi * cr_sz + cri + 1)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve. + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast<int32_t>(spi * cr_sz + cri), + static_cast<int32_t>(right_col * cr_sz + cri), + static_cast<int32_t>(right_col * cr_sz + 0), + static_cast<int32_t>(spi * cr_sz + 0)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + } // for(size_t cri = 0; cri < cr_sz; cri++) + } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) + } // END: 3. Create CoordIdx. + + { // 4. Create vertices list. + // just copy all vertices + for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { + for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { + ext_alias.Vertices.emplace_back(pointset_arr[spi][cri]); + } + } + } // END: 4. Create vertices list. + //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); + //PrintVectorSet("Ext. Vertices", ext_alias.Vertices); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Extrusion"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <IndexedFaceSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorIndex="" MFInt32 [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// convex="true" SFBool [initializeOnly] +// coordIndex="" MFInt32 [initializeOnly] +// creaseAngle="0" SFFloat [initializeOnly] +// normalIndex="" MFInt32 [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// texCoordIndex="" MFInt32 [initializeOnly] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </IndexedFaceSet> +void X3DImporter::readIndexedFaceSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + std::vector<int32_t> colorIndex; + bool colorPerVertex = true; + bool convex = true; + std::vector<int32_t> coordIndex; + float creaseAngle = 0; + std::vector<int32_t> normalIndex; + bool normalPerVertex = true; + bool solid = true; + std::vector<int32_t> texCoordIndex; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "convex", convex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getInt32ArrayAttribute(node, "normalIndex", normalIndex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getInt32ArrayAttribute(node, "texCoordIndex", texCoordIndex); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedFaceSet, ne); + } else { + // check data + if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedFaceSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.Convex = convex; + ne_alias.CoordIndex = coordIndex; + ne_alias.CreaseAngle = creaseAngle; + ne_alias.NormalIndex = normalIndex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + ne_alias.TexCoordIndex = texCoordIndex; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedFaceSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Sphere +// DEF="" ID +// USE="" IDREF +// radius="1" SFloat [initializeOnly] +// solid="true" SFBool [initializeOnly] +// /> +void X3DImporter::readSphere(XmlNode &node) { + std::string use, def; + ai_real radius = 1; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getRealAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Sphere, ne); + } else { + const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property + + std::vector<aiVector3D> tlist; + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Sphere, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + StandardShapes::MakeSphere(tess, tlist); + // copy data from temp array and apply scale + for (std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it) { + aiVector3D v = *it; + ((X3DNodeElementGeometry3D *)ne)->Vertices.emplace_back(v * radius); + } + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Sphere"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Group.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Group.cpp new file mode 100644 index 0000000..e7a2e91 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Group.cpp @@ -0,0 +1,277 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Group.cpp +/// \brief Parsing data from nodes of "Grouping" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// <Group +// DEF="" ID +// USE="" IDREF +// bboxCenter="0 0 0" SFVec3f [initializeOnly] +// bboxSize="-1 -1 -1" SFVec3f [initializeOnly] +// > +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </Group> +// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform. +void X3DImporter::startReadGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// <StaticGroup +// DEF="" ID +// USE="" IDREF +// bboxCenter="0 0 0" SFVec3f [initializeOnly] +// bboxSize="-1 -1 -1" SFVec3f [initializeOnly] +// > +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </StaticGroup> +// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or +// contain any USE references outside the StaticGroup. +void X3DImporter::startReadStaticGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadStaticGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// <Switch +// DEF="" ID +// USE="" IDREF +// bboxCenter="0 0 0" SFVec3f [initializeOnly] +// bboxSize="-1 -1 -1" SFVec3f [initializeOnly] +// whichChoice="-1" SFInt32 [inputOutput] +// > +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </Switch> +// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child +// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing +// is chosen. +void X3DImporter::startReadSwitch(XmlNode &node) { + std::string def, use; + int32_t whichChoice = -1; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getIntAttribute(node, "whichChoice", whichChoice); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + // also set values specific to this type of group + ((X3DNodeElementGroup *)mNodeElementCur)->UseChoice = true; + ((X3DNodeElementGroup *)mNodeElementCur)->Choice = whichChoice; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadSwitch() { + // just exit from node. Defined choice will be accepted at postprocessing stage. + ParseHelper_Node_Exit(); // go up in scene graph +} + +// <Transform +// DEF="" ID +// USE="" IDREF +// bboxCenter="0 0 0" SFVec3f [initializeOnly] +// bboxSize="-1 -1 -1" SFVec3f [initializeOnly] +// center="0 0 0" SFVec3f [inputOutput] +// rotation="0 0 1 0" SFRotation [inputOutput] +// scale="1 1 1" SFVec3f [inputOutput] +// scaleOrientation="0 0 1 0" SFRotation [inputOutput] +// translation="0 0 0" SFVec3f [inputOutput] +// > +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </Transform> +// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. +// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate +// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the +// equivalent transformation matrices, +// P' = T * C * R * SR * S * -SR * -C * P +void X3DImporter::startReadTransform(XmlNode &node) { + aiVector3D center(0, 0, 0); + float rotation[4] = { 0, 0, 1, 0 }; + aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed + float scale_orientation[4] = { 0, 0, 1, 0 }; + aiVector3D translation(0, 0, 0); + aiMatrix4x4 matr, tmatr; + std::string use, def; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "center", center); + X3DXmlHelper::getVector3DAttribute(node, "scale", scale); + X3DXmlHelper::getVector3DAttribute(node, "translation", translation); + std::vector<float> tvec; + if (X3DXmlHelper::getFloatArrayAttribute(node, "rotation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError("<Transform>: rotation vector must have 4 elements."); + memcpy(rotation, tvec.data(), sizeof(rotation)); + tvec.clear(); + } + if (X3DXmlHelper::getFloatArrayAttribute(node, "scaleOrientation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError("<Transform>: scaleOrientation vector must have 4 elements."); + memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + tvec.clear(); + } + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + bool newgroup = (nullptr == mNodeElementCur); + if(newgroup) + ParseHelper_Group_Begin(); + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + if (newgroup && isNodeEmpty(node)) { + ParseHelper_Node_Exit(); + } + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) { + mNodeElementCur->ID = def; + } + + // + // also set values specific to this type of group + // + // calculate transformation matrix + aiMatrix4x4::Translation(translation, matr); // T + aiMatrix4x4::Translation(center, tmatr); // C + matr *= tmatr; + aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R + matr *= tmatr; + aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR + matr *= tmatr; + aiMatrix4x4::Scaling(scale, tmatr); // S + matr *= tmatr; + aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR + matr *= tmatr; + aiMatrix4x4::Translation(-center, tmatr); // -C + matr *= tmatr; + // and assign it + ((X3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) { + ParseHelper_Node_Exit(); + } + } // if(!use.empty()) else +} + +void X3DImporter::endReadTransform() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Light.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Light.cpp new file mode 100644 index 0000000..f1ed5e4 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Light.cpp @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Light.cpp +/// \brief Parsing data from nodes of "Lighting" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include <assimp/StringUtils.h> + +namespace Assimp { + +// <DirectionalLight +// DEF="" ID +// USE="" IDREF +// ambientIntensity="0" SFFloat [inputOutput] +// color="1 1 1" SFColor [inputOutput] +// direction="0 0 -1" SFVec3f [inputOutput] +// global="false" SFBool [inputOutput] +// intensity="1" SFFloat [inputOutput] +// on="true" SFBool [inputOutput] +// /> +void X3DImporter::readDirectionalLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiColor3D color(1, 1, 1); + aiVector3D direction(0, 0, -1); + bool global = false; + float intensity = 1; + bool on = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + XmlParser::getBoolAttribute(node, "on", on); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_DirectionalLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_DirectionalLight, mNodeElementCur); + if (!def.empty()) + ne->ID = def; + else + ne->ID = "DirectionalLight_" + ai_to_string((size_t)ne); // make random name + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "DirectionalLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// <PointLight +// DEF="" ID +// USE="" IDREF +// ambientIntensity="0" SFFloat [inputOutput] +// attenuation="1 0 0" SFVec3f [inputOutput] +// color="1 1 1" SFColor [inputOutput] +// global="true" SFBool [inputOutput] +// intensity="1" SFFloat [inputOutput] +// location="0 0 0" SFVec3f [inputOutput] +// on="true" SFBool [inputOutput] +// radius="100" SFFloat [inputOutput] +// /> +void X3DImporter::readPointLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + aiColor3D color(1, 1, 1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "PointLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "PointLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// <SpotLight +// DEF="" ID +// USE="" IDREF +// ambientIntensity="0" SFFloat [inputOutput] +// attenuation="1 0 0" SFVec3f [inputOutput] +// beamWidth="0.7854" SFFloat [inputOutput] +// color="1 1 1" SFColor [inputOutput] +// cutOffAngle="1.570796" SFFloat [inputOutput] +// direction="0 0 -1" SFVec3f [inputOutput] +// global="true" SFBool [inputOutput] +// intensity="1" SFFloat [inputOutput] +// location="0 0 0" SFVec3f [inputOutput] +// on="true" SFBool [inputOutput] +// radius="100" SFFloat [inputOutput] +// /> +void X3DImporter::readSpotLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + float beamWidth = 0.7854f; + aiColor3D color(1, 1, 1); + float cutOffAngle = 1.570796f; + aiVector3D direction(0, 0, -1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + XmlParser::getFloatAttribute(node, "beamWidth", beamWidth); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getFloatAttribute(node, "cutOffAngle", cutOffAngle); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_SpotLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + if (beamWidth > cutOffAngle) beamWidth = cutOffAngle; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->BeamWidth = beamWidth; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->CutOffAngle = cutOffAngle; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "SpotLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "SpotLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Macro.hpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Macro.hpp new file mode 100644 index 0000000..a84a739 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Macro.hpp @@ -0,0 +1,121 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Macro.hpp +/// \brief Useful macrodefines. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED +#define X3DIMPORTER_MACRO_HPP_INCLUDED + +#include <assimp/XmlParser.h> +#include "X3DImporter.hpp" +#include <string> + +namespace Assimp { + +/// Used for regular checking while attribute "USE" is defined. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF - string holding "DEF" value. +/// \param [in] pUSE - string holding "USE" value. +/// \param [in] pType - type of element to find. +/// \param [out] pNE - pointer to found node element. +inline X3DNodeElementBase *X3DImporter::MACRO_USE_CHECKANDAPPLY(XmlNode &node, std::string pDEF, std::string pUSE, X3DElemType pType, X3DNodeElementBase *pNE) { + checkNodeMustBeEmpty(node); + if (!pDEF.empty()) + Assimp::Throw_DEF_And_USE(node.name()); + if (!FindNodeElement(pUSE, pType, &pNE)) + Assimp::Throw_USE_NotFound(node.name(), pUSE); + ai_assert(nullptr != mNodeElementCur); + mNodeElementCur->Children.push_back(pNE); /* add found object as child to current element */ + + return pNE; +} + +} // namespace Assimp + +/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET +/// Compact variant for checking "USE" and "DEF". +/// \param [in] pNode - pugi xml node to read. +/// \param [out] pDEF_Var - output variable name for "DEF" value. +/// \param [out] pUSE_Var - output variable name for "USE" value. +#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pNode, pDEF_Var, pUSE_Var) \ + do { \ + XmlParser::getStdStrAttribute(pNode, "DEF", pDEF_Var); \ + XmlParser::getStdStrAttribute(pNode, "USE", pUSE_Var); \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pP1); \ + pOut.push_back(pP2); \ + pOut.push_back(pP3); \ + pOut.push_back(pP4); \ + } else { \ + pOut.push_back(pP4); \ + pOut.push_back(pP3); \ + pOut.push_back(pP2); \ + pOut.push_back(pP1); \ + } \ + } while (false) + +#endif // X3DIMPORTER_MACRO_HPP_INCLUDED diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Metadata.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Metadata.cpp new file mode 100644 index 0000000..8e07d8b --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Metadata.cpp @@ -0,0 +1,255 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Metadata.cpp +/// \brief Parsing data from nodes of "Metadata" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +bool X3DImporter::checkForMetadataNode(XmlNode &node) { + const std::string &name = node.name(); + if (name == "MetadataBoolean") { + readMetadataBoolean(node); + } else if (name == "MetadataDouble") { + readMetadataDouble(node); + } else if (name == "MetadataFloat") { + readMetadataFloat(node); + } else if (name == "MetadataInteger") { + readMetadataInteger(node); + } else if (name == "MetadataSet") { + readMetadataSet(node); + } else if (name == "MetadataString") { + readMetadataString(node); + } else + return false; + return true; +} + +void X3DImporter::childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName) { + ParseHelper_Node_Enter(pParentElement); + for (auto childNode : node.children()) { + if (!checkForMetadataNode(childNode)) skipUnsupportedNode(pNodeName, childNode); + } + ParseHelper_Node_Exit(); +} + +/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName) +/// Find element by "USE" or create new one. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF_Var - variable name with "DEF" value. +/// \param [in] pUSE_Var - variable name with "USE" value. +/// \param [in] pReference - variable name with "reference" value. +/// \param [in] pValue - variable name with "value" value. +/// \param [in, out] pNE - pointer to node element. +/// \param [in] pMetaClass - Class of node. +/// \param [in] pMetaName - Name of node. +/// \param [in] pType - type of element to find. +#define MACRO_METADATA_FINDCREATE(pNode, pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType) \ + /* if "USE" defined then find already defined element. */ \ + if (!pUSE_Var.empty()) { \ + ne = MACRO_USE_CHECKANDAPPLY(pNode, pDEF_Var, pUSE_Var, pType, pNE); \ + } else { \ + pNE = new pMetaClass(mNodeElementCur); \ + if (!pDEF_Var.empty()) pNE->ID = pDEF_Var; \ + \ + ((pMetaClass *)pNE)->Reference = pReference; \ + ((pMetaClass *)pNE)->Value = pValue; \ + /* also metadata node can contain childs */ \ + if (!isNodeEmpty(pNode)) \ + childrenReadMetadata(pNode, pNE, pMetaName); /* in that case node element will be added to child elements list of current node. */ \ + else \ + mNodeElementCur->Children.push_back(pNE); /* else - add element to child list manually */ \ + \ + NodeElement_List.push_back(pNE); /* add new element to elements list. */ \ + } /* if(!pUSE_Var.empty()) else */ \ + \ + do { \ + } while (false) + +// <MetadataBoolean +// DEF="" ID +// USE="" IDREF +// name="" SFString [inputOutput] +// reference="" SFString [inputOutput] +// value="" MFBool [inputOutput] +// /> +void X3DImporter::readMetadataBoolean(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector<bool> value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getBooleanArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaBoolean, "MetadataBoolean", ENET_MetaBoolean); +} + +// <MetadataDouble +// DEF="" ID +// USE="" IDREF +// name="" SFString [inputOutput] +// reference="" SFString [inputOutput] +// value="" MFDouble [inputOutput] +// /> +void X3DImporter::readMetadataDouble(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector<double> value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getDoubleArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaDouble, "MetadataDouble", ENET_MetaDouble); +} + +// <MetadataFloat +// DEF="" ID +// USE="" IDREF +// name="" SFString [inputOutput] +// reference="" SFString [inputOutput] +// value="" MFFloat [inputOutput] +// /> +void X3DImporter::readMetadataFloat(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector<float> value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getFloatArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaFloat, "MetadataFloat", ENET_MetaFloat); +} + +// <MetadataInteger +// DEF="" ID +// USE="" IDREF +// name="" SFString [inputOutput] +// reference="" SFString [inputOutput] +// value="" MFInteger [inputOutput] +// /> +void X3DImporter::readMetadataInteger(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector<int32_t> value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getInt32ArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaInt, "MetadataInteger", ENET_MetaInteger); +} + +// <MetadataSet +// DEF="" ID +// USE="" IDREF +// name="" SFString [inputOutput] +// reference="" SFString [inputOutput] +// /> +void X3DImporter::readMetadataSet(XmlNode &node) { + std::string def, use; + std::string name, reference; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_MetaSet, ne); + } else { + ne = new X3DNodeElementMetaSet(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementMetaSet *)ne)->Reference = reference; + // also metadata node can contain children + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "MetadataSet"); + else + mNodeElementCur->Children.push_back(ne); // made object as child to current element + + NodeElement_List.push_back(ne); // add new element to elements list. + } // if(!use.empty()) else +} + +// <MetadataString +// DEF="" ID +// USE="" IDREF +// name="" SFString [inputOutput] +// reference="" SFString [inputOutput] +// value="" MFString [inputOutput] +// /> +void X3DImporter::readMetadataString(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector<std::string> value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getStringArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaString, "MetadataString", ENET_MetaString); +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Networking.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Networking.cpp new file mode 100644 index 0000000..f2b4716 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Networking.cpp @@ -0,0 +1,124 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Networking.cpp +/// \brief Parsing data from nodes of "Networking" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +// Header files, Assimp. +#include <assimp/DefaultIOSystem.h> + +//#include <regex> + +namespace Assimp { + +//static std::regex pattern_parentDir(R"((^|/)[^/]+/../)"); +static std::string parentDir("/../"); + +// <Inline +// DEF="" ID +// USE="" IDREF +// bboxCenter="0 0 0" SFVec3f [initializeOnly] +// bboxSize="-1 -1 -1" SFVec3f [initializeOnly] +// load="true" SFBool [inputOutput] +// url="" MFString [inputOutput] +// /> +void X3DImporter::readInline(XmlNode &node) { + std::string def, use; + bool load = true; + std::list<std::string> url; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "load", load); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + X3DNodeElementBase *ne = nullptr; + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + if (load && !url.empty()) { + std::string full_path = mpIOHandler->CurrentDirectory() + url.front(); + + //full_path = std::regex_replace(full_path, pattern_parentDir, "$1"); + for (std::string::size_type pos = full_path.find(parentDir); pos != std::string::npos; pos = full_path.find(parentDir, pos)) { + if (pos > 0) { + std::string::size_type pos2 = full_path.rfind('/', pos - 1); + if (pos2 != std::string::npos) { + full_path.erase(pos2, pos - pos2 + 3); + pos = pos2; + } else { + full_path.erase(0, pos + 4); + pos = 0; + } + } else { + pos += 3; + } + } + // Attribute "url" can contain list of strings. But we need only one - first. + std::string::size_type slashPos = full_path.find_last_of("\\/"); + mpIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : full_path.substr(0, slashPos + 1)); + ParseFile(full_path, mpIOHandler); + mpIOHandler->PopDirectory(); + } + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) childrenReadMetadata(node, mNodeElementCur, "Inline"); + + // exit from node in that place + ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Node.hpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Node.hpp new file mode 100644 index 0000000..8d33c4b --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Node.hpp @@ -0,0 +1,463 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Node.hpp +/// \brief Elements of scene graph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H +#define INCLUDED_AI_X3D_IMPORTER_NODE_H + +// Header files, Assimp. +#include <assimp/types.h> + +#include <list> +#include <vector> + +enum X3DElemType { + ENET_Group, ///< Element has type "Group". + ENET_MetaBoolean, ///< Element has type "Metadata boolean". + ENET_MetaDouble, ///< Element has type "Metadata double". + ENET_MetaFloat, ///< Element has type "Metadata float". + ENET_MetaInteger, ///< Element has type "Metadata integer". + ENET_MetaSet, ///< Element has type "Metadata set". + ENET_MetaString, ///< Element has type "Metadata string". + ENET_Arc2D, ///< Element has type "Arc2D". + ENET_ArcClose2D, ///< Element has type "ArcClose2D". + ENET_Circle2D, ///< Element has type "Circle2D". + ENET_Disk2D, ///< Element has type "Disk2D". + ENET_Polyline2D, ///< Element has type "Polyline2D". + ENET_Polypoint2D, ///< Element has type "Polypoint2D". + ENET_Rectangle2D, ///< Element has type "Rectangle2D". + ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". + ENET_Box, ///< Element has type "Box". + ENET_Cone, ///< Element has type "Cone". + ENET_Cylinder, ///< Element has type "Cylinder". + ENET_Sphere, ///< Element has type "Sphere". + ENET_ElevationGrid, ///< Element has type "ElevationGrid". + ENET_Extrusion, ///< Element has type "Extrusion". + ENET_Coordinate, ///< Element has type "Coordinate". + ENET_Normal, ///< Element has type "Normal". + ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". + ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". + ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". + ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". + ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". + ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". + ENET_LineSet, ///< Element has type "LineSet". + ENET_PointSet, ///< Element has type "PointSet". + ENET_TriangleSet, ///< Element has type "TriangleSet". + ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". + ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". + ENET_Color, ///< Element has type "Color". + ENET_ColorRGBA, ///< Element has type "ColorRGBA". + ENET_Shape, ///< Element has type "Shape". + ENET_Appearance, ///< Element has type "Appearance". + ENET_Material, ///< Element has type "Material". + ENET_ImageTexture, ///< Element has type "ImageTexture". + ENET_TextureTransform, ///< Element has type "TextureTransform". + ENET_DirectionalLight, ///< Element has type "DirectionalLight". + ENET_PointLight, ///< Element has type "PointLight". + ENET_SpotLight, ///< Element has type "SpotLight". + + ENET_Invalid ///< Element has invalid type and possible contain invalid data. +}; + +struct X3DNodeElementBase { + X3DNodeElementBase *Parent; + std::string ID; + std::list<X3DNodeElementBase *> Children; + X3DElemType Type; + + virtual ~X3DNodeElementBase() { + // empty + } + +protected: + X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : + Parent(pParent), Type(type) { + // empty + } +}; + +/// This struct hold <Color> value. +struct X3DNodeElementColor : X3DNodeElementBase { + std::list<aiColor3D> Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColor(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} + +}; // struct X3DNodeElementColor + +/// This struct hold <ColorRGBA> value. +struct X3DNodeElementColorRGBA : X3DNodeElementBase { + std::list<aiColor4D> Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColorRGBA(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} + +}; // struct X3DNodeElementColorRGBA + +/// This struct hold <Coordinate> value. +struct X3DNodeElementCoordinate : public X3DNodeElementBase { + std::list<aiVector3D> Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} + +}; // struct X3DNodeElementCoordinate + +/// This struct hold <Normal> value. +struct X3DNodeElementNormal : X3DNodeElementBase { + std::list<aiVector3D> Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementNormal(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} + +}; // struct X3DNodeElementNormal + +/// This struct hold <TextureCoordinate> value. +struct X3DNodeElementTextureCoordinate : X3DNodeElementBase { + std::list<aiVector2D> Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} + +}; // struct X3DNodeElementTextureCoordinate + +/// Two-dimensional figure. +struct X3DNodeElementGeometry2D : X3DNodeElementBase { + std::list<aiVector3D> Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Solid(true) {} + +}; // class X3DNodeElementGeometry2D + +/// Three-dimensional body. +struct X3DNodeElementGeometry3D : X3DNodeElementBase { + std::list<aiVector3D> Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { + // empty + } +}; // class X3DNodeElementGeometry3D + +/// Uniform rectangular grid of varying height. +struct X3DNodeElementElevationGrid : X3DNodeElementGeometry3D { + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector<int32_t> CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with indexed vertices. +struct X3DNodeElementIndexedSet : public X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + std::vector<int32_t> ColorIndex; ///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>. + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, + /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results + /// even if the convex field is FALSE. + bool Convex; + std::vector<int32_t> CoordIndex; ///< Field to specify the polygonal faces by indexing into the <Coordinate>. + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector<int32_t> NormalIndex; ///< Field to specify the polygonal faces by indexing into the <Normal>. + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector<int32_t> TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementIndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with set of vertices. +struct X3DNodeElementSet : X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector<int32_t> CoordIndex; ///< Field to specify the polygonal faces by indexing into the <Coordinate>. + std::vector<int32_t> NormalIndex; ///< Field to specify the polygonal faces by indexing into the <Normal>. + std::vector<int32_t> TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>. + std::vector<int32_t> VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementSet(X3DElemType type, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(type, pParent) {} + +}; // class X3DNodeElementSet + +/// This struct hold <Shape> value. +struct X3DNodeElementShape : X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementShape(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} +}; // struct X3DNodeElementShape + +/// This struct hold <Appearance> value. +struct X3DNodeElementAppearance : public X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementAppearance(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} + +}; // struct X3DNodeElementAppearance + +struct X3DNodeElementMaterial : public X3DNodeElementBase { + float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. + aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. + aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. + float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. + aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. + float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementMaterial(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Material, pParent), + AmbientIntensity(0.0f), + DiffuseColor(), + EmissiveColor(), + Shininess(0.0f), + SpecularColor(), + Transparency(1.0f) { + // empty + } +}; // class X3DNodeElementMaterial + +/// This struct hold <ImageTexture> value. +struct X3DNodeElementImageTexture : X3DNodeElementBase { + /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated + /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are + /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. + bool RepeatS; + bool RepeatT; ///< See \ref RepeatS. + std::string URL; ///< URL of the texture. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementImageTexture(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} + +}; // struct X3DNodeElementImageTexture + +/// This struct hold <TextureTransform> value. +struct X3DNodeElementTextureTransform : X3DNodeElementBase { + aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. + float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. + aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. + aiVector2D Translation; ///< Specifies a translation of the texture coordinates. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureTransform(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} + +}; // struct X3DNodeElementTextureTransform + +struct X3DNodeElementGroup : X3DNodeElementBase { + aiMatrix4x4 Transformation; ///< Transformation matrix. + + /// As you know node elements can use already defined node elements when attribute "USE" is defined. + /// Standard search when looking for an element in the whole scene graph, existing at this moment. + /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. + bool Static; + + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pStatic - static node flag. + X3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : + X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} +}; + +struct X3DNodeElementMeta : X3DNodeElementBase { + std::string Name; ///< Name of metadata object. + std::string Reference; + + virtual ~X3DNodeElementMeta() { + // empty + } + +protected: + X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : + X3DNodeElementBase(type, parent) { + // empty + } +}; + +struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { + std::vector<bool> Value; ///< Stored value. + + explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaDouble : X3DNodeElementMeta { + std::vector<double> Value; ///< Stored value. + + explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { + std::vector<float> Value; ///< Stored value. + + explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaInt : public X3DNodeElementMeta { + std::vector<int32_t> Value; ///< Stored value. + + explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaSet : public X3DNodeElementMeta { + std::list<X3DNodeElementMeta> Value; ///< Stored value. + + explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaString : X3DNodeElementMeta { + std::vector<std::string> Value; ///< Stored value. + + explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { + // empty + } +}; + +/// \struct X3DNodeElementLight +/// This struct hold <TextureTransform> value. +struct X3DNodeElementLight : X3DNodeElementBase { + float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. + aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. + aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. + /// \var Global + /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. + /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. + bool Global; + float Intensity; ///< Specifies the brightness of the direct emission from the light. + /// \var Attenuation + /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor + /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. + aiVector3D Attenuation; + aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. + float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. + float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. + float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). + + /// Constructor + /// \param [in] pParent - pointer to parent node. + /// \param [in] pLightType - type of the light source. + X3DNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pLightType, pParent) {} + +}; // struct X3DNodeElementLight + +#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Postprocess.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Postprocess.cpp new file mode 100644 index 0000000..87121ef --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Postprocess.cpp @@ -0,0 +1,731 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Postprocess.cpp +/// \brief Convert built scenegraph and objects to Assimp scenegraph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +// Header files, Assimp. +#include <assimp/StandardShapes.h> +#include <assimp/StringUtils.h> +#include <assimp/ai_assert.h> + +// Header files, stdlib. +#include <algorithm> +#include <iterator> +#include <string> + +namespace Assimp { + +aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const { + X3DNodeElementBase *cur_node; + std::list<aiMatrix4x4> matr; + aiMatrix4x4 out_matr; + + // starting walk from current element to root + cur_node = mNodeElementCur; + if (cur_node != nullptr) { + do { + // if cur_node is group then store group transformation matrix in list. + if (cur_node->Type == X3DElemType::ENET_Group) matr.push_back(((X3DNodeElementGroup *)cur_node)->Transformation); + + cur_node = cur_node->Parent; + } 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 X3DImporter::PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list<X3DNodeElementBase *> &pList) const { + // walk through childs and find for metadata. + for (std::list<X3DNodeElementBase *>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || + ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || + ((*el_it)->Type == X3DElemType::ENET_MetaString)) { + pList.push_back(*el_it); + } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { + PostprocessHelper_CollectMetadata(**el_it, pList); + } + } // for(std::list<X3DNodeElementBase*>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +bool X3DImporter::PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || + (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || + (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { + return true; + } else { + return false; + } +} + +bool X3DImporter::PostprocessHelper_ElementIsMesh(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || + (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || + (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || + (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || + (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || + (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || + (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || + (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || + (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || + (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || + (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || + (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { + return true; + } else { + return false; + } +} + +void X3DImporter::Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list<aiLight *> &pSceneLightList) const { + const X3DNodeElementLight &ne = *((X3DNodeElementLight *)&pNodeElement); + aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); + aiLight *new_light = new aiLight; + + new_light->mName = ne.ID; + new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; + new_light->mColorDiffuse = ne.Color * ne.Intensity; + new_light->mColorSpecular = ne.Color * ne.Intensity; + switch (pNodeElement.Type) { + case X3DElemType::ENET_DirectionalLight: + new_light->mType = aiLightSource_DIRECTIONAL; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + + break; + case X3DElemType::ENET_PointLight: + new_light->mType = aiLightSource_POINT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + + break; + case X3DElemType::ENET_SpotLight: + new_light->mType = aiLightSource_SPOT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + new_light->mAngleInnerCone = ne.BeamWidth; + new_light->mAngleOuterCone = ne.CutOffAngle; + + break; + default: + throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + ai_to_string(pNodeElement.Type) + "."); + } + + pSceneLightList.push_back(new_light); +} + +void X3DImporter::Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const { + // check argument + if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); + if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); + + *pMaterial = new aiMaterial; + aiMaterial &taimat = **pMaterial; // creating alias for convenience. + + // at this point pNodeElement point to <Appearance> node. Walk through childs and add all stored data. + for (std::list<X3DNodeElementBase *>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if ((*el_it)->Type == X3DElemType::ENET_Material) { + aiColor3D tcol3; + float tvalf; + X3DNodeElementMaterial &tnemat = *((X3DNodeElementMaterial *)*el_it); + + tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; + taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); + taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); + tvalf = 1; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); + taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); + tvalf = 1.0f - tnemat.Transparency; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); + } // if((*el_it)->Type == X3DElemType::ENET_Material) + else if ((*el_it)->Type == X3DElemType::ENET_ImageTexture) { + X3DNodeElementImageTexture &tnetex = *((X3DNodeElementImageTexture *)*el_it); + aiString url_str(tnetex.URL.c_str()); + int mode = aiTextureOp_Multiply; + + taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); + taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_ImageTexture) + else if ((*el_it)->Type == X3DElemType::ENET_TextureTransform) { + aiUVTransform trans; + X3DNodeElementTextureTransform &tnetextr = *((X3DNodeElementTextureTransform *)*el_it); + + trans.mTranslation = tnetextr.Translation - tnetextr.Center; + trans.mScaling = tnetextr.Scale; + trans.mRotation = tnetextr.Rotation; + taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_TextureTransform) + } // for(std::list<X3DNodeElementBase*>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +void X3DImporter::Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const { + // check argument + if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); + if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); + + /************************************************************************************************************************************/ + /************************************************************ Geometry2D ************************************************************/ + /************************************************************************************************************************************/ + if ((pNodeElement.Type == X3DElemType::ENET_Arc2D) || (pNodeElement.Type == X3DElemType::ENET_ArcClose2D) || + (pNodeElement.Type == X3DElemType::ENET_Circle2D) || (pNodeElement.Type == X3DElemType::ENET_Disk2D) || + (pNodeElement.Type == X3DElemType::ENET_Polyline2D) || (pNodeElement.Type == X3DElemType::ENET_Polypoint2D) || + (pNodeElement.Type == X3DElemType::ENET_Rectangle2D) || (pNodeElement.Type == X3DElemType::ENET_TriangleSet2D)) { + X3DNodeElementGeometry2D &tnemesh = *((X3DNodeElementGeometry2D *)&pNodeElement); // create alias for convenience + std::vector<aiVector3D> tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + *pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + /************************************************************************************************************************************/ + /************************************************************ Geometry3D ************************************************************/ + /************************************************************************************************************************************/ + // + // Predefined figures + // + if ((pNodeElement.Type == X3DElemType::ENET_Box) || (pNodeElement.Type == X3DElemType::ENET_Cone) || + (pNodeElement.Type == X3DElemType::ENET_Cylinder) || (pNodeElement.Type == X3DElemType::ENET_Sphere)) { + X3DNodeElementGeometry3D &tnemesh = *((X3DNodeElementGeometry3D *)&pNodeElement); // create alias for convenience + std::vector<aiVector3D> tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + + *pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + // + // Parametric figures + // + if (pNodeElement.Type == X3DElemType::ENET_ElevationGrid) { + X3DNodeElementElevationGrid &tnemesh = *((X3DNodeElementElevationGrid *)&pNodeElement); // create alias for convenience + + // at first create mesh from existing vertices. + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIdx, tnemesh.Vertices); + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, ((X3DNodeElementNormal *)*ch_it)->Value, tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_ElevationGrid) + // + // Indexed primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) + + if (pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) + + if ((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ + IndexedTriangleStripSet: " + + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) + + if (pNodeElement.Type == X3DElemType::ENET_Extrusion) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, tnemesh.Vertices); + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_Extrusion) + + // + // Primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_PointSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector<aiVector3D> vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list<aiVector3D>::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 1); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_PointSet) + + if (pNodeElement.Type == X3DElemType::ENET_LineSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_LineSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if (nullptr == *pMesh) { + break; + } + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector<aiVector3D> vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list<aiVector3D>::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 3); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for <Coordinate> node and create mesh. + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) + + throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + ai_to_string(pNodeElement.Type) + "."); +} + +void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list<aiMesh *> &pSceneMeshList, + std::list<aiMaterial *> &pSceneMaterialList, std::list<aiLight *> &pSceneLightList) const { + std::list<X3DNodeElementBase *>::const_iterator chit_begin = pNodeElement.Children.begin(); + std::list<X3DNodeElementBase *>::const_iterator chit_end = pNodeElement.Children.end(); + std::list<aiNode *> SceneNode_Child; + std::list<unsigned int> SceneNode_Mesh; + + // At first read all metadata + Postprocess_CollectMetadata(pNodeElement, pSceneNode); + // check if we have deal with grouping node. Which can contain transformation or switch + if (pNodeElement.Type == X3DElemType::ENET_Group) { + const X3DNodeElementGroup &tne_group = *((X3DNodeElementGroup *)&pNodeElement); // create alias for convenience + + pSceneNode.mTransformation = tne_group.Transformation; + if (tne_group.UseChoice) { + // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. + if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { + chit_begin = pNodeElement.Children.end(); + chit_end = pNodeElement.Children.end(); + } else { + for (size_t i = 0; i < (size_t)tne_group.Choice; i++) + ++chit_begin; // forward iterator to chosen node. + + chit_end = chit_begin; + ++chit_end; // point end iterator to next element after chosen node. + } + } // if(tne_group.UseChoice) + } // if(pNodeElement.Type == X3DElemType::ENET_Group) + + // Reserve memory for fast access and check children. + for (std::list<X3DNodeElementBase *>::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. + if ((*it)->Type == X3DElemType::ENET_Group) { + // if child is group then create new node and do recursive call. + aiNode *new_node = new aiNode; + + new_node->mName = (*it)->ID; + new_node->mParent = &pSceneNode; + SceneNode_Child.push_back(new_node); + Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); + } else if ((*it)->Type == X3DElemType::ENET_Shape) { + // shape can contain only one geometry and one appearance nodes. + Postprocess_BuildShape(*((X3DNodeElementShape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); + } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || + ((*it)->Type == X3DElemType::ENET_SpotLight)) { + Postprocess_BuildLight(*((X3DNodeElementLight *)*it), pSceneLightList); + } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata + { + throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + ai_to_string((*it)->Type) + "."); + } + } // for(std::list<X3DNodeElementBase*>::const_iterator it = chit_begin; it != chit_end; it++) + + // copy data about children and meshes to aiNode. + if (!SceneNode_Child.empty()) { + std::list<aiNode *>::const_iterator it = SceneNode_Child.begin(); + + pSceneNode.mNumChildren = static_cast<unsigned int>(SceneNode_Child.size()); + pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; + for (size_t i = 0; i < pSceneNode.mNumChildren; i++) + pSceneNode.mChildren[i] = *it++; + } + + if (!SceneNode_Mesh.empty()) { + std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin(); + + pSceneNode.mNumMeshes = static_cast<unsigned int>(SceneNode_Mesh.size()); + pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; + for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) + pSceneNode.mMeshes[i] = *it++; + } + + // that's all. return to previous deals +} + +void X3DImporter::Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list<unsigned int> &pNodeMeshInd, + std::list<aiMesh *> &pSceneMeshList, std::list<aiMaterial *> &pSceneMaterialList) const { + aiMaterial *tmat = nullptr; + aiMesh *tmesh = nullptr; + X3DElemType mesh_type = X3DElemType::ENET_Invalid; + unsigned int mat_ind = 0; + + for (std::list<X3DNodeElementBase *>::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { + if (PostprocessHelper_ElementIsMesh((*it)->Type)) { + Postprocess_BuildMesh(**it, &tmesh); + if (tmesh != nullptr) { + // if mesh successfully built then add data about it to arrays + pNodeMeshInd.push_back(static_cast<unsigned int>(pSceneMeshList.size())); + pSceneMeshList.push_back(tmesh); + // keep mesh type. Need above for texture coordinate generation. + mesh_type = (*it)->Type; + } + } else if ((*it)->Type == X3DElemType::ENET_Appearance) { + Postprocess_BuildMaterial(**it, &tmat); + if (tmat != nullptr) { + // if material successfully built then add data about it to array + mat_ind = static_cast<unsigned int>(pSceneMaterialList.size()); + pSceneMaterialList.push_back(tmat); + } + } + } // for(std::list<X3DNodeElementBase*>::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); it++) + + // associate read material with read mesh. + if ((tmesh != nullptr) && (tmat != nullptr)) { + tmesh->mMaterialIndex = mat_ind; + // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. + if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { + int32_t tm; + aiVector3D tvec3; + + switch (mesh_type) { + case X3DElemType::ENET_Box: + tm = aiTextureMapping_BOX; + break; + case X3DElemType::ENET_Cone: + case X3DElemType::ENET_Cylinder: + tm = aiTextureMapping_CYLINDER; + break; + case X3DElemType::ENET_Sphere: + tm = aiTextureMapping_SPHERE; + break; + default: + tm = aiTextureMapping_PLANE; + break; + } // switch(mesh_type) + + tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); + } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) + } // if((tmesh != nullptr) && (tmat != nullptr)) +} + +void X3DImporter::Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const { + std::list<X3DNodeElementBase *> meta_list; + size_t meta_idx; + + PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. + if (!meta_list.empty()) { + if (pSceneNode.mMetaData != nullptr) { + throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); + } + + // copy collected metadata to output node. + pSceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(meta_list.size())); + meta_idx = 0; + for (std::list<X3DNodeElementBase *>::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { + X3DNodeElementMeta *cur_meta = (X3DNodeElementMeta *)*it; + + // due to limitations we can add only first element of value list. + // Add an element according to its type. + if ((*it)->Type == X3DElemType::ENET_MetaBoolean) { + if (((X3DNodeElementMetaBoolean *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaBoolean *)cur_meta)->Value.begin()) == true); + } else if ((*it)->Type == X3DElemType::ENET_MetaDouble) { + if (((X3DNodeElementMetaDouble *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, (float)*(((X3DNodeElementMetaDouble *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaFloat) { + if (((X3DNodeElementMetaFloat *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaFloat *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaInteger) { + if (((X3DNodeElementMetaInt *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaInt *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaString) { + if (((X3DNodeElementMetaString *)cur_meta)->Value.size() > 0) { + aiString tstr(((X3DNodeElementMetaString *)cur_meta)->Value.begin()->data()); + + pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, tstr); + } + } else { + throw DeadlyImportError("Postprocess. Unknown metadata type."); + } // if((*it)->Type == X3DElemType::ENET_Meta*) else + } // for(std::list<X3DNodeElementBase*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) + } // if( !meta_list.empty() ) +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Rendering.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Rendering.cpp new file mode 100644 index 0000000..66a30a9 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Rendering.cpp @@ -0,0 +1,993 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Rendering.cpp +/// \brief Parsing data from nodes of "Rendering" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// <Color +// DEF="" ID +// USE="" IDREF +// color="" MFColor [inputOutput] +// /> +void X3DImporter::readColor(XmlNode &node) { + std::string use, def; + std::list<aiColor3D> color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor3DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Color, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColor(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColor *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Color"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <ColorRGBA +// DEF="" ID +// USE="" IDREF +// color="" MFColorRGBA [inputOutput] +// /> +void X3DImporter::readColorRGBA(XmlNode &node) { + std::string use, def; + std::list<aiColor4D> color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor4DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ColorRGBA, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColorRGBA(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColorRGBA *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ColorRGBA"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Coordinate +// DEF="" ID +// USE="" IDREF +// point="" MFVec3f [inputOutput] +// /> +void X3DImporter::readCoordinate(XmlNode &node) { + std::string use, def; + std::list<aiVector3D> point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Coordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Coordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <IndexedLineSet +// DEF="" ID +// USE="" IDREF +// colorIndex="" MFInt32 [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// coordIndex="" MFInt32 [initializeOnly] +// > +// <!-- ColorCoordinateContentModel --> +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </IndexedLineSet> +void X3DImporter::readIndexedLineSet(XmlNode &node) { + std::string use, def; + std::vector<int32_t> colorIndex; + bool colorPerVertex = true; + std::vector<int32_t> coordIndex; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedLineSet, ne); + } else { + // check data + if ((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3))) + throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedLineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.CoordIndex = coordIndex; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for Color and Coordinate nodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedLineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <IndexedTriangleFanSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// index="" MFInt32 [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </IndexedTriangleFanSet> +void X3DImporter::readIndexedTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector<int32_t> index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleFanSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + idx[1] = idx[2]; + } else { + idx[counter] = idx[2]; + } + ++counter; + } + } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <IndexedTriangleSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// index="" MFInt32 [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </IndexedTriangleSet> +void X3DImporter::readIndexedTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector<int32_t> index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[counter++] = *idx_it; + if (counter > 2) { + counter = 0; + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <IndexedTriangleStripSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// index="" MFInt32 [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </IndexedTriangleStripSet> +void X3DImporter::readIndexedTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector<int32_t> index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleStripSet, ne); + } else { + // check data + if (index.empty()) { + throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute."); + } + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + idx[counter & 1] = idx[2]; + ++counter; + } + } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <LineSet +// DEF="" ID +// USE="" IDREF +// vertexCount="" MFInt32 [initializeOnly] +// > +// <!-- ColorCoordinateContentModel --> +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </LineSet> +void X3DImporter::readLineSet(XmlNode &node) { + std::string use, def; + std::vector<int32_t> vertexCount; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "vertexCount", vertexCount); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_LineSet, ne); + } else { + // check data + if (vertexCount.empty()) { + throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute."); + } + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_LineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.VertexCount = vertexCount; + // create CoordIdx + size_t coord_num = 0; + + ne_alias.CoordIndex.clear(); + for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two."); + + for (int32_t i = 0; i < *vc_it; i++) + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num++)); // add vertices indices + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("LineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <PointSet +// DEF="" ID +// USE="" IDREF +// > +// <!-- ColorCoordinateContentModel --> +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </PointSet> +void X3DImporter::readPointSet(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_PointSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("PointSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <TriangleFanSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// fanCount="" MFInt32 [inputOutput] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </TriangleFanSet> +void X3DImporter::readTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector<int32_t> fanCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "fanCount", fanCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleFanSet, ne); + } else { + // check data + if (fanCount.empty()) { + throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute."); + } + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = fanCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num_first, coord_num_prev; + + ne_alias.CoordIndex.clear(); + // assign indices for first triangle + coord_num_first = 0; + coord_num_prev = 1; + for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three."); + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 2 1 + // 0 + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev++)); + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev)); + } else { + // 1 2 + // 0 + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev + 1)); + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev++)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } // for(int32_t vc = 2; vc < *vc_it; vc++) + + coord_num_prev++; // that index will be center of next fan + coord_num_first = coord_num_prev++; // forward to next point - second point of fan + } // for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <TriangleSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </TriangleSet> +void X3DImporter::readTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_TriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <TriangleStripSet +// DEF="" ID +// USE="" IDREF +// ccw="true" SFBool [initializeOnly] +// colorPerVertex="true" SFBool [initializeOnly] +// normalPerVertex="true" SFBool [initializeOnly] +// solid="true" SFBool [initializeOnly] +// stripCount="" MFInt32 [inputOutput] +// > +// <!-- ComposedGeometryContentModel --> +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// </TriangleStripSet> +void X3DImporter::readTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector<int32_t> stripCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "stripCount", stripCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleStripSet, ne); + } else { + // check data + if (stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = stripCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num0, coord_num1, coord_num2; // indices of current triangle + bool odd_tri; // sequence of current triangle + size_t coord_num_sb; // index of first point of strip + + ne_alias.CoordIndex.clear(); + coord_num_sb = 0; + for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three."); + + // set initial values for first triangle + coord_num0 = coord_num_sb; + coord_num1 = coord_num_sb + 1; + coord_num2 = coord_num_sb + 2; + odd_tri = true; + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 0 2 + // 1 + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num1)); + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num2)); + } else { + // 0 1 + // 2 + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num2)); + ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num1)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + // prepare values for next triangle + if (odd_tri) { + coord_num0 = coord_num2; + coord_num2++; + } else { + coord_num1 = coord_num2; + coord_num2 = coord_num1 + 1; + } + + odd_tri = !odd_tri; + coord_num_sb = coord_num2; // that index will be start of next strip + } // for(int32_t vc = 2; vc < *vc_it; vc++) + } // for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Normal +// DEF="" ID +// USE="" IDREF +// vector="" MFVec3f [inputOutput] +// /> +void X3DImporter::readNormal(XmlNode &node) { + std::string use, def; + std::list<aiVector3D> vector; + X3DNodeElementBase *ne=nullptr; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "vector", vector); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Normal, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementNormal(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementNormal *)ne)->Value = vector; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Normal"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Shape.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Shape.cpp new file mode 100644 index 0000000..1c472e1 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Shape.cpp @@ -0,0 +1,241 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Shape.cpp +/// \brief Parsing data from nodes of "Shape" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +void X3DImporter::readShape(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Shape, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementShape(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for appearance node + if (currentChildName == "Appearance") readAppearance(currentChildNode); + // check for X3DGeometryNodes + else if (currentChildName == "Arc2D") + readArc2D(currentChildNode); + else if (currentChildName == "ArcClose2D") + readArcClose2D(currentChildNode); + else if (currentChildName == "Circle2D") + readCircle2D(currentChildNode); + else if (currentChildName == "Disk2D") + readDisk2D(currentChildNode); + else if (currentChildName == "Polyline2D") + readPolyline2D(currentChildNode); + else if (currentChildName == "Polypoint2D") + readPolypoint2D(currentChildNode); + else if (currentChildName == "Rectangle2D") + readRectangle2D(currentChildNode); + else if (currentChildName == "TriangleSet2D") + readTriangleSet2D(currentChildNode); + else if (currentChildName == "Box") + readBox(currentChildNode); + else if (currentChildName == "Cone") + readCone(currentChildNode); + else if (currentChildName == "Cylinder") + readCylinder(currentChildNode); + else if (currentChildName == "ElevationGrid") + readElevationGrid(currentChildNode); + else if (currentChildName == "Extrusion") + readExtrusion(currentChildNode); + else if (currentChildName == "IndexedFaceSet") + readIndexedFaceSet(currentChildNode); + else if (currentChildName == "Sphere") + readSphere(currentChildNode); + else if (currentChildName == "IndexedLineSet") + readIndexedLineSet(currentChildNode); + else if (currentChildName == "LineSet") + readLineSet(currentChildNode); + else if (currentChildName == "PointSet") + readPointSet(currentChildNode); + else if (currentChildName == "IndexedTriangleFanSet") + readIndexedTriangleFanSet(currentChildNode); + else if (currentChildName == "IndexedTriangleSet") + readIndexedTriangleSet(currentChildNode); + else if (currentChildName == "IndexedTriangleStripSet") + readIndexedTriangleStripSet(currentChildNode); + else if (currentChildName == "TriangleFanSet") + readTriangleFanSet(currentChildNode); + else if (currentChildName == "TriangleSet") + readTriangleSet(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("Shape", currentChildNode); + } + + ParseHelper_Node_Exit(); + } // if (!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Appearance +// DEF="" ID +// USE="" IDREF +// > +// <!-- AppearanceChildContentModel --> +// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and +// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader, +// PackagedShader, ProgramShader). +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model." +// </Appearance> +void X3DImporter::readAppearance(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Appearance, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementAppearance(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + if (currentChildName == "Material") + readMaterial(currentChildNode); + else if (currentChildName == "ImageTexture") + readImageTexture(currentChildNode); + else if (currentChildName == "TextureTransform") + readTextureTransform(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("Appearance", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <Material +// DEF="" ID +// USE="" IDREF +// ambientIntensity="0.2" SFFloat [inputOutput] +// diffuseColor="0.8 0.8 0.8" SFColor [inputOutput] +// emissiveColor="0 0 0" SFColor [inputOutput] +// shininess="0.2" SFFloat [inputOutput] +// specularColor="0 0 0" SFColor [inputOutput] +// transparency="0" SFFloat [inputOutput] +// /> +void X3DImporter::readMaterial(XmlNode &node) { + std::string use, def; + float ambientIntensity = 0.2f; + float shininess = 0.2f; + float transparency = 0; + aiColor3D diffuseColor(0.8f, 0.8f, 0.8f); + aiColor3D emissiveColor(0, 0, 0); + aiColor3D specularColor(0, 0, 0); + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + XmlParser::getFloatAttribute(node, "shininess", shininess); + XmlParser::getFloatAttribute(node, "transparency", transparency); + X3DXmlHelper::getColor3DAttribute(node, "diffuseColor", diffuseColor); + X3DXmlHelper::getColor3DAttribute(node, "emissiveColor", emissiveColor); + X3DXmlHelper::getColor3DAttribute(node, "specularColor", specularColor); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Material, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementMaterial(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementMaterial *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementMaterial *)ne)->Shininess = shininess; + ((X3DNodeElementMaterial *)ne)->Transparency = transparency; + ((X3DNodeElementMaterial *)ne)->DiffuseColor = diffuseColor; + ((X3DNodeElementMaterial *)ne)->EmissiveColor = emissiveColor; + ((X3DNodeElementMaterial *)ne)->SpecularColor = specularColor; + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Material"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DImporter_Texturing.cpp b/libs/assimp/code/AssetLib/X3D/X3DImporter_Texturing.cpp new file mode 100644 index 0000000..32c1a90 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DImporter_Texturing.cpp @@ -0,0 +1,179 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/// \file X3DImporter_Texturing.cpp +/// \brief Parsing data from nodes of "Texturing" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// <ImageTexture +// DEF="" ID +// USE="" IDREF +// repeatS="true" SFBool +// repeatT="true" SFBool +// url="" MFString +// /> +// When the url field contains no values ([]), texturing is disabled. +void X3DImporter::readImageTexture(XmlNode &node) { + std::string use, def; + bool repeatS = true; + bool repeatT = true; + std::list<std::string> url; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "repeatS", repeatS); + XmlParser::getBoolAttribute(node, "repeatT", repeatT); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ImageTexture, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementImageTexture(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementImageTexture *)ne)->RepeatS = repeatS; + ((X3DNodeElementImageTexture *)ne)->RepeatT = repeatT; + // Attribute "url" can contain list of strings. But we need only one - first. + if (!url.empty()) + ((X3DNodeElementImageTexture *)ne)->URL = url.front(); + else + ((X3DNodeElementImageTexture *)ne)->URL = ""; + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ImageTexture"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <TextureCoordinate +// DEF="" ID +// USE="" IDREF +// point="" MFVec3f [inputOutput] +// /> +void X3DImporter::readTextureCoordinate(XmlNode &node) { + std::string use, def; + std::list<aiVector2D> point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureCoordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureCoordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// <TextureTransform +// DEF="" ID +// USE="" IDREF +// center="0 0" SFVec2f [inputOutput] +// rotation="0" SFFloat [inputOutput] +// scale="1 1" SFVec2f [inputOutput] +// translation="0 0" SFVec2f [inputOutput] +// /> +void X3DImporter::readTextureTransform(XmlNode &node) { + std::string use, def; + aiVector2D center(0, 0); + float rotation = 0; + aiVector2D scale(1, 1); + aiVector2D translation(0, 0); + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "center", center); + XmlParser::getFloatAttribute(node, "rotation", rotation); + X3DXmlHelper::getVector2DAttribute(node, "scale", scale); + X3DXmlHelper::getVector2DAttribute(node, "translation", translation); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureTransform, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureTransform(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureTransform *)ne)->Center = center; + ((X3DNodeElementTextureTransform *)ne)->Rotation = rotation; + ((X3DNodeElementTextureTransform *)ne)->Scale = scale; + ((X3DNodeElementTextureTransform *)ne)->Translation = translation; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureTransform"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/libs/assimp/code/AssetLib/X3D/X3DXmlHelper.cpp b/libs/assimp/code/AssetLib/X3D/X3DXmlHelper.cpp new file mode 100644 index 0000000..ff24b74 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DXmlHelper.cpp @@ -0,0 +1,294 @@ +#include "X3DXmlHelper.h" +#include "X3DImporter.hpp" + +#include <assimp/ParsingUtils.h> + +namespace Assimp { + +bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.r = stof(*it++); + color.g = stof(*it++); + color.b = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() != 2) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it++); + color.z = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector<bool> &boolArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + boolArray.push_back(s[0] == 't' || s[0] == '1'); + else + Throw_ConvertFail_Str2ArrB(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector<double> &doubleArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + doubleArray.push_back(atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrD(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector<float> &floatArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + floatArray.push_back((float)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector<int32_t> &intArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + intArray.push_back((int32_t)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getStringListAttribute(XmlNode &node, const char *attributeName, std::list<std::string> &stringList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + auto it = values.begin(); + std::string currentConcat = ""; + bool inQuotes = false; + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) { + if (inQuotes) { + if (*(s.rbegin()) == '"') { + stringList.push_back(currentConcat + s.substr(0, s.length() - 1)); + currentConcat = ""; + inQuotes = false; + } else { + currentConcat += " " + s; + } + } else { + if (s[0] == '"') { + currentConcat = s.substr(1); + inQuotes = true; + } else { + stringList.push_back(s); + } + } + } else if (!inQuotes) + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + if (inQuotes) Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + return true; + } + return false; +} + +bool X3DXmlHelper::getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector<std::string> &stringArray) { + std::list<std::string> tlist; + + if (getStringListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + stringArray.reserve(tlist.size()); + for (std::list<std::string>::iterator it = tlist.begin(); it != tlist.end(); ++it) { + stringArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector2D> &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() % 2) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector2D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector2D> &vectorArray) { + std::list<aiVector2D> tlist; + + if (getVector2DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector3D> &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector3D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + tvec.z = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector3D> &vectorArray) { + std::list<aiVector3D> tlist; + + if (getVector3DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor3D> &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor3D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor4D> &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector<std::string> values; + tokenize<std::string>(val, values, " "); + if (values.size() % 4 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor4D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + tvec.a = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +} // namespace Assimp diff --git a/libs/assimp/code/AssetLib/X3D/X3DXmlHelper.h b/libs/assimp/code/AssetLib/X3D/X3DXmlHelper.h new file mode 100644 index 0000000..dd305f8 --- /dev/null +++ b/libs/assimp/code/AssetLib/X3D/X3DXmlHelper.h @@ -0,0 +1,30 @@ +#pragma once + +#include <assimp/XmlParser.h> +#include <assimp/types.h> +#include <list> + +namespace Assimp { + +class X3DXmlHelper { +public: + static bool getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color); + static bool getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &vector); + static bool getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &vector); + + static bool getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector<bool> &boolArray); + static bool getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector<double> &doubleArray); + static bool getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector<float> &floatArray); + static bool getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector<int32_t> &intArray); + static bool getStringListAttribute(XmlNode &node, const char *attributeName, std::list<std::string> &stringArray); + static bool getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector<std::string> &stringArray); + + static bool getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector2D> &vectorList); + static bool getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector2D> &vectorArray); + static bool getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector3D> &vectorList); + static bool getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector3D> &vectorArray); + static bool getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor3D> &colorList); + static bool getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor4D> &colorList); +}; + +} // namespace Assimp |