diff --git a/graphics/include/ignition/common/ColladaExporter.hh b/graphics/include/ignition/common/ColladaExporter.hh index 76495f620..623c85a4a 100644 --- a/graphics/include/ignition/common/ColladaExporter.hh +++ b/graphics/include/ignition/common/ColladaExporter.hh @@ -19,11 +19,14 @@ #include #include +#include #include #include #include +#include + namespace ignition { namespace common @@ -47,6 +50,10 @@ namespace ignition public: virtual void Export(const Mesh *_mesh, const std::string &_filename, bool _exportTextures = false); + public: void Export(const Mesh *_mesh, + const std::string &_filename, bool _exportTextures, + const std::vector &_submeshToMatrix); + IGN_COMMON_WARN_IGNORE__DLL_INTERFACE_MISSING /// \internal /// \brief Pointer to private data. diff --git a/graphics/src/ColladaExporter.cc b/graphics/src/ColladaExporter.cc index a7743d10f..f6d80f6c3 100644 --- a/graphics/src/ColladaExporter.cc +++ b/graphics/src/ColladaExporter.cc @@ -112,7 +112,8 @@ class ignition::common::ColladaExporterPrivate /// \param[in] _libraryVisualScenesXml Pointer to the library visual /// scenes XML instance public: void ExportVisualScenes( - tinyxml2::XMLElement *_libraryVisualScenesXml); + tinyxml2::XMLElement *_libraryVisualScenesXml, + const std::vector &_submeshToMatrix); /// \brief Export scene element /// \param[in] _sceneXml Pointer to the scene XML instance @@ -153,6 +154,23 @@ ColladaExporter::~ColladaExporter() void ColladaExporter::Export(const Mesh *_mesh, const std::string &_filename, bool _exportTextures) { + std::vector empty; + this->Export(_mesh, _filename, _exportTextures, empty); +} + +////////////////////////////////////////////////// +void ColladaExporter::Export(const Mesh *_mesh, const std::string &_filename, + bool _exportTextures, const std::vector &_submeshToMatrix) +{ + if ( _submeshToMatrix.size() > 0 && + (_mesh->SubMeshCount() != _submeshToMatrix.size()) ) + { + ignerr << "_submeshToMatrix.size() : " << _mesh->SubMeshCount() + << " , must be equal to SubMeshCount() : " << _mesh->SubMeshCount() + << std::endl; + return; + } + this->dataPtr->mesh = _mesh; this->dataPtr->materialCount = this->dataPtr->mesh->MaterialCount(); this->dataPtr->subMeshCount = this->dataPtr->mesh->SubMeshCount(); @@ -165,14 +183,6 @@ void ColladaExporter::Export(const Mesh *_mesh, const std::string &_filename, this->dataPtr->path = unix_filename.substr(0, beginFilename); this->dataPtr->filename = unix_filename.substr(beginFilename); - if (this->dataPtr->materialCount != 0 && - this->dataPtr->materialCount != this->dataPtr->subMeshCount) - { - ignwarn << "Material count [" << this->dataPtr->materialCount << - "] different from submesh count [" << - this->dataPtr->subMeshCount << "]\n"; - } - // Collada file tinyxml2::XMLDocument xmlDoc; @@ -223,7 +233,7 @@ void ColladaExporter::Export(const Mesh *_mesh, const std::string &_filename, // Library visual scenes element tinyxml2::XMLElement *libraryVisualScenesXml = xmlDoc.NewElement("library_visual_scenes"); - this->dataPtr->ExportVisualScenes(libraryVisualScenesXml); + this->dataPtr->ExportVisualScenes(libraryVisualScenesXml, _submeshToMatrix); colladaXml->LinkEndChild(libraryVisualScenesXml); // Scene element @@ -390,9 +400,12 @@ void ColladaExporterPrivate::ExportGeometries( { for (unsigned int i = 0; i < this->subMeshCount; ++i) { + unsigned int materialIndex = + this->mesh->SubMeshByIndex(i).lock()->MaterialIndex(); + char meshId[100], materialId[100]; snprintf(meshId, sizeof(meshId), "mesh_%u", i); - snprintf(materialId, sizeof(materialId), "material_%u", i); + snprintf(materialId, sizeof(materialId), "material_%u", materialIndex); tinyxml2::XMLElement *geometryXml = _libraryGeometriesXml->GetDocument()->NewElement("geometry"); @@ -506,14 +519,16 @@ int ColladaExporterPrivate::ExportImages( tinyxml2::XMLElement *initFromXml = _libraryImagesXml->GetDocument()->NewElement("init_from"); - initFromXml->LinkEndChild(_libraryImagesXml->GetDocument()->NewText( - imageString.substr(imageString.find("meshes/")+7).c_str())); + const auto imageName = imageString.substr(imageString.rfind("/")); + const auto imagePath = ignition::common::joinPaths("..", "materials", + "textures", imageName); + initFromXml->LinkEndChild( + _libraryImagesXml->GetDocument()->NewText(imagePath.c_str())); imageXml->LinkEndChild(initFromXml); if (this->exportTextures) { createDirectories(this->path + this->filename + "/materials/textures"); - std::ifstream src(imageString.c_str(), std::ios::binary); std::ofstream dst((this->path + this->filename + "/materials/textures" + imageString.substr( @@ -672,7 +687,7 @@ void ColladaExporterPrivate::ExportEffects( { tinyxml2::XMLElement *textureXml = _libraryEffectsXml->GetDocument()->NewElement("texture"); - snprintf(id, sizeof(id), "image_%u", i); + snprintf(id, sizeof(id), "image_%u_sampler", i); textureXml->SetAttribute("texture", id); textureXml->SetAttribute("texcoord", "UVSET0"); diffuseXml->LinkEndChild(textureXml); @@ -736,7 +751,8 @@ void ColladaExporterPrivate::ExportEffects( ////////////////////////////////////////////////// void ColladaExporterPrivate::ExportVisualScenes( - tinyxml2::XMLElement *_libraryVisualScenesXml) + tinyxml2::XMLElement *_libraryVisualScenesXml, + const std::vector &_submeshToMatrix) { tinyxml2::XMLElement *visualSceneXml = _libraryVisualScenesXml->GetDocument()->NewElement("visual_scene"); @@ -744,17 +760,34 @@ void ColladaExporterPrivate::ExportVisualScenes( visualSceneXml->SetAttribute("name", "Scene"); visualSceneXml->SetAttribute("id", "Scene"); - tinyxml2::XMLElement *nodeXml = - _libraryVisualScenesXml->GetDocument()->NewElement("node"); - visualSceneXml->LinkEndChild(nodeXml); - nodeXml->SetAttribute("name", "node"); - nodeXml->SetAttribute("id", "node"); - for (unsigned int i = 0; i < this->subMeshCount; ++i) { - char meshId[100], materialId[100], attributeValue[101]; + char meshId[100], attributeValue[101], nodeId[106]; + snprintf(meshId, sizeof(meshId), "mesh_%u", i); - snprintf(materialId, sizeof(materialId), "material_%u", i); + snprintf(nodeId, sizeof(nodeId), "node_%u", i); + + tinyxml2::XMLElement *nodeXml = + _libraryVisualScenesXml->GetDocument()->NewElement("node"); + visualSceneXml->LinkEndChild(nodeXml); + + nodeXml->SetAttribute("name", nodeId); + nodeXml->SetAttribute("id", nodeId); + + if (i < _submeshToMatrix.size()) + { + std::ostringstream fillData; + fillData.precision(8); + fillData << std::fixed; + + fillData << _submeshToMatrix.at(i); + + tinyxml2::XMLElement *matrixXml = + _libraryVisualScenesXml->GetDocument()->NewElement("matrix"); + nodeXml->LinkEndChild(matrixXml); + matrixXml->LinkEndChild(_libraryVisualScenesXml->GetDocument()->NewText( + fillData.str().c_str())); + } tinyxml2::XMLElement *instanceGeometryXml = _libraryVisualScenesXml->GetDocument()->NewElement("instance_geometry"); @@ -762,11 +795,18 @@ void ColladaExporterPrivate::ExportVisualScenes( snprintf(attributeValue, sizeof(attributeValue), "#%s", meshId); instanceGeometryXml->SetAttribute("url", attributeValue); + unsigned int materialIndex = + this->mesh->SubMeshByIndex(i).lock()->MaterialIndex(); + const ignition::common::MaterialPtr material = - this->mesh->MaterialByIndex(i); + this->mesh->MaterialByIndex(materialIndex); if (material) { + char materialId[100]; + + snprintf(materialId, sizeof(materialId), "material_%u", materialIndex); + tinyxml2::XMLElement *bindMaterialXml = _libraryVisualScenesXml->GetDocument()->NewElement("bind_material"); instanceGeometryXml->LinkEndChild(bindMaterialXml); diff --git a/graphics/src/ColladaExporter_TEST.cc b/graphics/src/ColladaExporter_TEST.cc index 0d49444f2..152d65226 100644 --- a/graphics/src/ColladaExporter_TEST.cc +++ b/graphics/src/ColladaExporter_TEST.cc @@ -18,10 +18,11 @@ #include "tinyxml2.h" #include "test_config.h" -#include "ignition/common/Mesh.hh" -#include "ignition/common/SubMesh.hh" #include "ignition/common/ColladaLoader.hh" #include "ignition/common/ColladaExporter.hh" +#include "ignition/common/Filesystem.hh" +#include "ignition/common/Mesh.hh" +#include "ignition/common/SubMesh.hh" #include "test/util.hh" #ifdef _WIN32 @@ -193,6 +194,123 @@ TEST_F(ColladaExporter, ExportCordlessDrill) common::removeAll(pathOut + "/tmp"); } +///////////////////////////////////////////////// +TEST_F(ColladaExporter, ExportMeshWithSubmeshes) +{ + std::string boxFilenameIn = std::string(PROJECT_SOURCE_PATH) + + "/test/data/box.dae"; + + common::ColladaLoader loader; + const common::Mesh *boxMesh = loader.Load( + boxFilenameIn); + + const common::Mesh *drillMesh = loader.Load( + common::joinPaths(PROJECT_SOURCE_PATH, + "/test/data/cordless_drill/meshes/cordless_drill.dae")); + + common::Mesh outMesh; + std::weak_ptr subm; + std::vector subMeshMatrix; + math::Pose3d localPose = math::Pose3d::Zero; + + int i = outMesh.AddMaterial( + boxMesh->MaterialByIndex( + boxMesh->SubMeshByIndex(0).lock()->MaterialIndex())); + subm = outMesh.AddSubMesh(*boxMesh->SubMeshByIndex(0).lock().get()); + subm.lock()->SetMaterialIndex(i); + + localPose.SetX(10); + math::Matrix4d matrix(localPose); + subMeshMatrix.push_back(matrix); + + i = outMesh.AddMaterial( + drillMesh->MaterialByIndex( + drillMesh->SubMeshByIndex(0).lock()->MaterialIndex())); + subm = outMesh.AddSubMesh(*drillMesh->SubMeshByIndex(0).lock().get()); + subm.lock()->SetMaterialIndex(i); + + localPose.SetX(-10); + matrix = math::Matrix4d(localPose); + subMeshMatrix.push_back(matrix); + + std::string pathOut = common::joinPaths(common::cwd(), "tmp"); + common::createDirectories(pathOut); + + common::ColladaExporter exporter; + exporter.Export(&outMesh, + common::joinPaths(pathOut, "mesh_with_submeshes"), true, subMeshMatrix); + + // Check .dae file + tinyxml2::XMLDocument xmlDoc; + std::string filename = common::joinPaths(pathOut, + "mesh_with_submeshes/meshes/mesh_with_submeshes.dae"); + + EXPECT_EQ(xmlDoc.LoadFile(filename.c_str()), tinyxml2::XML_SUCCESS); + + ASSERT_TRUE(xmlDoc.FirstChildElement("COLLADA") != nullptr); + ASSERT_TRUE(xmlDoc.FirstChildElement( + "COLLADA")->FirstChildElement("library_geometries") != nullptr); + + tinyxml2::XMLElement *geometryXml = xmlDoc.FirstChildElement("COLLADA") + ->FirstChildElement("library_geometries") + ->FirstChildElement("geometry"); + ASSERT_TRUE(geometryXml != nullptr); + + for (unsigned int j = 0; j < outMesh.SubMeshCount(); ++j) + { + unsigned int countMeshInt = + outMesh.SubMeshByIndex(j).lock()->VertexCount()*3; + char countMesh[100]; + snprintf(countMesh, sizeof(countMesh), "%u", countMeshInt); + + const char *countDae = geometryXml + ->FirstChildElement("mesh") + ->FirstChildElement("source") + ->FirstChildElement("float_array") + ->Attribute("count"); + + EXPECT_STREQ(countDae, countMesh); + + geometryXml = geometryXml->NextSiblingElement("geometry"); + } + + tinyxml2::XMLElement *nodeXml = xmlDoc.FirstChildElement("COLLADA") + ->FirstChildElement("library_visual_scenes") + ->FirstChildElement("visual_scene") + ->FirstChildElement("node"); + ASSERT_TRUE(nodeXml != nullptr); + + for (unsigned int j = 0; j < outMesh.SubMeshCount(); ++j) + { + std::ostringstream fillData; + fillData.precision(8); + fillData << std::fixed; + fillData << subMeshMatrix.at(j); + + std::string matrixStr = nodeXml->FirstChildElement("matrix")->GetText(); + EXPECT_EQ(matrixStr, fillData.str()); + + nodeXml = nodeXml->NextSiblingElement("node"); + } + + // Reload mesh and compare + const common::Mesh *meshReloaded = loader.Load(common::joinPaths(pathOut, + "mesh_with_submeshes/meshes/mesh_with_submeshes.dae")); + + EXPECT_EQ(outMesh.Name(), meshReloaded->Name()); + EXPECT_EQ(outMesh.SubMeshCount(), meshReloaded->SubMeshCount()); + EXPECT_EQ(outMesh.MaterialCount(), + meshReloaded->MaterialCount()); + EXPECT_EQ(outMesh.IndexCount(), meshReloaded->IndexCount()); + EXPECT_EQ(outMesh.VertexCount(), meshReloaded->VertexCount()); + EXPECT_EQ(outMesh.NormalCount(), meshReloaded->NormalCount()); + EXPECT_EQ(outMesh.TexCoordCount(), + meshReloaded->TexCoordCount()); + + // Remove temp directory + common::removeAll(pathOut); +} + ///////////////////////////////////////////////// int main(int argc, char **argv) {