Files
OpenSceneGraph/src/osgPlugins/dae/daeRSkinning.cpp

572 lines
21 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_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_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 = findSkeletonNode(_skeletonMap.begin()->first, pDomInstanceController);
if (skelNode)
{
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_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_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_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_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_WARN << "Failed to locate joint '" << pNames->get(i) << "'" << std::endl;
}
}
}
else
{
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_WARN << "Failed to locate geometry " << pDomSkin->getSource().getURI() << std::endl;
return;
}
domGeometry* pDomGeometry = daeSafeCast< domGeometry >(pDaeSkinSource);
if (!pDomGeometry)
{
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::Array * vert = pOsgRigGeometry->getVertexArray();
osg::Vec3Array * vertf = NULL;
osg::Vec3dArray* vertd = NULL;
if (vert->getType() == osg::Array::Vec3ArrayType)
{
vertf = static_cast<osg::Vec3Array*>(vert);
for (size_t i = 0; i < vertf->size(); ++i)
{
(*vertf)[i] = (*vertf)[i] * bindMatrix;
}
}
else if (vert->getType() == osg::Array::Vec3dArrayType)
{
vertd = static_cast<osg::Vec3dArray*>(vert);
for (size_t i = 0; i < vertd->size(); ++i)
{
(*vertd)[i] = (*vertd)[i] * bindMatrix;
}
}
else
{
OSG_NOTIFY(osg::WARN) << "Vertices vector type isn't supported." << std::endl;
continue;
}
osg::Array * norm = pOsgRigGeometry->getNormalArray();
if (norm)
{
osg::Vec3Array * normf = NULL;
osg::Vec3dArray* normd = NULL;
if (norm->getType() == osg::Array::Vec3ArrayType)
{
normf = static_cast<osg::Vec3Array*>(norm);
for (size_t i = 0; i < normf->size(); ++i)
{
(*normf)[i] = osg::Matrix::transform3x3((*normf)[i], bindMatrix);
}
}
else if (norm->getType() == osg::Array::Vec3dArrayType)
{
normd = static_cast<osg::Vec3dArray*>(norm);
for (size_t i = 0; i < normd->size(); ++i)
{
(*normd)[i] = osg::Matrix::transform3x3((*normd)[i], bindMatrix);
}
}
else
{
OSG_NOTIFY(osg::WARN) << "Normals vector type isn't supported." << std::endl;
//continue;
}
}
}
}
// 1 <vertex_weights count>
domSkin::domVertex_weights* pDomVertexWeights = pDomSkin->getVertex_weights();
domInputLocalOffset_Array domInputs = pDomVertexWeights->getInput_array();
if (domInputs.getCount() > 2)
{
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_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_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_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_WARN << "Cannot find bone " << pIDREFs->get(i).getID() << std::endl;
}
}
}
else
{
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_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_WARN << "Joint index is larger the number of joints" <<std::endl;
break;
}
if (weightIndex >= weights.getCount())
{
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));
}
}
}
}
}