Files
OpenSceneGraph/src/osgPlugins/dae/daeRSkinning.cpp
Robert Osfield bbca791251 From Michael Platings, "Here's the all-new, all-dancing DAE plugin, with support for reading
osgAnimation. It's been tested with the majority of the samples in the
COLLADA test repository and works with all of them either as well as, or
better than, the version of the plugin currently in SVN.

Known issue: vertex animation (AKA morphing) doesn't work at present,
but that's a relatively unpopular method of animating so it's not high
on my priority list."

Follow up email:
"I've been informed that the previous DAE submission didn't build on
unix, so here's the submission again with the fixes.  Thanks to Gregory Potdevin and Benjamin Bozou.
Also, my apologies to Roland for not crediting his part in making DAE
animation happen, my work was indeed built on top of his work. Thanks
also to Marius Heise and of course Cedric Pinson."

Changes by Robert Osfield, fixed compile issues when compile without C* automatic conversion enabled in ref_ptr<>
and constructor initialization fixes to address some warnings under gcc.
2010-02-26 14:41:50 +00:00

534 lines
19 KiB
C++

/*
* Copyright 2006 Sony Computer Entertainment Inc.
*
* Licensed under the SCEA Shared Source License, Version 1.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://research.scea.com/scea_shared_source_license.html
*
* 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 "daeReader.h"
#include <dae.h>
#include <dae/domAny.h>
#include <dom/domCOLLADA.h>
#include <dom/domInstanceWithExtra.h>
#include <dom/domConstants.h>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/UpdateMatrixTransform>
using namespace osgDAE;
domNode* daeReader::getRootJoint(domNode* joint) const
{
int depth = 0;
while (domNode* parent = daeSafeCast<domNode>(joint->getParent()))
{
if (isJoint(parent))
{
joint = parent;
++depth;
}
else
{
break;
}
}
return joint;
}
domNode* daeReader::findJointNode(daeElement* searchFrom, domInstance_controller* pDomInstanceController) const
{
domController *pDomController = daeSafeCast<domController>(getElementFromURI(pDomInstanceController->getUrl()));
domSkin::domJoints* pDomSkinJoints = pDomController->getSkin()->getJoints();
domInputLocal_Array domInputs = pDomSkinJoints->getInput_array();
domSource* pDomJointsSource = NULL;
for (size_t i=0; i < domInputs.getCount(); i++)
{
if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT))
{
pDomJointsSource = daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (!pDomJointsSource)
{
osg::notify( osg::WARN ) << "Could not find skin joints source '" << domInputs[i]->getSource().getURI() << "'" <<std::endl;
return NULL;
}
}
}
if (domIDREF_array* pDomIDREFs = pDomJointsSource->getIDREF_array())
{
if (pDomIDREFs->getCount())
{
return daeSafeCast< domNode >(getElementFromIDRef(pDomIDREFs->getValue().get(0)));
}
}
else if (domName_array* pDomNames = pDomJointsSource->getName_array())
{
if (pDomNames->getCount())
{
daeString target = pDomNames->getValue().get(0);
daeSIDResolver resolver(searchFrom, target);
return daeSafeCast<domNode>(resolver.getElement());
}
}
osg::notify( osg::WARN ) << "No valid names or IDREFS array in <skin>" <<std::endl;
return NULL;
}
domNode* daeReader::findSkeletonNode(daeElement* searchFrom, domInstance_controller* pDomInstanceController) const
{
domNode* pDomNode = findJointNode(searchFrom, pDomInstanceController);
if (!pDomNode)
{
return NULL;
}
return getRootJoint(pDomNode);
}
void daeReader::processSkins()
{
if (_skinInstanceControllers.empty() || _skeletonMap.empty())
{
return;
}
typedef std::map<domNode* /*Skeleton root*/, domInstance_controllerList> SkelSkinMap;
SkelSkinMap skelSkinMap;
//group the skins according to which group of joints they're attached to.
for (size_t i = 0; i < _skinInstanceControllers.size(); ++i)
{
domInstance_controller* pDomInstanceController = _skinInstanceControllers[i];
const domInstance_controller::domSkeleton_Array& pDomSkeletons =
pDomInstanceController->getSkeleton_array();
if (pDomSkeletons.getCount() == 0)
{
domNode* skelNode = _skeletonMap.begin()->first;
if (skelNode = findSkeletonNode(skelNode, pDomInstanceController))
{
skelSkinMap[skelNode].push_back(pDomInstanceController);
}
}
else
{
if (daeElement* pDaeElement = pDomSkeletons.get(0)->getValue().getElement())
{
if (domNode* skelNode = findSkeletonNode(pDaeElement, pDomInstanceController))
{
skelSkinMap[skelNode].push_back(pDomInstanceController);
}
}
}
}
for (SkelSkinMap::iterator it = skelSkinMap.begin(); it != skelSkinMap.end(); ++it)
{
processSkeletonSkins(it->first, it->second);
}
}
void getJointsAndInverseObjectspaceBindMatrices(domInstance_controller* pDomInstanceController,
domNode* pDomSkeletonNode,
std::vector<std::pair<domNode*, osg::Matrix> >& jointsAndBindMatrices)
{
domController* pDomController = daeSafeCast< domController >(getElementFromURI(pDomInstanceController->getUrl()));
domSkin* pDomSkin = pDomController->getSkin();
domSkin::domJoints* pDomSkinJoints = pDomSkin->getJoints();
domInputLocal_Array domInputs = pDomSkinJoints->getInput_array();
if (domInputs.getCount() > 2)
{
osg::notify( osg::WARN ) << "Only a single pair of skin joints inputs is supported." << std::endl;
}
domSource* pDomJointsSource = NULL;
domSource* pDomInvBindMatricesSource = NULL;
for (size_t i=0; i < domInputs.getCount(); i++)
{
if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT))
{
pDomJointsSource = daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (!pDomJointsSource)
{
osg::notify( osg::WARN ) << "Could not find skin joints source '" << domInputs[i]->getSource().getURI() << "'" <<std::endl;
return;
}
}
else if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_INV_BIND_MATRIX))
{
pDomInvBindMatricesSource = daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (!pDomInvBindMatricesSource)
{
osg::notify( osg::WARN ) << "Could not find skin inverse bind matrices source '" << domInputs[i]->getSource().getURI() << "'" <<std::endl;
return;
}
}
}
domFloat_array* pDomFloatArray = pDomInvBindMatricesSource->getFloat_array();
domListOfFloats matrices = pDomFloatArray->getValue();
osg::Matrix parentInverseSkeletonBindMatrix;
if (domIDREF_array* pDomIDREFs = pDomJointsSource->getIDREF_array())
{
// IDREFS refer to an absolute joint and therefore do not allow a different skeleton
xsIDREFS* pIDREFS = &(pDomIDREFs->getValue());
for (size_t i=0; i < pIDREFS->getCount(); i++)
{
domNode* pDomNode = daeSafeCast< domNode >(getElementFromIDRef(pIDREFS->get(i)));
if (pDomNode)
{
jointsAndBindMatrices.push_back(std::pair<domNode*, osg::Matrix>(pDomNode, osg::Matrix()));
}
else
{
osg::notify( osg::WARN ) << "Failed to locate joint '" << pIDREFS->get(i).getID() << "'" << std::endl;
}
}
}
else if (domName_array* pDomNames = pDomJointsSource->getName_array())
{
// Using a list of names is the preferred way of referring to joints, because
// this refers to a joint relative to the given skeletons
domListOfNames* pNames = &(pDomNames->getValue());
for (size_t i=0; i < pNames->getCount(); i++)
{
daeSIDResolver resolver(pDomSkeletonNode, pNames->get(i));
domNode* pDomNode = daeSafeCast< domNode >(resolver.getElement());
if (pDomNode)
{
jointsAndBindMatrices.push_back(std::pair<domNode*, osg::Matrix>(pDomNode, osg::Matrix()));
}
else
{
osg::notify( osg::WARN ) << "Failed to locate joint '" << pNames->get(i) << "'" << std::endl;
}
}
}
else
{
osg::notify( osg::WARN ) << "No valid names or IDREFS array in <skin>" <<std::endl;
}
for (size_t i = 0; i < jointsAndBindMatrices.size(); ++i)
{
osg::Matrix invMat(
matrices.get(i*16 + 0), matrices.get(i*16 + 4), matrices.get(i*16 + 8), matrices.get(i*16 + 12),
matrices.get(i*16 + 1), matrices.get(i*16 + 5), matrices.get(i*16 + 9), matrices.get(i*16 + 13),
matrices.get(i*16 + 2), matrices.get(i*16 + 6), matrices.get(i*16 + 10), matrices.get(i*16 + 14),
matrices.get(i*16 + 3), matrices.get(i*16 + 7), matrices.get(i*16 + 11), matrices.get(i*16 + 15));
jointsAndBindMatrices[i].second = invMat;
}
}
void daeReader::processSkeletonSkins(domNode* skeletonRoot, const domInstance_controllerList& instanceControllers)
{
for (size_t i = 0; i < instanceControllers.size(); ++i)
{
domInstance_controller* instanceController = instanceControllers[i];
std::vector<std::pair<domNode*, osg::Matrix> > jointsAndInverseBindMatrices;
getJointsAndInverseObjectspaceBindMatrices(instanceController, skeletonRoot, jointsAndInverseBindMatrices);
for (size_t j = 0; j < jointsAndInverseBindMatrices.size(); ++j)
{
osgAnimation::Bone* pOsgBone = getOrCreateBone(jointsAndInverseBindMatrices[j].first);
pOsgBone->setInvBindMatrixInSkeletonSpace(jointsAndInverseBindMatrices[j].second);
}
}
osgAnimation::Skeleton* skeleton = getOrCreateSkeleton(skeletonRoot);
for (size_t i = 0; i < instanceControllers.size(); ++i)
{
domInstance_controller *pDomInstanceController = instanceControllers[i];
domController *pDomController = daeSafeCast< domController >(getElementFromURI(pDomInstanceController->getUrl()));
processSkin(pDomController->getSkin(), skeletonRoot, skeleton, pDomInstanceController->getBind_material());
}
}
osgAnimation::VertexInfluence& getVertexInfluence(
osgAnimation::VertexInfluenceMap& vim, const std::string& name)
{
osgAnimation::VertexInfluenceMap::iterator it = vim.lower_bound(name);
if (it == vim.end() || name != it->first)
{
it = vim.insert(it, osgAnimation::VertexInfluenceMap::value_type(
name, osgAnimation::VertexInfluence()));
it->second.setName(name);
}
return it->second;
}
// <skin source>
// 0..1 <bind_shape_matrix>
// 3..* <source>
// 1 <joints>
// 2..* <input semantic source>
// 0..* <extra>
// 1 <vertex_weights count>
// 2..* <input semantic source>
// 0..1 <vcount>
// 0..1 <v>
// 0.* <extra>
// 0..* <extra>
void daeReader::processSkin(domSkin* pDomSkin, domNode* skeletonRoot, osgAnimation::Skeleton* pOsgSkeleton, domBind_material* pDomBindMaterial)
{
daeElement* pDaeSkinSource = getElementFromURI( pDomSkin->getSource());
if (!pDaeSkinSource)
{
osg::notify( osg::WARN ) << "Failed to locate geometry " << pDomSkin->getSource().getURI() << std::endl;
return;
}
domGeometry* pDomGeometry = daeSafeCast< domGeometry >(pDaeSkinSource);
if (!pDomGeometry)
{
osg::notify( osg::WARN ) << "Skin source is of type " << pDaeSkinSource->getTypeName() << " which is not supported." << std::endl;
return;
}
// Base mesh
const osg::Geode* pOriginalGeode = NULL;
osg::Geode* pOsgGeode = getOrCreateGeometry(pDomGeometry, pDomBindMaterial, &pOriginalGeode);
if (!pOsgGeode)
return;
domMesh* pDomMesh = pDomGeometry->getMesh();
osg::Geode* pOsgRigGeode = new osg::Geode;
pOsgRigGeode->setDataVariance(osg::Object::DYNAMIC);
typedef std::map<const osg::Geometry*, osgAnimation::RigGeometry*> GeometryRigGeometryMap;
GeometryRigGeometryMap old2newGeometryMap;
for (unsigned i = 0; i < pOsgGeode->getNumDrawables(); ++i)
{
if (osg::Geometry* pOsgGeometry = dynamic_cast<osg::Geometry*>(pOsgGeode->getDrawable(i)))
{
const osg::Geometry* pOriginalGeometry = dynamic_cast<const osg::Geometry*>(pOriginalGeode->getDrawable(i));
osgAnimation::RigGeometry* pOsgRigGeometry = new osgAnimation::RigGeometry();
pOsgRigGeometry->setSourceGeometry(pOsgGeometry);
pOsgRigGeometry->copyFrom(*pOsgGeometry);
old2newGeometryMap.insert(GeometryRigGeometryMap::value_type(pOriginalGeometry, pOsgRigGeometry));
pOsgRigGeometry->setDataVariance(osg::Object::DYNAMIC);
pOsgRigGeometry->setUseDisplayList( false );
pOsgRigGeode->addDrawable(pOsgRigGeometry);
}
else
{
pOsgRigGeode->addDrawable(pOsgGeode->getDrawable(i));
}
}
pOsgSkeleton->addChild(pOsgRigGeode);
// <bind_shape_matrix>
if (domSkin::domBind_shape_matrix* pDomBindShapeMatrix = pDomSkin->getBind_shape_matrix())
{
domFloat4x4 matrix = pDomBindShapeMatrix->getValue();
osg::Matrix bindMatrix(
matrix.get(0), matrix.get(4), matrix.get(8), matrix.get(12),
matrix.get(1), matrix.get(5), matrix.get(9), matrix.get(13),
matrix.get(2), matrix.get(6), matrix.get(10), matrix.get(14),
matrix.get(3), matrix.get(7), matrix.get(11), matrix.get(15));
for (unsigned d = 0; d < pOsgRigGeode->getNumDrawables(); ++d)
{
osgAnimation::RigGeometry* pOsgRigGeometry = dynamic_cast<osgAnimation::RigGeometry*>(pOsgRigGeode->getDrawable(d));
if (!pOsgRigGeometry)
continue;
osg::Vec3Array& vertices = *static_cast<osg::Vec3Array*>(pOsgRigGeometry->getVertexArray());
for (size_t i = 0; i < vertices.size(); ++i)
{
vertices[i] = vertices[i] * bindMatrix;
}
if (osg::Vec3Array* normals = static_cast<osg::Vec3Array*>(pOsgRigGeometry->getNormalArray()))
{
for (size_t i = 0; i < normals->size(); ++i)
{
(*normals)[i] = osg::Matrix::transform3x3((*normals)[i], bindMatrix);
}
}
}
}
// 1 <vertex_weights count>
domSkin::domVertex_weights* pDomVertexWeights = pDomSkin->getVertex_weights();
domInputLocalOffset_Array domInputs = pDomVertexWeights->getInput_array();
if (domInputs.getCount() > 2)
{
osg::notify( osg::WARN ) << "Only a single pair of skin vertex weights inputs is supported." << std::endl;
}
domSource* pDomJointsSource = NULL;
domSource* pDomWeightsSource = NULL;
for (size_t i=0; i < 2; i++)
{
if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT))
{
pDomJointsSource = daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (!pDomJointsSource)
{
osg::notify( osg::WARN ) << "Could not find skin joints source '" << domInputs[i]->getSource().getURI() << "'" <<std::endl;
return;
}
}
else if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT))
{
pDomWeightsSource = daeSafeCast<domSource>(getElementFromURI(domInputs[i]->getSource()));
if (!pDomWeightsSource)
{
osg::notify( osg::WARN ) << "Could not find skin weights source '" << domInputs[i]->getSource().getURI() << "'" <<std::endl;
return;
}
}
}
domFloat_array* pDomFloatArray = pDomWeightsSource->getFloat_array();
domListOfFloats weights = pDomFloatArray->getValue();
domSkin::domVertex_weights::domVcount* pDomVcount = pDomVertexWeights->getVcount();
domListOfUInts influenceCounts = pDomVcount->getValue();
domSkin::domVertex_weights::domV* pDomV= pDomVertexWeights->getV();
domListOfInts jointWeightIndices = pDomV->getValue();
std::vector<std::string> jointNames;
if (domName_array* pDomNames = pDomJointsSource->getName_array())
{
domListOfNames* pNames = &(pDomNames->getValue());
jointNames.reserve(pNames->getCount());
for (size_t i = 0; i < pNames->getCount(); ++i)
{
const char* szName = pNames->get(i);
daeSIDResolver resolver(skeletonRoot, szName);
osgAnimation::Bone* pOsgBone = _jointMap[daeSafeCast<domNode>(resolver.getElement())].get();
if (pOsgBone)
{
jointNames.push_back(pOsgBone->getName());
}
else
{
jointNames.push_back(szName);
osg::notify(osg::WARN) << "Cannot find bone " << szName << std::endl;
}
}
}
else if (domIDREF_array* pDomIDREFs = pDomJointsSource->getIDREF_array())
{
xsIDREFS* pIDREFs = &(pDomIDREFs->getValue());
jointNames.reserve(pIDREFs->getCount());
for (size_t i = 0; i < pIDREFs->getCount(); ++i)
{
osgAnimation::Bone* pOsgBone = _jointMap[daeSafeCast<domNode>(pIDREFs->get(i).getElement())].get();
if (pOsgBone)
{
jointNames.push_back(pOsgBone->getName());
}
else
{
jointNames.push_back(pIDREFs->get(i).getID());
osg::notify(osg::WARN) << "Cannot find bone " << pIDREFs->get(i).getID() << std::endl;
}
}
}
else
{
osg::notify( osg::WARN ) << "No valid names or IDREFS array in <skin>" <<std::endl;
return;
}
for (size_t i = 0, vIndex = 0; i < influenceCounts.getCount(); i++)
{
OldToNewIndexMap::key_type indexID(pDomMesh, i);
const OldToNewIndexMap::const_iterator start = _oldToNewIndexMap.find(indexID);
const size_t nInfluences = influenceCounts[i];
if (start == _oldToNewIndexMap.end())
{
vIndex += nInfluences * 2;
//this vertex isn't used
continue;
}
const OldToNewIndexMap::const_iterator end = _oldToNewIndexMap.upper_bound(indexID);
for (size_t j = 0; j < nInfluences; ++j)
{
if (vIndex + 2 > jointWeightIndices.getCount())
{
osg::notify( osg::WARN ) << "vIndex is larger than number of v values" <<std::endl;
break;
}
size_t jointIndex = jointWeightIndices[vIndex++];
size_t weightIndex = jointWeightIndices[vIndex++];
if (jointIndex >= jointNames.size())
{
osg::notify( osg::WARN ) << "Joint index is larger the number of joints" <<std::endl;
break;
}
if (weightIndex >= weights.getCount())
{
osg::notify( osg::WARN ) << "Weight index is larger the number of weights" <<std::endl;
break;
}
float weight = weights[weightIndex];
if (weight > 0.0f)
{
const std::string& name = jointNames[jointIndex];
for (OldToNewIndexMap::const_iterator it = start; it != end; ++it)
{
osgAnimation::RigGeometry* pRigGeometry = old2newGeometryMap[it->second.first.get()];
osgAnimation::VertexInfluenceMap* vim = pRigGeometry->getInfluenceMap();
if (!vim)
{
pRigGeometry->setInfluenceMap(vim = new osgAnimation::VertexInfluenceMap);
}
getVertexInfluence(*vim, name).push_back(
osgAnimation::VertexIndexWeight(it->second.second, weight));
}
}
}
}
}