Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transformation matrix to ColladaExport #100

Merged
merged 13 commits into from
Nov 18, 2020
Merged
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);
scpeters marked this conversation as resolved.
Show resolved Hide resolved

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";
}

mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
// 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");

mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
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";
Comment on lines +200 to +201
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::string boxFilenameIn = std::string(PROJECT_SOURCE_PATH) +
"/test/data/box.dae";
std::string boxFilenameIn = common::joinPaths(PROJECT_SOURCE_PATH,
"/test/data/box.dae");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needed.

Copy link
Contributor

@chapulina chapulina Dec 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this went through unresolved partially resolved. The string needs to be separated on all / for Windows.

I also noticed this test failing on CI:

  [ RUN      ] ColladaExporter.ExportCordlessDrill
  unknown file: Failure
  C++ exception with description "basic_string::_M_construct null not valid" thrown in the test body.
  [  FAILED  ] ColladaExporter.ExportCordlessDrill (3 ms)
  [ RUN      ] ColladaExporter.ExportMeshWithSubmeshes
  unknown file: Failure
  C++ exception with description "basic_string::_M_construct null not valid" thrown in the test body.
  [  FAILED  ] ColladaExporter.ExportMeshWithSubmeshes (3 ms)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this didn't fail in the action here?

Fix here: #133


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