pxmlw6n2f/Gazebo_Distributed_TCP/gazebo/common/ColladaLoader.cc

2069 lines
66 KiB
C++

/*
* Copyright (C) 2012 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <tinyxml.h>
#include <math.h>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/unordered_map.hpp>
#include "gazebo/math/Helpers.hh"
#include "gazebo/math/Angle.hh"
#include "gazebo/math/Vector2d.hh"
#include "gazebo/math/Vector3.hh"
#include "gazebo/math/Matrix4.hh"
#include "gazebo/math/Quaternion.hh"
#include "gazebo/common/Console.hh"
#include "gazebo/common/Material.hh"
#include "gazebo/common/Mesh.hh"
#include "gazebo/common/Skeleton.hh"
#include "gazebo/common/SkeletonAnimation.hh"
#include "gazebo/common/SystemPaths.hh"
#include "gazebo/common/Exception.hh"
#include "gazebo/common/ColladaLoaderPrivate.hh"
#include "gazebo/common/ColladaLoader.hh"
using namespace gazebo;
using namespace common;
/////////////////////////////////////////////////
struct Vector3Hash : std::unary_function<const ignition::math::Vector3d,
std::size_t>
{
std::size_t operator()(const ignition::math::Vector3d &_v) const
{
std::size_t seed = 0;
boost::hash_combine(seed, _v.X());
boost::hash_combine(seed, _v.Y());
boost::hash_combine(seed, _v.Z());
return seed;
}
};
/////////////////////////////////////////////////
struct Vector2dHash : std::unary_function<const ignition::math::Vector2d,
std::size_t>
{
std::size_t operator()(const ignition::math::Vector2d &_v) const
{
std::size_t seed = 0;
boost::hash_combine(seed, _v.X());
boost::hash_combine(seed, _v.Y());
return seed;
}
};
//////////////////////////////////////////////////
ColladaLoader::ColladaLoader()
: MeshLoader(), dataPtr(new ColladaLoaderPrivate)
{
this->dataPtr->meter = 1.0;
}
//////////////////////////////////////////////////
ColladaLoader::~ColladaLoader()
{
delete this->dataPtr;
this->dataPtr = 0;
}
//////////////////////////////////////////////////
Mesh *ColladaLoader::Load(const std::string &_filename)
{
this->dataPtr->positionIds.clear();
this->dataPtr->normalIds.clear();
this->dataPtr->texcoordIds.clear();
this->dataPtr->materialIds.clear();
this->dataPtr->positionDuplicateMap.clear();
this->dataPtr->normalDuplicateMap.clear();
this->dataPtr->texcoordDuplicateMap.clear();
// reset scale
this->dataPtr->meter = 1.0;
TiXmlDocument xmlDoc;
this->dataPtr->path.clear();
if (_filename.rfind('/') != std::string::npos)
{
this->dataPtr->path = _filename.substr(0, _filename.rfind('/'));
}
this->dataPtr->filename = _filename;
if (!xmlDoc.LoadFile(_filename))
gzerr << "Unable to load collada file[" << _filename << "]\n";
this->dataPtr->colladaXml = xmlDoc.FirstChildElement("COLLADA");
if (!this->dataPtr->colladaXml)
gzerr << "Missing COLLADA tag\n";
if (std::string(this->dataPtr->colladaXml->Attribute("version")) != "1.4.0" &&
std::string(this->dataPtr->colladaXml->Attribute("version")) != "1.4.1")
gzerr << "Invalid collada file. Must be version 1.4.0 or 1.4.1\n";
TiXmlElement *assetXml =
this->dataPtr->colladaXml->FirstChildElement("asset");
if (assetXml)
{
TiXmlElement *unitXml = assetXml->FirstChildElement("unit");
if (unitXml && unitXml->Attribute("meter"))
this->dataPtr->meter = ignition::math::parseFloat(
unitXml->Attribute("meter"));
}
Mesh *mesh = new Mesh();
mesh->SetPath(this->dataPtr->path);
this->LoadScene(mesh);
// This will make the model the correct size.
mesh->Scale(this->dataPtr->meter);
return mesh;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadScene(Mesh *_mesh)
{
TiXmlElement *sceneXml =
this->dataPtr->colladaXml->FirstChildElement("scene");
std::string sceneURL =
sceneXml->FirstChildElement("instance_visual_scene")->Attribute("url");
TiXmlElement *visSceneXml = this->GetElementId("visual_scene", sceneURL);
if (!visSceneXml)
{
gzerr << "Unable to find visual_scene id ='" << sceneURL << "'\n";
return;
}
TiXmlElement *nodeXml = visSceneXml->FirstChildElement("node");
while (nodeXml)
{
this->LoadNode(nodeXml, _mesh, ignition::math::Matrix4d::Identity);
nodeXml = nodeXml->NextSiblingElement("node");
}
}
/////////////////////////////////////////////////
void ColladaLoader::LoadNode(TiXmlElement *_elem, Mesh *_mesh,
const ignition::math::Matrix4d &_transform)
{
TiXmlElement *nodeXml;
TiXmlElement *instGeomXml;
ignition::math::Matrix4d transform = this->LoadNodeTransform(_elem);
transform = _transform * transform;
if (_elem->Attribute("name"))
{
this->dataPtr->currentNodeName = _elem->Attribute("name");
}
nodeXml = _elem->FirstChildElement("node");
while (nodeXml)
{
this->LoadNode(nodeXml, _mesh, transform);
nodeXml = nodeXml->NextSiblingElement("node");
}
if (_elem->FirstChildElement("instance_node"))
{
std::string nodeURLStr =
_elem->FirstChildElement("instance_node")->Attribute("url");
nodeXml = this->GetElementId("node", nodeURLStr);
if (!nodeXml)
{
gzerr << "Unable to find node[" << nodeURLStr << "]\n";
return;
}
this->LoadNode(nodeXml, _mesh, transform);
return;
}
else
nodeXml = _elem;
instGeomXml = nodeXml->FirstChildElement("instance_geometry");
while (instGeomXml)
{
std::string geomURL = instGeomXml->Attribute("url");
TiXmlElement *geomXml = this->GetElementId("geometry", geomURL);
this->dataPtr->materialMap.clear();
TiXmlElement *bindMatXml, *techniqueXml, *matXml;
bindMatXml = instGeomXml->FirstChildElement("bind_material");
while (bindMatXml)
{
if ((techniqueXml = bindMatXml->FirstChildElement("technique_common")))
{
matXml = techniqueXml->FirstChildElement("instance_material");
while (matXml)
{
std::string symbol = matXml->Attribute("symbol");
std::string target = matXml->Attribute("target");
this->dataPtr->materialMap[symbol] = target;
matXml = matXml->NextSiblingElement("instance_material");
}
}
bindMatXml = bindMatXml->NextSiblingElement("bind_material");
}
this->LoadGeometry(geomXml, transform, _mesh);
instGeomXml = instGeomXml->NextSiblingElement("instance_geometry");
}
TiXmlElement *instContrXml =
nodeXml->FirstChildElement("instance_controller");
while (instContrXml)
{
std::string contrURL = instContrXml->Attribute("url");
TiXmlElement *contrXml = this->GetElementId("controller", contrURL);
TiXmlElement *instSkelXml = instContrXml->FirstChildElement("skeleton");
std::string rootURL = instSkelXml->GetText();
TiXmlElement *rootNodeXml = this->GetElementId("node", rootURL);
this->dataPtr->materialMap.clear();
TiXmlElement *bindMatXml, *techniqueXml, *matXml;
bindMatXml = instContrXml->FirstChildElement("bind_material");
while (bindMatXml)
{
if ((techniqueXml = bindMatXml->FirstChildElement("technique_common")))
{
matXml = techniqueXml->FirstChildElement("instance_material");
while (matXml)
{
std::string symbol = matXml->Attribute("symbol");
std::string target = matXml->Attribute("target");
this->dataPtr->materialMap[symbol] = target;
matXml = matXml->NextSiblingElement("instance_material");
}
}
bindMatXml = bindMatXml->NextSiblingElement("bind_material");
}
this->LoadController(contrXml, rootNodeXml, transform, _mesh);
instContrXml = instContrXml->NextSiblingElement("instance_controller");
}
}
/////////////////////////////////////////////////
ignition::math::Matrix4d ColladaLoader::LoadNodeTransform(TiXmlElement *_elem)
{
ignition::math::Matrix4d transform(ignition::math::Matrix4d::Identity);
if (_elem->FirstChildElement("matrix"))
{
std::string matrixStr = _elem->FirstChildElement("matrix")->GetText();
std::istringstream iss(matrixStr);
std::vector<double> values(16);
for (unsigned int i = 0; i < 16; ++i)
iss >> values[i];
transform.Set(values[0], values[1], values[2], values[3],
values[4], values[5], values[6], values[7],
values[8], values[9], values[10], values[11],
values[12], values[13], values[14], values[15]);
}
else
{
if (_elem->FirstChildElement("translate"))
{
std::string transStr = _elem->FirstChildElement("translate")->GetText();
ignition::math::Vector3d translate;
translate = boost::lexical_cast<ignition::math::Vector3d>(transStr);
// translate *= this->dataPtr->meter;
transform.Translate(translate);
}
TiXmlElement *rotateXml = _elem->FirstChildElement("rotate");
while (rotateXml)
{
ignition::math::Matrix3d mat;
ignition::math::Vector3d axis;
double angle;
std::string rotateStr = rotateXml->GetText();
std::istringstream iss(rotateStr);
iss >> axis.X() >> axis.Y() >> axis.Z();
iss >> angle;
mat.Axis(axis, IGN_DTOR(angle));
ignition::math::Matrix4d mat4(ignition::math::Matrix4d::Identity);
mat4 = mat;
transform = transform * mat4;
rotateXml = rotateXml->NextSiblingElement("rotate");
}
if (_elem->FirstChildElement("scale"))
{
std::string scaleStr = _elem->FirstChildElement("scale")->GetText();
ignition::math::Vector3d scale;
scale = boost::lexical_cast<ignition::math::Vector3d>(scaleStr);
ignition::math::Matrix4d scaleMat;
scaleMat.Scale(scale);
transform = transform * scaleMat;
}
}
return transform;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadController(TiXmlElement *_contrXml,
TiXmlElement *_skelXml,
const ignition::math::Matrix4d &_transform, Mesh *_mesh)
{
Skeleton *skeleton = new Skeleton(this->LoadSkeletonNodes(_skelXml, NULL));
_mesh->SetSkeleton(skeleton);
TiXmlElement *rootXml = _contrXml->GetDocument()->RootElement();
if (rootXml->FirstChildElement("library_animations"))
this->LoadAnimations(rootXml->FirstChildElement("library_animations"),
skeleton);
TiXmlElement *skinXml = _contrXml->FirstChildElement("skin");
std::string geomURL = skinXml->Attribute("source");
ignition::math::Matrix4d bindTrans;
std::string matrixStr =
skinXml->FirstChildElement("bind_shape_matrix")->GetText();
std::istringstream iss(matrixStr);
std::vector<double> values(16);
for (unsigned int i = 0; i < 16; ++i)
iss >> values[i];
bindTrans.Set(values[0], values[1], values[2], values[3],
values[4], values[5], values[6], values[7],
values[8], values[9], values[10], values[11],
values[12], values[13], values[14], values[15]);
skeleton->SetBindShapeTransform(bindTrans);
TiXmlElement *jointsXml = skinXml->FirstChildElement("joints");
std::string jointsURL, invBindMatURL;
TiXmlElement *inputXml = jointsXml->FirstChildElement("input");
while (inputXml)
{
std::string semantic = inputXml->Attribute("semantic");
std::string source = inputXml->Attribute("source");
if (semantic == "JOINT")
jointsURL = source;
else
{
if (semantic == "INV_BIND_MATRIX")
invBindMatURL = source;
}
inputXml = inputXml->NextSiblingElement("input");
}
jointsXml = this->GetElementId("source", jointsURL);
if (!jointsXml)
{
gzerr << "Could not find node[" << jointsURL << "]\n";
gzthrow("Faild to parse skinning information in Collada file.");
}
std::string jointsStr = jointsXml->FirstChildElement("Name_array")->GetText();
std::vector<std::string> joints;
boost::split(joints, jointsStr, boost::is_any_of(" "));
TiXmlElement *invBMXml = this->GetElementId("source", invBindMatURL);
if (!invBMXml)
{
gzerr << "Could not find node[" << invBindMatURL << "]\n";
gzthrow("Faild to parse skinning information in Collada file.");
}
std::string posesStr = invBMXml->FirstChildElement("float_array")->GetText();
std::vector<std::string> strs;
boost::split(strs, posesStr, boost::is_any_of(" "));
for (unsigned int i = 0; i < joints.size(); ++i)
{
unsigned int id = i * 16;
ignition::math::Matrix4d mat;
mat.Set(ignition::math::parseFloat(strs[id + 0]),
ignition::math::parseFloat(strs[id + 1]),
ignition::math::parseFloat(strs[id + 2]),
ignition::math::parseFloat(strs[id + 3]),
ignition::math::parseFloat(strs[id + 4]),
ignition::math::parseFloat(strs[id + 5]),
ignition::math::parseFloat(strs[id + 6]),
ignition::math::parseFloat(strs[id + 7]),
ignition::math::parseFloat(strs[id + 8]),
ignition::math::parseFloat(strs[id + 9]),
ignition::math::parseFloat(strs[id + 10]),
ignition::math::parseFloat(strs[id + 11]),
ignition::math::parseFloat(strs[id + 12]),
ignition::math::parseFloat(strs[id + 13]),
ignition::math::parseFloat(strs[id + 14]),
ignition::math::parseFloat(strs[id + 15]));
skeleton->GetNodeByName(joints[i])->SetInverseBindTransform(mat);
}
TiXmlElement *vertWeightsXml = skinXml->FirstChildElement("vertex_weights");
inputXml = vertWeightsXml->FirstChildElement("input");
unsigned int jOffset = 0;
unsigned int wOffset = 0;
std::string weightsURL;
while (inputXml)
{
std::string semantic = inputXml->Attribute("semantic");
std::string source = inputXml->Attribute("source");
int offset;
inputXml->Attribute("offset", &offset);
if (semantic == "JOINT")
jOffset = offset;
else
if (semantic == "WEIGHT")
{
weightsURL = source;
wOffset = offset;
}
inputXml = inputXml->NextSiblingElement("input");
}
TiXmlElement *weightsXml = this->GetElementId("source", weightsURL);
std::string wString = weightsXml->FirstChildElement("float_array")->GetText();
std::vector<std::string> wStrs;
boost::split(wStrs, wString, boost::is_any_of(" "));
std::vector<float> weights;
for (unsigned int i = 0; i < wStrs.size(); ++i)
weights.push_back(math::parseFloat(wStrs[i]));
std::string cString = vertWeightsXml->FirstChildElement("vcount")->GetText();
std::string vString = vertWeightsXml->FirstChildElement("v")->GetText();
std::vector<std::string> vCountStrs;
std::vector<std::string> vStrs;
boost::split(vCountStrs, cString, boost::is_any_of(" "));
boost::split(vStrs, vString, boost::is_any_of(" "));
std::vector<unsigned int> vCount;
std::vector<unsigned int> v;
for (unsigned int i = 0; i < vCountStrs.size(); ++i)
vCount.push_back(math::parseInt(vCountStrs[i]));
for (unsigned int i = 0; i < vStrs.size(); ++i)
v.push_back(math::parseInt(vStrs[i]));
skeleton->SetNumVertAttached(vCount.size());
unsigned int vIndex = 0;
for (unsigned int i = 0; i < vCount.size(); ++i)
{
for (unsigned int j = 0; j < vCount[i]; ++j)
{
skeleton->AddVertNodeWeight(i, joints[v[vIndex + jOffset]],
weights[v[vIndex + wOffset]]);
vIndex += (jOffset + wOffset + 1);
}
}
TiXmlElement *geomXml = this->GetElementId("geometry", geomURL);
this->LoadGeometry(geomXml, _transform, _mesh);
}
/////////////////////////////////////////////////
void ColladaLoader::LoadAnimations(TiXmlElement *_xml, Skeleton *_skel)
{
TiXmlElement *childXml = _xml->FirstChildElement("animation");
if (childXml->FirstChildElement("animation"))
{
while (childXml)
{
this->LoadAnimationSet(childXml, _skel);
childXml->NextSiblingElement("animation");
}
}
else
this->LoadAnimationSet(_xml, _skel);
}
/////////////////////////////////////////////////
void ColladaLoader::LoadAnimationSet(TiXmlElement *_xml, Skeleton *_skel)
{
std::stringstream animName;
if (_xml->Attribute("name"))
animName << _xml->Attribute("name");
else
if (_xml->Attribute("id"))
animName << _xml->Attribute("id");
else
animName << "animation" << (_skel->GetNumAnimations() + 1);
RawSkeletonAnim animation;
TiXmlElement *animXml = _xml->FirstChildElement("animation");
while (animXml)
{
TiXmlElement *chanXml = animXml->FirstChildElement("channel");
while (chanXml)
{
std::string sourceURL = chanXml->Attribute("source");
std::string targetStr = chanXml->Attribute("target");
std::string targetBone = targetStr.substr(0, targetStr.find('/'));
char sep = '0';
if (targetStr.find('(') != std::string::npos)
sep = '(';
else
if (targetStr.find('.') != std::string::npos)
sep = '.';
std::string targetTrans;
if (sep == '0')
targetTrans = targetStr.substr(targetStr.find('/') + 1);
else
targetTrans = targetStr.substr(targetStr.find('/') + 1,
targetStr.find(sep) - targetStr.find('/') - 1);
std::string idxStr = targetStr.substr(targetStr.find(sep) + 1);
int idx1 = -1;
int idx2 = -1;
if (sep == '.')
idx1 = (idxStr == "X") ? 0 : ((idxStr == "Y") ? 1 : ((idxStr == "Z")
? 2 : ((idxStr == "ANGLE") ? 3 : -1)));
else
if (sep == '(')
{
std::string idx1Str = idxStr.substr(0, 1);
idx1 = ignition::math::parseInt(idx1Str);
if (idxStr.length() > 4)
{
std::string idx2Str = idxStr.substr(3, 1);
idx2 = ignition::math::parseInt(idx2Str);
}
}
TiXmlElement *frameTimesXml = NULL;
TiXmlElement *frameTransXml = NULL;
TiXmlElement *sampXml = this->GetElementId("sampler", sourceURL);
TiXmlElement *inputXml = sampXml->FirstChildElement("input");
while (inputXml)
{
std::string semantic = inputXml->Attribute("semantic");
if (semantic == "INPUT")
frameTimesXml = this->GetElementId("source",
inputXml->Attribute("source"));
else
if (semantic == "OUTPUT")
frameTransXml = this->GetElementId("source",
inputXml->Attribute("source"));
/// FIXME interpolation semantic?
inputXml = inputXml->NextSiblingElement("input");
}
TiXmlElement *timeArray = frameTimesXml->FirstChildElement("float_array");
std::string timeStr = timeArray->GetText();
std::vector<std::string> timeStrs;
boost::split(timeStrs, timeStr, boost::is_any_of(" "));
std::vector<double> times;
for (unsigned int i = 0; i < timeStrs.size(); ++i)
times.push_back(math::parseFloat(timeStrs[i]));
TiXmlElement *output = frameTransXml->FirstChildElement("float_array");
std::string outputStr = output->GetText();
std::vector<std::string> outputStrs;
boost::split(outputStrs, outputStr, boost::is_any_of(" "));
std::vector<double> values;
for (unsigned int i = 0; i < outputStrs.size(); ++i)
values.push_back(math::parseFloat(outputStrs[i]));
TiXmlElement *accessor =
frameTransXml->FirstChildElement("technique_common");
accessor = accessor->FirstChildElement("accessor");
unsigned int stride =
ignition::math::parseInt(accessor->Attribute("stride"));
for (unsigned int i = 0; i < times.size(); ++i)
{
if (animation[targetBone].find(times[i]) == animation[targetBone].end())
animation[targetBone][times[i]] =
_skel->GetNodeById(targetBone)->GetTransforms();
std::vector<NodeTransform> *frame = &animation[targetBone][times[i]];
for (unsigned int j = 0; j < (*frame).size(); ++j)
{
NodeTransform *nt = &((*frame)[j]);
if (nt->GetSID() == targetTrans)
{
if (idx1 != -1)
{
int index = (idx2 == -1) ? idx1 : (idx1 * 4) + idx2;
nt->SetComponent(index, values[i]);
}
else
for (unsigned int k = 0; k < stride; k++)
nt->SetComponent(k, values[(i*stride) + k]);
}
}
}
chanXml = chanXml->NextSiblingElement("channel");
}
animXml = animXml->NextSiblingElement("animation");
}
SkeletonAnimation *anim = new SkeletonAnimation(animName.str());
for (RawSkeletonAnim::iterator iter = animation.begin();
iter != animation.end(); ++iter)
for (RawNodeAnim::iterator niter = iter->second.begin();
niter != iter->second.end(); ++niter)
{
ignition::math::Matrix4d transform(ignition::math::Matrix4d::Identity);
for (unsigned int i = 0; i < niter->second.size(); ++i)
{
niter->second[i].RecalculateMatrix();
transform = transform * niter->second[i]();
}
anim->AddKeyFrame(iter->first, niter->first, transform);
}
_skel->AddAnimation(anim);
}
/////////////////////////////////////////////////
SkeletonNode* ColladaLoader::LoadSkeletonNodes(TiXmlElement *_xml,
SkeletonNode *_parent)
{
std::string name;
if (_xml->Attribute("sid"))
name = _xml->Attribute("sid");
else
name = _xml->Attribute("name");
SkeletonNode* node = new SkeletonNode(_parent, name, _xml->Attribute("id"));
if (std::string(_xml->Attribute("type")) == std::string("NODE"))
node->SetType(SkeletonNode::NODE);
this->SetSkeletonNodeTransform(_xml, node);
TiXmlElement *childXml = _xml->FirstChildElement("node");
while (childXml)
{
this->LoadSkeletonNodes(childXml, node);
childXml = childXml->NextSiblingElement("node");
}
return node;
}
/////////////////////////////////////////////////
void ColladaLoader::SetSkeletonNodeTransform(TiXmlElement *_elem,
SkeletonNode *_node)
{
ignition::math::Matrix4d transform(ignition::math::Matrix4d::Identity);
if (_elem->FirstChildElement("matrix"))
{
std::string matrixStr = _elem->FirstChildElement("matrix")->GetText();
std::istringstream iss(matrixStr);
std::vector<double> values(16);
for (unsigned int i = 0; i < 16; ++i)
iss >> values[i];
transform.Set(values[0], values[1], values[2], values[3],
values[4], values[5], values[6], values[7],
values[8], values[9], values[10], values[11],
values[12], values[13], values[14], values[15]);
NodeTransform nt(transform);
nt.SetSourceValues(transform);
if (_elem->FirstChildElement("matrix")->Attribute("sid"))
nt.SetSID(_elem->FirstChildElement("matrix")->Attribute("sid"));
_node->AddRawTransform(nt);
}
else
{
if (_elem->FirstChildElement("translate"))
{
std::string transStr = _elem->FirstChildElement("translate")->GetText();
ignition::math::Vector3d translate;
translate = boost::lexical_cast<ignition::math::Vector3d>(transStr);
// translate *= this->dataPtr->meter;
transform.Translate(translate);
NodeTransform nt(transform);
if (_elem->FirstChildElement("translate")->Attribute("sid"))
nt.SetSID(_elem->FirstChildElement("translate")->Attribute("sid"));
nt.SetType(NodeTransform::TRANSLATE);
nt.SetSourceValues(translate);
_node->AddRawTransform(nt);
}
TiXmlElement *rotateXml = _elem->FirstChildElement("rotate");
while (rotateXml)
{
ignition::math::Matrix3d mat;
ignition::math::Vector3d axis;
double angle;
std::string rotateStr = rotateXml->GetText();
std::istringstream iss(rotateStr);
iss >> axis.X() >> axis.Y() >> axis.Z();
iss >> angle;
mat.Axis(axis, IGN_DTOR(angle));
ignition::math::Matrix4d mat4(ignition::math::Matrix4d::Identity);
mat4 = mat;
NodeTransform nt(mat4);
if (rotateXml->Attribute("sid"))
nt.SetSID(rotateXml->Attribute("sid"));
nt.SetType(NodeTransform::ROTATE);
nt.SetSourceValues(axis, angle);
_node->AddRawTransform(nt);
transform = transform * mat4;
rotateXml = rotateXml->NextSiblingElement("rotate");
}
if (_elem->FirstChildElement("scale"))
{
std::string scaleStr = _elem->FirstChildElement("scale")->GetText();
ignition::math::Vector3d scale;
scale = boost::lexical_cast<ignition::math::Vector3d>(scaleStr);
ignition::math::Matrix4d scaleMat;
scaleMat.Scale(scale);
NodeTransform nt(scaleMat);
if (_elem->FirstChildElement("matrix")->Attribute("sid"))
nt.SetSID(_elem->FirstChildElement("matrix")->Attribute("sid"));
nt.SetType(NodeTransform::SCALE);
nt.SetSourceValues(scale);
_node->AddRawTransform(nt);
transform = transform * scaleMat;
}
}
_node->SetTransform(transform);
}
/////////////////////////////////////////////////
void ColladaLoader::LoadGeometry(TiXmlElement *_xml,
const ignition::math::Matrix4d &_transform, Mesh *_mesh)
{
TiXmlElement *meshXml = _xml->FirstChildElement("mesh");
TiXmlElement *childXml;
if (!meshXml)
return;
childXml = meshXml->FirstChildElement("triangles");
while (childXml)
{
this->LoadTriangles(childXml, _transform, _mesh);
childXml = childXml->NextSiblingElement("triangles");
}
childXml = meshXml->FirstChildElement("polylist");
while (childXml)
{
this->LoadPolylist(childXml, _transform, _mesh);
childXml = childXml->NextSiblingElement("polylist");
}
childXml = meshXml->FirstChildElement("lines");
while (childXml)
{
this->LoadLines(childXml, _transform, _mesh);
childXml = childXml->NextSiblingElement("lines");
}
}
/////////////////////////////////////////////////
TiXmlElement *ColladaLoader::GetElementId(const std::string &_name,
const std::string &_id)
{
return this->GetElementId(this->dataPtr->colladaXml, _name, _id);
}
/////////////////////////////////////////////////
TiXmlElement *ColladaLoader::GetElementId(TiXmlElement *_parent,
const std::string &_name,
const std::string &_id)
{
std::string id = _id;
if (id.length() > 0 && id[0] == '#')
id.erase(0, 1);
if ((id.empty() && _parent->Value() == _name) ||
(_parent->Attribute("id") && _parent->Attribute("id") == id) ||
(_parent->Attribute("sid") && _parent->Attribute("sid") == id))
{
return _parent;
}
TiXmlElement *elem = _parent->FirstChildElement();
while (elem)
{
TiXmlElement *result = this->GetElementId(elem, _name, _id);
if (result)
{
return result;
}
elem = elem->NextSiblingElement();
}
return NULL;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadVertices(const std::string &_id,
const ignition::math::Matrix4d &_transform,
std::vector<ignition::math::Vector3d> &_verts,
std::vector<ignition::math::Vector3d> &_norms)
{
std::map<unsigned int, unsigned int> vertDup;
std::map<unsigned int, unsigned int> normDup;
this->LoadVertices(_id, _transform, _verts, _norms, vertDup, normDup);
}
/////////////////////////////////////////////////
void ColladaLoader::LoadVertices(const std::string &_id,
const ignition::math::Matrix4d &_transform,
std::vector<ignition::math::Vector3d> &_verts,
std::vector<ignition::math::Vector3d> &_norms,
std::map<unsigned int, unsigned int> &_vertDups,
std::map<unsigned int, unsigned int> &_normDups)
{
TiXmlElement *verticesXml = this->GetElementId(this->dataPtr->colladaXml,
"vertices", _id);
if (!verticesXml)
{
gzerr << "Unable to find vertices[" << _id << "] in collada file\n";
return;
}
TiXmlElement *inputXml = verticesXml->FirstChildElement("input");
while (inputXml)
{
std::string semantic = inputXml->Attribute("semantic");
std::string sourceStr = inputXml->Attribute("source");
if (semantic == "NORMAL")
{
this->LoadNormals(sourceStr, _transform, _norms, _normDups);
}
else if (semantic == "POSITION")
{
this->LoadPositions(sourceStr, _transform, _verts, _vertDups);
}
inputXml = inputXml->NextSiblingElement("input");
}
}
/////////////////////////////////////////////////
void ColladaLoader::LoadPositions(const std::string &_id,
const ignition::math::Matrix4d &_transform,
std::vector<ignition::math::Vector3d> &_values,
std::map<unsigned int, unsigned int> &_duplicates)
{
if (this->dataPtr->positionIds.find(_id) != this->dataPtr->positionIds.end())
{
_values = this->dataPtr->positionIds[_id];
_duplicates = this->dataPtr->positionDuplicateMap[_id];
return;
}
TiXmlElement *sourceXml = this->GetElementId("source", _id);
if (!sourceXml)
{
gzerr << "Unable to find source\n";
return;
}
TiXmlElement *floatArrayXml = sourceXml->FirstChildElement("float_array");
if (!floatArrayXml || !floatArrayXml->GetText())
{
int count = 1;
if (floatArrayXml && floatArrayXml->Attribute("count"))
{
try
{
count = boost::lexical_cast<int>(floatArrayXml->Attribute("count"));
}
catch(...)
{
// Do nothing. Messages are printed out below.
}
}
if (count)
{
gzerr << "Vertex source missing float_array element, "
<< "or count is invalid.\n";
}
else
{
gzlog << "Vertex source has a float_array with a count of zero. "
<< "This is likely not desired\n";
}
return;
}
std::string valueStr = floatArrayXml->GetText();
boost::unordered_map<ignition::math::Vector3d,
unsigned int, Vector3Hash> unique;
std::vector<std::string> strs;
std::vector<std::string>::iterator iter, end;
boost::split(strs, valueStr, boost::is_any_of(" "));
end = strs.end();
for (iter = strs.begin(); iter != end; iter += 3)
{
ignition::math::Vector3d vec(math::parseFloat(*iter),
ignition::math::parseFloat(*(iter+1)),
ignition::math::parseFloat(*(iter+2)));
vec = _transform * vec;
_values.push_back(vec);
// create a map of duplicate indices
if (unique.find(vec) != unique.end())
_duplicates[_values.size()-1] = unique[vec];
else
unique[vec] = _values.size()-1;
}
this->dataPtr->positionDuplicateMap[_id] = _duplicates;
this->dataPtr->positionIds[_id] = _values;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadNormals(const std::string &_id,
const ignition::math::Matrix4d &_transform,
std::vector<ignition::math::Vector3d> &_values,
std::map<unsigned int, unsigned int> &_duplicates)
{
if (this->dataPtr->normalIds.find(_id) != this->dataPtr->normalIds.end())
{
_values = this->dataPtr->normalIds[_id];
_duplicates = this->dataPtr->normalDuplicateMap[_id];
return;
}
ignition::math::Matrix4d rotMat = _transform;
rotMat.Translate(ignition::math::Vector3d::Zero);
TiXmlElement *normalsXml = this->GetElementId("source", _id);
if (!normalsXml)
{
gzerr << "Unable to find normals[" << _id << "] in collada file\n";
return;
}
TiXmlElement *floatArrayXml = normalsXml->FirstChildElement("float_array");
if (!floatArrayXml || !floatArrayXml->GetText())
{
int count = 1;
if (floatArrayXml && floatArrayXml->Attribute("count"))
{
try
{
count = boost::lexical_cast<int>(floatArrayXml->Attribute("count"));
}
catch(...)
{
// Do nothing. Messages are printed out below.
}
}
if (count)
{
gzwarn << "Normal source missing float_array element, or count is "
<< "invalid.\n";
}
else
{
gzlog << "Normal source has a float_array with a count of zero. "
<< "This is likely not desired\n";
}
return;
}
boost::unordered_map<ignition::math::Vector3d,
unsigned int, Vector3Hash> unique;
std::string valueStr = floatArrayXml->GetText();
std::istringstream iss(valueStr);
do
{
ignition::math::Vector3d vec;
iss >> vec.X() >> vec.Y() >> vec.Z();
if (iss)
{
vec = rotMat * vec;
vec.Normalize();
_values.push_back(vec);
// create a map of duplicate indices
if (unique.find(vec) != unique.end())
_duplicates[_values.size()-1] = unique[vec];
else
unique[vec] = _values.size()-1;
}
} while (iss);
this->dataPtr->normalDuplicateMap[_id] = _duplicates;
this->dataPtr->normalIds[_id] = _values;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadTexCoords(const std::string &_id,
std::vector<ignition::math::Vector2d> &_values,
std::map<unsigned int, unsigned int> &_duplicates)
{
if (this->dataPtr->texcoordIds.find(_id) != this->dataPtr->texcoordIds.end())
{
_values = this->dataPtr->texcoordIds[_id];
_duplicates = this->dataPtr->texcoordDuplicateMap[_id];
return;
}
int stride = 0;
int texCount = 0;
int totCount = 0;
// Get the source element for the texture coordinates.
TiXmlElement *xml = this->GetElementId("source", _id);
if (!xml)
{
gzerr << "Unable to find tex coords[" << _id << "] in collada file\n";
return;
}
// Get the array of float values. These are the raw values for the texture
// coordinates.
TiXmlElement *floatArrayXml = xml->FirstChildElement("float_array");
if (!floatArrayXml || !floatArrayXml->GetText())
{
int count = 1;
if (floatArrayXml && floatArrayXml->Attribute("count"))
{
try
{
count = boost::lexical_cast<int>(floatArrayXml->Attribute("count"));
}
catch(...)
{
// Do nothing. Messages are printed out below.
}
}
if (count)
{
gzerr << "Normal source missing float_array element, or count is "
<< "invalid.\n";
}
else
{
gzlog << "Normal source has a float_array with a count of zero. "
<< "This is likely not desired\n";
}
return;
}
// Read in the total number of texture coordinate values
else if (floatArrayXml->Attribute("count"))
totCount = boost::lexical_cast<int>(floatArrayXml->Attribute("count"));
else
{
gzerr << "<float_array> has no count attribute in texture coordinate "
<< "element with id[" << _id << "]\n";
return;
}
// The technique_common holds an <accessor> element that indicates how to
// parse the float array.
xml = xml->FirstChildElement("technique_common");
if (!xml)
{
gzerr << "Unable to find technique_common element for texture "
<< "coordinates with id[" << _id << "]\n";
return;
}
// Get the accessor XML element.
xml = xml->FirstChildElement("accessor");
if (!xml)
{
gzerr << "Unable to find <accessor> as a child of <technique_common> "
<< "for texture coordinates with id[" << _id << "]\n";
return;
}
// Read in the stride for the texture coordinate values. The stride
// indicates the number of values in the float array the comprise
// a complete texture coordinate.
if (xml->Attribute("stride"))
stride = boost::lexical_cast<int>(xml->Attribute("stride"));
else
{
gzerr << "<accessor> has no stride attribute in texture coordinate element "
<< "with id[" << _id << "]\n";
return;
}
// Read in the count of texture coordinates.
if (xml->Attribute("count"))
texCount = boost::lexical_cast<int>(xml->Attribute("count"));
else
{
gzerr << "<accessor> has no count attribute in texture coordinate element "
<< "with id[" << _id << "]\n";
return;
}
// \TODO This is a good a GZ_ASSERT
// The total number of texture values should equal the stride multiplied
// by the number of texture coordinates.
if (texCount * stride != totCount)
{
gzerr << "Error reading texture coordinates. Coordinate counts in element "
"with id[" << _id << "] do not add up correctly\n";
return;
}
// Nothing to read. Don't print a warning because the collada file is
// correct.
if (totCount == 0)
return;
boost::unordered_map<ignition::math::Vector2d,
unsigned int, Vector2dHash> unique;
// Read the raw texture values, and split them on spaces.
std::string valueStr = floatArrayXml->GetText();
std::vector<std::string> values;
boost::split(values, valueStr, boost::is_any_of(" "));
// Read in all the texture coordinates.
for (int i = 0; i < totCount; i += stride)
{
// We only handle 2D texture coordinates right now.
ignition::math::Vector2d vec(boost::lexical_cast<double>(values[i]),
1.0 - boost::lexical_cast<double>(values[i+1]));
_values.push_back(vec);
// create a map of duplicate indices
if (unique.find(vec) != unique.end())
{
_duplicates[_values.size()-1] = unique[vec];
}
else
unique[vec] = _values.size()-1;
}
this->dataPtr->texcoordDuplicateMap[_id] = _duplicates;
this->dataPtr->texcoordIds[_id] = _values;
}
/////////////////////////////////////////////////
Material *ColladaLoader::LoadMaterial(const std::string &_name)
{
if (this->dataPtr->materialIds.find(_name)
!= this->dataPtr->materialIds.end())
{
return this->dataPtr->materialIds[_name];
}
TiXmlElement *matXml = this->GetElementId("material", _name);
if (!matXml || !matXml->FirstChildElement("instance_effect"))
return NULL;
Material *mat = new Material();
std::string effectName =
matXml->FirstChildElement("instance_effect")->Attribute("url");
TiXmlElement *effectXml = this->GetElementId("effect", effectName);
TiXmlElement *commonXml = effectXml->FirstChildElement("profile_COMMON");
if (commonXml)
{
TiXmlElement *techniqueXml = commonXml->FirstChildElement("technique");
TiXmlElement *lambertXml = techniqueXml->FirstChildElement("lambert");
TiXmlElement *phongXml = techniqueXml->FirstChildElement("phong");
TiXmlElement *blinnXml = techniqueXml->FirstChildElement("blinn");
if (lambertXml)
{
this->LoadColorOrTexture(lambertXml, "ambient", mat);
this->LoadColorOrTexture(lambertXml, "emission", mat);
this->LoadColorOrTexture(lambertXml, "diffuse", mat);
if (lambertXml->FirstChildElement("transparency"))
{
mat->SetTransparency(
this->LoadFloat(lambertXml->FirstChildElement("transparency")));
}
if (lambertXml->FirstChildElement("transparent"))
{
TiXmlElement *transXml = lambertXml->FirstChildElement("transparent");
this->LoadTransparent(transXml, mat);
}
}
else if (phongXml)
{
this->LoadColorOrTexture(phongXml, "ambient", mat);
this->LoadColorOrTexture(phongXml, "emission", mat);
this->LoadColorOrTexture(phongXml, "specular", mat);
this->LoadColorOrTexture(phongXml, "diffuse", mat);
if (phongXml->FirstChildElement("shininess"))
mat->SetShininess(
this->LoadFloat(phongXml->FirstChildElement("shininess")));
if (phongXml->FirstChildElement("transparency"))
mat->SetTransparency(
this->LoadFloat(phongXml->FirstChildElement("transparency")));
if (phongXml->FirstChildElement("transparent"))
{
TiXmlElement *transXml = phongXml->FirstChildElement("transparent");
this->LoadTransparent(transXml, mat);
}
}
else if (blinnXml)
{
this->LoadColorOrTexture(blinnXml, "ambient", mat);
this->LoadColorOrTexture(blinnXml, "emission", mat);
this->LoadColorOrTexture(blinnXml, "specular", mat);
this->LoadColorOrTexture(blinnXml, "diffuse", mat);
if (blinnXml->FirstChildElement("shininess"))
mat->SetShininess(
this->LoadFloat(blinnXml->FirstChildElement("shininess")));
if (blinnXml->FirstChildElement("transparency"))
mat->SetTransparency(
this->LoadFloat(blinnXml->FirstChildElement("transparency")));
if (blinnXml->FirstChildElement("transparent"))
{
TiXmlElement *transXml = blinnXml->FirstChildElement("transparent");
this->LoadTransparent(transXml, mat);
}
}
}
TiXmlElement *glslXml = effectXml->FirstChildElement("profile_GLSL");
if (glslXml)
gzerr << "profile_GLSL unsupported\n";
TiXmlElement *cgXml = effectXml->FirstChildElement("profile_CG");
if (cgXml)
gzerr << "profile_CG unsupported\n";
this->dataPtr->materialIds[_name] = mat;
return mat;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadColorOrTexture(TiXmlElement *_elem,
const std::string &_type, Material *_mat)
{
if (!_elem || !_elem->FirstChildElement(_type))
return;
TiXmlElement *typeElem = _elem->FirstChildElement(_type);
if (typeElem->FirstChildElement("color"))
{
std::string colorStr = typeElem->FirstChildElement("color")->GetText();
Color color = boost::lexical_cast<Color>(colorStr);
if (_type == "diffuse")
_mat->SetDiffuse(color);
else if (_type == "ambient")
_mat->SetAmbient(color);
else if (_type == "emission")
_mat->SetEmissive(color);
else if (_type == "specular")
_mat->SetSpecular(color);
}
else if (typeElem->FirstChildElement("texture"))
{
_mat->SetLighting(true);
TiXmlElement *imageXml = NULL;
std::string textureName =
typeElem->FirstChildElement("texture")->Attribute("texture");
TiXmlElement *textureXml = this->GetElementId("newparam", textureName);
if (textureXml)
{
if (std::string(textureXml->Value()) == "image")
{
imageXml = textureXml;
}
else
{
TiXmlElement *sampler = textureXml->FirstChildElement("sampler2D");
if (sampler)
{
std::string sourceName =
sampler->FirstChildElement("source")->GetText();
TiXmlElement *sourceXml = this->GetElementId("newparam", sourceName);
if (sourceXml)
{
TiXmlElement *surfaceXml = sourceXml->FirstChildElement("surface");
if (surfaceXml && surfaceXml->FirstChildElement("init_from"))
{
imageXml = this->GetElementId("image",
surfaceXml->FirstChildElement("init_from")->GetText());
}
}
}
}
}
else
{
imageXml = this->GetElementId("image", textureName);
}
if (imageXml && imageXml->FirstChildElement("init_from"))
{
std::string imgFile =
imageXml->FirstChildElement("init_from")->GetText();
_mat->SetTextureImage(imgFile, this->dataPtr->path);
}
}
}
/////////////////////////////////////////////////
void ColladaLoader::LoadPolylist(TiXmlElement *_polylistXml,
const ignition::math::Matrix4d &_transform,
Mesh *_mesh)
{
// This function parses polylist types in collada into
// a set of triangle meshes. The assumption is that
// each polylist polygon is convex, and we do decomposion
// by anchoring each triangle about vertex 0 or each polygon
SubMesh *subMesh = new SubMesh;
subMesh->SetName(this->dataPtr->currentNodeName);
bool combinedVertNorms = false;
subMesh->SetPrimitiveType(SubMesh::TRIANGLES);
if (_polylistXml->Attribute("material"))
{
std::map<std::string, std::string>::iterator iter;
std::string matStr = _polylistXml->Attribute("material");
int matIndex = -1;
iter = this->dataPtr->materialMap.find(matStr);
if (iter != this->dataPtr->materialMap.end())
matStr = iter->second;
common::Material *mat = this->LoadMaterial(matStr);
matIndex = _mesh->GetMaterialIndex(mat);
if (matIndex < 0)
matIndex = _mesh->AddMaterial(mat);
if (matIndex < 0)
gzwarn << "Unable to add material[" << matStr << "]\n";
else
subMesh->SetMaterialIndex(matIndex);
}
TiXmlElement *polylistInputXml = _polylistXml->FirstChildElement("input");
std::vector<ignition::math::Vector3d> verts;
std::vector<ignition::math::Vector3d> norms;
std::vector<ignition::math::Vector2d> texcoords;
const unsigned int VERTEX = 0;
const unsigned int NORMAL = 1;
const unsigned int TEXCOORD = 2;
unsigned int otherSemantics = TEXCOORD + 1;
// look up table of position/normal/texcoord duplicate indices
std::map<unsigned int, unsigned int> texDupMap;
std::map<unsigned int, unsigned int> normalDupMap;
std::map<unsigned int, unsigned int> positionDupMap;
ignition::math::Matrix4d bindShapeMat(ignition::math::Matrix4d::Identity);
if (_mesh->HasSkeleton())
bindShapeMat = _mesh->GetSkeleton()->BindShapeTransform();
// read input elements. A vector of int is used because there can be
// multiple TEXCOORD inputs.
std::map<const unsigned int, std::set<int>> inputs;
unsigned int inputSize = 0;
while (polylistInputXml)
{
std::string semantic = polylistInputXml->Attribute("semantic");
std::string source = polylistInputXml->Attribute("source");
std::string offset = polylistInputXml->Attribute("offset");
if (semantic == "VERTEX")
{
unsigned int count = norms.size();
this->LoadVertices(source, _transform, verts, norms,
positionDupMap, normalDupMap);
if (norms.size() > count)
combinedVertNorms = true;
inputs[VERTEX].insert(ignition::math::parseInt(offset));
}
else if (semantic == "NORMAL")
{
this->LoadNormals(source, _transform, norms, normalDupMap);
combinedVertNorms = false;
inputs[NORMAL].insert(ignition::math::parseInt(offset));
}
else if (semantic == "TEXCOORD")
{
this->LoadTexCoords(source, texcoords, texDupMap);
inputs[TEXCOORD].insert(ignition::math::parseInt(offset));
}
else
{
inputs[otherSemantics++].insert(ignition::math::parseInt(offset));
gzwarn << "Polylist input semantic: '" << semantic << "' is currently"
<< " not supported" << std::endl;
}
polylistInputXml = polylistInputXml->NextSiblingElement("input");
}
for (const auto &input : inputs)
inputSize += input.second.size();
// read vcount
// break poly into triangles
// if vcount >= 4, anchor around 0 (note this is bad for concave elements)
// e.g. if vcount = 4, break into triangle 1: [0,1,2], triangle 2: [0,2,3]
std::vector<std::string> vcountStrs;
TiXmlElement *vcountXml = _polylistXml->FirstChildElement("vcount");
std::string vcountStr = vcountXml->GetText();
boost::split(vcountStrs, vcountStr, boost::is_any_of(" "));
std::vector<int> vcounts;
for (unsigned int j = 0; j < vcountStrs.size(); ++j)
vcounts.push_back(math::parseInt(vcountStrs[j]));
// read p
TiXmlElement *pXml = _polylistXml->FirstChildElement("p");
std::string pStr = pXml->GetText();
// vertexIndexMap is a map of collada vertex index to Gazebo submesh vertex
// indices, used for identifying vertices that can be shared.
std::map<unsigned int, std::vector<GeometryIndices> > vertexIndexMap;
unsigned int *values = new unsigned int[inputSize];
memset(values, 0, inputSize);
std::vector<std::string> strs;
boost::split(strs, pStr, boost::is_any_of(" "));
std::vector<std::string>::iterator strsIter = strs.begin();
for (unsigned int l = 0; l < vcounts.size(); ++l)
{
// put us at the beginning of the polygon list
if (l > 0)
strsIter += inputSize*vcounts[l-1];
for (unsigned int k = 2; k < (unsigned int)vcounts[l]; ++k)
{
// if vcounts[l] = 5, then read 0,1,2, then 0,2,3, 0,3,4,...
// here k = the last number in the series
// j is the triangle loop
for (unsigned int j = 0; j < 3; ++j)
{
// break polygon into triangles
unsigned int triangle_index;
if (j == 0)
triangle_index = 0;
if (j == 1)
triangle_index = (k-1)*inputSize;
if (j == 2)
triangle_index = (k)*inputSize;
for (unsigned int i = 0; i < inputSize; ++i)
{
values[i] = ignition::math::parseInt(strsIter[triangle_index+i]);
/*gzerr << "debug parsing "
<< " poly-i[" << l
<< "] tri-end-index[" << k
<< "] tri-vertex-i[" << j
<< "] triangle[" << triangle_index
<< "] input[" << i
<< "] value[" << values[i]
<< "]\n"; */
}
unsigned int daeVertIndex = 0;
bool addIndex = inputs[VERTEX].empty();
// find a set of vertex/normal/texcoord that can be reused
// only do this if the mesh has vertices
if (!inputs[VERTEX].empty())
{
// Get the vertex position index value. If it is a duplicate then use
// the existing index instead
daeVertIndex = values[*inputs[VERTEX].begin()];
if (positionDupMap.find(daeVertIndex) != positionDupMap.end())
daeVertIndex = positionDupMap[daeVertIndex];
// if the vertex index has not been previously added then just add it.
if (vertexIndexMap.find(daeVertIndex) == vertexIndexMap.end())
{
addIndex = true;
}
else
{
// if the vertex index was previously added, check to see if it has
// the same normal and texcoord index values
bool toDuplicate = true;
unsigned int reuseIndex = 0;
std::vector<GeometryIndices> inputValues =
vertexIndexMap[daeVertIndex];
for (unsigned int i = 0; i < inputValues.size(); ++i)
{
GeometryIndices iv = inputValues[i];
bool normEqual = false;
bool texEqual = false;
if (!inputs[NORMAL].empty())
{
// Get the vertex normal index value. If the normal is a
// duplicate then reset the index to the first instance of the
// duplicated position
unsigned int remappedNormalIndex =
values[*inputs[NORMAL].begin()];
if (normalDupMap.find(remappedNormalIndex)
!= normalDupMap.end())
{
remappedNormalIndex = normalDupMap[remappedNormalIndex];
}
if (iv.normalIndex == remappedNormalIndex)
normEqual = true;
}
if (!inputs[TEXCOORD].empty())
{
// \todo: Add support for multiple texture maps to SubMesh.
// Here we are only using the first texture coordinates, when
// multiple could have been specified. See Gazebo issue #532.
// Get the vertex texcoord index value. If the texcoord is a
// duplicate then reset the index to the first instance of the
// duplicated texcoord
unsigned int remappedTexcoordIndex =
values[*inputs[TEXCOORD].begin()];
if (texDupMap.find(remappedTexcoordIndex) != texDupMap.end())
remappedTexcoordIndex = texDupMap[remappedTexcoordIndex];
texEqual = iv.texcoordIndex == remappedTexcoordIndex;
}
// if the vertex has matching normal and texcoord index values
// then the vertex can be reused.
if ((inputs[NORMAL].empty() || normEqual) &&
(inputs[TEXCOORD].empty() || texEqual))
{
// found a vertex that can be shared.
toDuplicate = false;
reuseIndex = iv.mappedIndex;
subMesh->AddIndex(reuseIndex);
break;
}
}
addIndex = toDuplicate;
}
}
// if the vertex index is new or can not be shared then add it
if (addIndex)
{
GeometryIndices input;
if (!inputs[VERTEX].empty())
{
subMesh->AddVertex(verts[daeVertIndex]);
unsigned int newVertIndex = subMesh->GetVertexCount()-1;
subMesh->AddIndex(newVertIndex);
if (combinedVertNorms)
subMesh->AddNormal(norms[daeVertIndex]);
if (_mesh->HasSkeleton())
{
subMesh->SetVertex(newVertIndex, bindShapeMat *
subMesh->Vertex(newVertIndex));
Skeleton *skel = _mesh->GetSkeleton();
for (unsigned int i = 0;
i < skel->GetNumVertNodeWeights(daeVertIndex); ++i)
{
std::pair<std::string, double> node_weight =
skel->GetVertNodeWeight(daeVertIndex, i);
SkeletonNode *node =
_mesh->GetSkeleton()->GetNodeByName(node_weight.first);
subMesh->AddNodeAssignment(subMesh->GetVertexCount()-1,
node->GetHandle(), node_weight.second);
}
}
input.vertexIndex = daeVertIndex;
input.mappedIndex = newVertIndex;
}
if (!inputs[NORMAL].empty())
{
unsigned int inputRemappedNormalIndex =
values[*inputs[NORMAL].begin()];
if (normalDupMap.find(inputRemappedNormalIndex)
!= normalDupMap.end())
inputRemappedNormalIndex = normalDupMap[inputRemappedNormalIndex];
subMesh->AddNormal(norms[inputRemappedNormalIndex]);
input.normalIndex = inputRemappedNormalIndex;
}
if (!inputs[TEXCOORD].empty())
{
// \todo: Add support for multiple texture maps to SubMesh.
// Here we are only using the first texture coordinates, when
// multiple could have been specified.
unsigned int inputRemappedTexcoordIndex =
values[*inputs[TEXCOORD].begin()];
if (texDupMap.find(inputRemappedTexcoordIndex) != texDupMap.end())
{
inputRemappedTexcoordIndex =
texDupMap[inputRemappedTexcoordIndex];
}
subMesh->AddTexCoord(texcoords[inputRemappedTexcoordIndex].X(),
texcoords[inputRemappedTexcoordIndex].Y());
input.texcoordIndex = inputRemappedTexcoordIndex;
}
// add the new gazebo submesh vertex index to the map
if (!inputs[VERTEX].empty())
{
std::vector<GeometryIndices> inputValues;
inputValues.push_back(input);
vertexIndexMap[daeVertIndex] = inputValues;
}
}
}
}
}
delete [] values;
_mesh->AddSubMesh(subMesh);
}
/////////////////////////////////////////////////
void ColladaLoader::LoadTriangles(TiXmlElement *_trianglesXml,
const ignition::math::Matrix4d &_transform,
Mesh *_mesh)
{
SubMesh *subMesh = new SubMesh;
subMesh->SetName(this->dataPtr->currentNodeName);
bool combinedVertNorms = false;
subMesh->SetPrimitiveType(SubMesh::TRIANGLES);
if (_trianglesXml->Attribute("material"))
{
std::map<std::string, std::string>::iterator iter;
std::string matStr = _trianglesXml->Attribute("material");
int matIndex = -1;
iter = this->dataPtr->materialMap.find(matStr);
if (iter != this->dataPtr->materialMap.end())
matStr = iter->second;
common::Material *mat = this->LoadMaterial(matStr);
matIndex = _mesh->GetMaterialIndex(mat);
if (matIndex < 0)
matIndex = _mesh->AddMaterial(mat);
if (matIndex < 0)
gzwarn << "Unable to add material[" << matStr << "]\n";
else
subMesh->SetMaterialIndex(matIndex);
}
TiXmlElement *trianglesInputXml = _trianglesXml->FirstChildElement("input");
std::vector<ignition::math::Vector3d> verts;
std::vector<ignition::math::Vector3d> norms;
std::vector<ignition::math::Vector2d> texcoords;
const unsigned int VERTEX = 0;
const unsigned int NORMAL = 1;
const unsigned int TEXCOORD = 2;
unsigned int otherSemantics = TEXCOORD + 1;
bool hasVertices = false;
bool hasNormals = false;
bool hasTexcoords = false;
unsigned int offsetSize = 0;
// read input elements. A vector of int is used because there can be
// multiple TEXCOORD inputs.
std::map<const unsigned int, std::set<int>> inputs;
// look up table of position/normal/texcoord duplicate indices
std::map<unsigned int, unsigned int> texDupMap;
std::map<unsigned int, unsigned int> normalDupMap;
std::map<unsigned int, unsigned int> positionDupMap;
while (trianglesInputXml)
{
std::string semantic = trianglesInputXml->Attribute("semantic");
std::string source = trianglesInputXml->Attribute("source");
std::string offset = trianglesInputXml->Attribute("offset");
if (semantic == "VERTEX")
{
unsigned int count = norms.size();
this->LoadVertices(source, _transform, verts, norms,
positionDupMap, normalDupMap);
if (norms.size() > count)
combinedVertNorms = true;
inputs[VERTEX].insert(ignition::math::parseInt(offset));
hasVertices = true;
}
else if (semantic == "NORMAL")
{
this->LoadNormals(source, _transform, norms, normalDupMap);
combinedVertNorms = false;
inputs[NORMAL].insert(ignition::math::parseInt(offset));
hasNormals = true;
}
else if (semantic == "TEXCOORD")
{
// we currently only support one set of UVs
this->LoadTexCoords(source, texcoords, texDupMap);
inputs[TEXCOORD].insert(ignition::math::parseInt(offset));
hasTexcoords = true;
}
else
{
inputs[otherSemantics++].insert(ignition::math::parseInt(offset));
gzwarn << "Triangle input semantic: '" << semantic << "' is currently"
<< " not supported" << std::endl;
}
trianglesInputXml = trianglesInputXml->NextSiblingElement("input");
}
for (const auto &input : inputs)
offsetSize += input.second.size();
TiXmlElement *pXml = _trianglesXml->FirstChildElement("p");
if (!pXml || !pXml->GetText())
{
int count = 1;
if (_trianglesXml->Attribute("count"))
{
try
{
count = boost::lexical_cast<int>(_trianglesXml->Attribute("count"));
}
catch(...)
{
// Do nothing. Messages are printed out below.
}
}
// It's possible that the triangle count is zero. In this case, we
// should not output an error message
if (count)
{
gzerr << "Collada file[" << this->dataPtr->filename
<< "] is invalid. Loading what we can...\n";
}
else
{
gzlog << "Triangle input has a count of zero. "
<< "This is likely not desired\n";
}
return;
}
std::string pStr = pXml->GetText();
// Collada format allows normals and texcoords to have their own set of
// indices for more efficient storage of data but opengl only supports one
// index buffer. So we need to reorder normals/texcoord to match the vertex
// index and duplicate any vertices that have the same index but different
// normal/texcoord.
// vertexIndexMap is a map of collada vertex index to Gazebo submesh vertex
// indices, used for identifying vertices that can be shared.
std::map<unsigned int, std::vector<GeometryIndices> > vertexIndexMap;
unsigned int *values = new unsigned int[offsetSize];
std::vector<std::string> strs;
boost::split(strs, pStr, boost::is_any_of(" "));
for (unsigned int j = 0; j < strs.size(); j += offsetSize)
{
for (unsigned int i = 0; i < offsetSize; ++i)
values[i] = ignition::math::parseInt(strs[j+i]);
unsigned int daeVertIndex = 0;
bool addIndex = !hasVertices;
// find a set of vertex/normal/texcoord that can be reused
// only do this if the mesh has vertices
if (hasVertices)
{
// Get the vertex position index value. If the position is a duplicate
// then reset the index to the first instance of the duplicated position
daeVertIndex = values[*inputs[VERTEX].begin()];
if (positionDupMap.find(daeVertIndex) != positionDupMap.end())
daeVertIndex = positionDupMap[daeVertIndex];
// if the vertex index has not been previously added then just add it.
if (vertexIndexMap.find(daeVertIndex) == vertexIndexMap.end())
{
addIndex = true;
}
else
{
// if the vertex index was previously added, check to see if it has the
// same normal and texcoord index values
bool toDuplicate = true;
unsigned int reuseIndex = 0;
std::vector<GeometryIndices> inputValues = vertexIndexMap[daeVertIndex];
for (unsigned int i = 0; i < inputValues.size(); ++i)
{
GeometryIndices iv = inputValues[i];
bool normEqual = false;
bool texEqual = false;
if (hasNormals)
{
// Get the vertex normal index value. If the normal is a duplicate
// then reset the index to the first instance of the duplicated
// position
unsigned int remappedNormalIndex = values[*inputs[NORMAL].begin()];
if (normalDupMap.find(remappedNormalIndex) != normalDupMap.end())
remappedNormalIndex = normalDupMap[remappedNormalIndex];
if (iv.normalIndex == remappedNormalIndex)
normEqual = true;
}
if (hasTexcoords)
{
// Get the vertex texcoord index value. If the texcoord is a
// duplicate then reset the index to the first instance of the
// duplicated texcoord
unsigned int remappedTexcoordIndex =
values[*inputs[TEXCOORD].begin()];
if (texDupMap.find(remappedTexcoordIndex) != texDupMap.end())
remappedTexcoordIndex = texDupMap[remappedTexcoordIndex];
if (iv.texcoordIndex == remappedTexcoordIndex)
texEqual = true;
}
// if the vertex has matching normal and texcoord index values then
// the vertex can be reused.
if ((!hasNormals || normEqual) && (!hasTexcoords || texEqual))
{
// found a vertex that can be shared.
toDuplicate = false;
reuseIndex = iv.mappedIndex;
subMesh->AddIndex(reuseIndex);
break;
}
}
addIndex = toDuplicate;
}
}
// if the vertex index is new or can not be shared then add it
if (addIndex)
{
GeometryIndices input;
if (hasVertices)
{
subMesh->AddVertex(verts[daeVertIndex]);
unsigned int newVertIndex = subMesh->GetVertexCount()-1;
subMesh->AddIndex(newVertIndex);
if (combinedVertNorms)
subMesh->AddNormal(norms[daeVertIndex]);
if (_mesh->HasSkeleton())
{
Skeleton *skel = _mesh->GetSkeleton();
for (unsigned int i = 0;
i < skel->GetNumVertNodeWeights(values[daeVertIndex]); ++i)
{
std::pair<std::string, double> node_weight =
skel->GetVertNodeWeight(values[daeVertIndex], i);
SkeletonNode *node =
_mesh->GetSkeleton()->GetNodeByName(node_weight.first);
subMesh->AddNodeAssignment(subMesh->GetVertexCount()-1,
node->GetHandle(), node_weight.second);
}
}
input.vertexIndex = daeVertIndex;
input.mappedIndex = newVertIndex;
}
if (hasNormals)
{
unsigned int inputRemappedNormalIndex = values[*inputs[NORMAL].begin()];
if (normalDupMap.find(inputRemappedNormalIndex) != normalDupMap.end())
inputRemappedNormalIndex = normalDupMap[inputRemappedNormalIndex];
subMesh->AddNormal(norms[inputRemappedNormalIndex]);
input.normalIndex = inputRemappedNormalIndex;
}
if (hasTexcoords)
{
unsigned int inputRemappedTexcoordIndex =
values[*inputs[TEXCOORD].begin()];
if (texDupMap.find(inputRemappedTexcoordIndex) != texDupMap.end())
inputRemappedTexcoordIndex = texDupMap[inputRemappedTexcoordIndex];
subMesh->AddTexCoord(texcoords[inputRemappedTexcoordIndex].X(),
texcoords[inputRemappedTexcoordIndex].Y());
input.texcoordIndex = inputRemappedTexcoordIndex;
}
// add the new gazebo submesh vertex index to the map
if (hasVertices)
{
std::vector<GeometryIndices> inputValues;
inputValues.push_back(input);
vertexIndexMap[daeVertIndex] = inputValues;
}
}
}
delete [] values;
_mesh->AddSubMesh(subMesh);
}
/////////////////////////////////////////////////
void ColladaLoader::LoadLines(TiXmlElement *_xml,
const ignition::math::Matrix4d &_transform,
Mesh *_mesh)
{
SubMesh *subMesh = new SubMesh;
subMesh->SetName(this->dataPtr->currentNodeName);
subMesh->SetPrimitiveType(SubMesh::LINES);
TiXmlElement *inputXml = _xml->FirstChildElement("input");
// std::string semantic = inputXml->Attribute("semantic");
std::string source = inputXml->Attribute("source");
std::vector<ignition::math::Vector3d> verts;
std::vector<ignition::math::Vector3d> norms;
this->LoadVertices(source, _transform, verts, norms);
TiXmlElement *pXml = _xml->FirstChildElement("p");
std::string pStr = pXml->GetText();
std::istringstream iss(pStr);
do
{
int a, b;
iss >> a >> b;
if (!iss)
break;
subMesh->AddVertex(verts[a]);
subMesh->AddIndex(subMesh->GetVertexCount() - 1);
subMesh->AddVertex(verts[b]);
subMesh->AddIndex(subMesh->GetVertexCount() - 1);
} while (iss);
_mesh->AddSubMesh(subMesh);
}
/////////////////////////////////////////////////
float ColladaLoader::LoadFloat(TiXmlElement *_elem)
{
float value = 0;
if (_elem->FirstChildElement("float"))
{
value =
ignition::math::parseFloat(_elem->FirstChildElement("float")->GetText());
}
return value;
}
/////////////////////////////////////////////////
void ColladaLoader::LoadTransparent(TiXmlElement *_elem, Material *_mat)
{
const char *opaqueCStr = _elem->Attribute("opaque");
if (!opaqueCStr)
{
// gzerr << "No Opaque set\n";
return;
}
// TODO: Handle transparent textures
if (_elem->FirstChildElement("color"))
{
const char *colorCStr = _elem->FirstChildElement("color")->GetText();
if (!colorCStr)
{
gzerr << "No color string\n";
return;
}
std::string opaqueStr = opaqueCStr;
std::string colorStr = colorCStr;
Color color = boost::lexical_cast<Color>(colorStr);
double srcFactor = 0;
double dstFactor = 0;
if (opaqueStr == "RGB_ZERO")
{
srcFactor = color.r * _mat->GetTransparency();
dstFactor = 1.0 - color.r * _mat->GetTransparency();
}
else if (opaqueStr == "A_ONE")
{
srcFactor = 1.0 - color.a * _mat->GetTransparency();
dstFactor = color.a * _mat->GetTransparency();
}
_mat->SetBlendFactors(srcFactor, dstFactor);
}
}