Skip to content

Commit

Permalink
Add transformation matrix to ColladaExport (#100)
Browse files Browse the repository at this point in the history
This adds transformation matrixes to nodes in collada files when exporting

Connects to: gazebosim/gz-sim#307

Signed-off-by: Gonzalo de Pedro <[email protected]>

Co-authored-by: Michael Carroll <[email protected]>
  • Loading branch information
gonzodepedro and mjcarroll authored Nov 18, 2020
1 parent d07cbdc commit c2f86eb
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 27 deletions.
7 changes: 7 additions & 0 deletions graphics/include/ignition/common/ColladaExporter.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@

#include <memory>
#include <string>
#include <vector>

#include <ignition/common/MeshExporter.hh>
#include <ignition/common/graphics/Export.hh>
#include <ignition/common/SuppressWarning.hh>

#include <ignition/math/Matrix4.hh>

namespace ignition
{
namespace common
Expand All @@ -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<math::Matrix4d> &_submeshToMatrix);

IGN_COMMON_WARN_IGNORE__DLL_INTERFACE_MISSING
/// \internal
/// \brief Pointer to private data.
Expand Down
90 changes: 65 additions & 25 deletions graphics/src/ColladaExporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<math::Matrix4d> &_submeshToMatrix);

/// \brief Export scene element
/// \param[in] _sceneXml Pointer to the scene XML instance
Expand Down Expand Up @@ -153,6 +154,23 @@ ColladaExporter::~ColladaExporter()
void ColladaExporter::Export(const Mesh *_mesh, const std::string &_filename,
bool _exportTextures)
{
std::vector<math::Matrix4d> empty;
this->Export(_mesh, _filename, _exportTextures, empty);
}

//////////////////////////////////////////////////
void ColladaExporter::Export(const Mesh *_mesh, const std::string &_filename,
bool _exportTextures, const std::vector<math::Matrix4d> &_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();
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -736,37 +751,62 @@ void ColladaExporterPrivate::ExportEffects(

//////////////////////////////////////////////////
void ColladaExporterPrivate::ExportVisualScenes(
tinyxml2::XMLElement *_libraryVisualScenesXml)
tinyxml2::XMLElement *_libraryVisualScenesXml,
const std::vector<math::Matrix4d> &_submeshToMatrix)
{
tinyxml2::XMLElement *visualSceneXml =
_libraryVisualScenesXml->GetDocument()->NewElement("visual_scene");
_libraryVisualScenesXml->LinkEndChild(visualSceneXml);
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");
nodeXml->LinkEndChild(instanceGeometryXml);
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);
Expand Down
122 changes: 120 additions & 2 deletions graphics/src/ColladaExporter_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<common::SubMesh> subm;
std::vector<math::Matrix4d> 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)
{
Expand Down

0 comments on commit c2f86eb

Please sign in to comment.