Skip to content

Commit

Permalink
Include a UsdSkelAnimMapper for blend shapes in UsdSkelSkinningQuery.
Browse files Browse the repository at this point in the history
This makes the data model more consistent between joint anim and blend shapes
Fixing bug in UsdSkelBakeSkinning with skinning transforms being computed
on prims that have no joint influences.
Adding UsdSkelAnimQuery::GetBlendShapeAttributes() method,
matching the interface of UsdSkelAnimQuery::GetJointTransformAttributes().
GetBlendShapeAttributes() is needed for motion sampling in Katana.
Adding UsdSkelBlendShapeQuery::GetBlendShapeIndex, for determining which blendshape
goes with each sub-shape.

(Internal change: 1973864)
  • Loading branch information
sgustafso authored and pixar-oss committed May 29, 2019
1 parent e66845f commit 39cb9b4
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 75 deletions.
11 changes: 11 additions & 0 deletions pxr/usd/lib/usdSkel/animQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@ UsdSkelAnimQuery::GetBlendShapeWeightTimeSamples(
}


bool
UsdSkelAnimQuery::GetBlendShapeWeightAttributes(
std::vector<UsdAttribute>* attrs) const
{
if(TF_VERIFY(IsValid(), "invalid anim query.")) {
return _impl->GetBlendShapeWeightAttributes(attrs);
}
return false;
}


bool
UsdSkelAnimQuery::GetBlendShapeWeightTimeSamplesInInterval(
const GfInterval& interval,
Expand Down
4 changes: 4 additions & 0 deletions pxr/usd/lib/usdSkel/animQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ class UsdSkelAnimQuery
bool GetBlendShapeWeightTimeSamplesInInterval(const GfInterval& interval,
std::vector<double>* times) const;

/// Get the attributes contributing to blendshape weight computations.
USDSKEL_API
bool GetBlendShapeWeightAttributes(std::vector<UsdAttribute>* attrs) const;

/// Return true if it possible, but not certain, that the blend shape
/// weights computed through this animation query change over time,
/// false otherwise.
Expand Down
11 changes: 11 additions & 0 deletions pxr/usd/lib/usdSkel/animQueryImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class UsdSkel_SkelAnimationQueryImpl : public UsdSkel_AnimQueryImpl
const GfInterval& interval,
std::vector<double>* times) const override;

bool GetBlendShapeWeightAttributes(
std::vector<UsdAttribute>* attrs) const override;

bool BlendShapeWeightsMightBeTimeVarying() const override;

private:
Expand Down Expand Up @@ -229,6 +232,14 @@ UsdSkel_SkelAnimationQueryImpl::GetBlendShapeWeightTimeSamples(
}


bool
UsdSkel_SkelAnimationQueryImpl::GetBlendShapeWeightAttributes(
std::vector<UsdAttribute>* attrs) const
{
attrs->push_back(_blendShapeWeights.GetAttribute());
return true;
}

bool
UsdSkel_SkelAnimationQueryImpl::BlendShapeWeightsMightBeTimeVarying() const
{
Expand Down
3 changes: 3 additions & 0 deletions pxr/usd/lib/usdSkel/animQueryImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class UsdSkel_AnimQueryImpl : public TfRefBase
GetBlendShapeWeightTimeSamples(const GfInterval& interval,
std::vector<double>* times) const = 0;

virtual bool
GetBlendShapeWeightAttributes(std::vector<UsdAttribute>* attrs) const = 0;

virtual bool
BlendShapeWeightsMightBeTimeVarying() const = 0;

Expand Down
11 changes: 11 additions & 0 deletions pxr/usd/lib/usdSkel/blendShapeQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ UsdSkelBlendShapeQuery::GetInbetween(size_t subShapeIndex) const
}


size_t
UsdSkelBlendShapeQuery::GetBlendShapeIndex(size_t subShapeIndex) const
{
if (subShapeIndex < _subShapes.size()) {
return _subShapes[subShapeIndex].GetBlendShapeIndex();
}
return 0;
}



std::vector<VtUIntArray>
UsdSkelBlendShapeQuery::ComputeBlendShapePointIndices() const
{
Expand Down
3 changes: 3 additions & 0 deletions pxr/usd/lib/usdSkel/blendShapeQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ class UsdSkelBlendShapeQuery
/// Returns the inbetween shape corresponding to sub-shape \p i, if any.
USDSKEL_API UsdSkelInbetweenShape GetInbetween(size_t subShapeIndex) const;

/// Returns the blend shape index corresponding to the \p i'th sub-shape.
USDSKEL_API size_t GetBlendShapeIndex(size_t subShapeIndex) const;

size_t GetNumBlendShapes() const { return _blendShapes.size(); }

size_t GetNumSubShapes() const { return _subShapes.size(); }
Expand Down
2 changes: 2 additions & 0 deletions pxr/usd/lib/usdSkel/cacheImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,13 @@ UsdSkel_CacheImpl::ReadScope::_FindOrCreateSkinningQuery(
const SkinningQueryKey& key)
{
UsdSkelSkeletonQuery skelQuery = FindOrCreateSkelQuery(key.skel);
const UsdSkelAnimQuery& animQuery = skelQuery.GetAnimQuery();

// TODO: Consider some form of deduplication.
return UsdSkelSkinningQuery(
skinnedPrim,
skelQuery ? skelQuery.GetJointOrder() : VtTokenArray(),
animQuery ? animQuery.GetBlendShapeOrder() : VtTokenArray(),
key.jointIndicesAttr, key.jointWeightsAttr,
key.geomBindTransformAttr, key.jointsAttr,
key.blendShapesAttr, key.blendShapeTargetsRel);
Expand Down
41 changes: 31 additions & 10 deletions pxr/usd/lib/usdSkel/skinningQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ UsdSkelSkinningQuery::UsdSkelSkinningQuery()
UsdSkelSkinningQuery::UsdSkelSkinningQuery(
const UsdPrim& prim,
const VtTokenArray& skelJointOrder,
const VtTokenArray& animBlendShapeOrder,
const UsdAttribute& jointIndices,
const UsdAttribute& jointWeights,
const UsdAttribute& geomBindTransform,
Expand All @@ -72,11 +73,16 @@ UsdSkelSkinningQuery::UsdSkelSkinningQuery(
_blendShapes(blendShapes),
_blendShapeTargets(blendShapeTargets)
{
VtTokenArray jointOrder;
if (joints && joints.Get(&jointOrder)) {
_jointOrder = jointOrder;
_mapper =
std::make_shared<UsdSkelAnimMapper>(skelJointOrder, jointOrder);
VtTokenArray order;
if (joints && joints.Get(&order)) {
_jointOrder = order;
_jointMapper =
std::make_shared<UsdSkelAnimMapper>(skelJointOrder, order);
}
if (blendShapes && blendShapes.Get(&order)) {
_blendShapeOrder = order;
_blendShapeMapper =
std::make_shared<UsdSkelAnimMapper>(animBlendShapeOrder, order);
}

_InitializeJointInfluenceBindings(jointIndices, jointWeights);
Expand Down Expand Up @@ -146,7 +152,7 @@ UsdSkelSkinningQuery::_InitializeBlendShapeBindings(
const UsdAttribute& blendShapes,
const UsdRelationship& blendShapeTargets)
{
if (blendShapes && blendShapeTargets) {
if (blendShapes && blendShapeTargets && _blendShapeMapper) {
_flags |= UsdSkel_HasBlendShapes;
}
}
Expand Down Expand Up @@ -188,6 +194,21 @@ UsdSkelSkinningQuery::GetJointOrder(VtTokenArray* jointOrder) const
}


bool
UsdSkelSkinningQuery::GetBlendShapeOrder(VtTokenArray* blendShapeOrder) const
{
if (blendShapeOrder) {
if (_blendShapeOrder) {
*blendShapeOrder = *_blendShapeOrder;
return true;
}
} else {
TF_CODING_ERROR("'blendShapeOrder' pointer is null.");
}
return false;
}


bool
UsdSkelSkinningQuery::GetTimeSamples(std::vector<double>* times) const
{
Expand Down Expand Up @@ -324,8 +345,8 @@ UsdSkelSkinningQuery::ComputeSkinnedPoints(const VtArray<Matrix4>& xforms,
// a mapper that should be used to reorder transforms
// (skel order -> binding order)
VtArray<Matrix4> orderedXforms(xforms);
if (_mapper) {
if (!_mapper->RemapTransforms(xforms, &orderedXforms)) {
if (_jointMapper) {
if (!_jointMapper->RemapTransforms(xforms, &orderedXforms)) {
return false;
}
}
Expand Down Expand Up @@ -376,8 +397,8 @@ UsdSkelSkinningQuery::ComputeSkinnedTransform(const VtArray<Matrix4>& xforms,
// a mapper that should be used to reorder transforms
// (skel order -> binding order)
VtArray<Matrix4> orderedXforms(xforms);
if (_mapper) {
if (!_mapper->Remap(xforms, &orderedXforms)) {
if (_jointMapper) {
if (!_jointMapper->Remap(xforms, &orderedXforms)) {
return false;
}
}
Expand Down
37 changes: 31 additions & 6 deletions pxr/usd/lib/usdSkel/skinningQuery.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class UsdSkelSkinningQuery
USDSKEL_API
UsdSkelSkinningQuery(const UsdPrim& prim,
const VtTokenArray& skelJointOrder,
const VtTokenArray& blendShapeOrder,
const UsdAttribute& jointIndices,
const UsdAttribute& jointWeights,
const UsdAttribute& geomBindTransform,
Expand Down Expand Up @@ -119,16 +120,38 @@ class UsdSkelSkinningQuery
return _blendShapeTargets;
}

/// Return the mapper for this target, if any.
/// This corresponds to the mapping of the joint order from
/// the ordering on the skeleton to the order of a custom
/// \em skel:joints relationships, set inside the hierarchy.
const UsdSkelAnimMapperRefPtr& GetMapper() const { return _mapper; }
/// Return a mapper for remapping from the joint order of the skeleton
/// to the local joint order of this prim, if any. Returns a null
/// pointer if the prim has no custom joint orer.
/// The mapper maps data from the order given by the \em joints order
/// on the Skeleton to the order given by the \em skel:joints property,
/// as optionally set through the UsdSkelBindingAPI.
const UsdSkelAnimMapperRefPtr& GetJointMapper() const {
return _jointMapper;
}

/// \deprecated Use GetJointMapper.
const UsdSkelAnimMapperRefPtr& GetMapper() const { return _jointMapper; }


/// Return the mapper for remapping blend shapes from the order of the
/// bound SkelAnimation to the local blend shape order of this prim.
/// Returns a null reference if the underlying prim has no blend shapes.
/// The mapper maps data from the order given by the \em blendShapes order
/// on the SkelAnimation to the order given by the \em skel:blendShapes
/// property, as set through the UsdSkelBindingAPI.
const UsdSkelAnimMapperRefPtr& GetBlendShapeMapper() const {
return _blendShapeMapper;
}

/// Get the custom joint order for this skinning site, if any.
USDSKEL_API
bool GetJointOrder(VtTokenArray* jointOrder) const;

/// Get the blend shapes for this skinning site, if any.
USDSKEL_API
bool GetBlendShapeOrder(VtTokenArray* blendShapes) const;

/// Populate \p times with the union of time samples for all properties
/// that affect skinning, independent of joint transforms and any
/// other prim-specific properties (such as points).
Expand Down Expand Up @@ -233,8 +256,10 @@ class UsdSkelSkinningQuery
UsdAttribute _geomBindTransformAttr;
UsdAttribute _blendShapes;
UsdRelationship _blendShapeTargets;
UsdSkelAnimMapperRefPtr _mapper;
UsdSkelAnimMapperRefPtr _jointMapper;
UsdSkelAnimMapperRefPtr _blendShapeMapper;
boost::optional<VtTokenArray> _jointOrder;
boost::optional<VtTokenArray> _blendShapeOrder;
};

PXR_NAMESPACE_CLOSE_SCOPE
Expand Down
8 changes: 4 additions & 4 deletions pxr/usd/lib/usdSkel/testenv/testUsdSkelCache.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,26 +220,26 @@ def _GetSkinningQuery(path):
query = _GetSkinningQuery(rootPath+"/Model1/NonRigidScope1/A")
assert query
assert query.IsRigidlyDeformed()
assert query.GetMapper()
assert query.GetJointMapper()
self.assertEquals(query.GetJointOrder(),
Vt.TokenArray(["A", "B", "C"]))

query = _GetSkinningQuery(rootPath+"/Model1/NonRigidScope1/B")
assert query
assert not query.IsRigidlyDeformed()
assert not query.GetMapper()
assert not query.GetJointMapper()

query = _GetSkinningQuery(rootPath+"/Model1/NonRigidScope2/A")
assert query
assert query.IsRigidlyDeformed()
assert query.GetMapper()
assert query.GetJointMapper()
self.assertEquals(query.GetJointOrder(),
Vt.TokenArray(["A", "B", "C"]))

query = _GetSkinningQuery(rootPath+"/Model1/NonRigidScope2/B")
assert query
assert query.IsRigidlyDeformed()
assert not query.GetMapper()
assert not query.GetJointMapper()

# TODO: When adding support for rigid deformation of intermediate
# xformables, Model1/RigidScopeParent/RigidScope should be treated
Expand Down
54 changes: 18 additions & 36 deletions pxr/usd/lib/usdSkel/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1956,25 +1956,6 @@ _BakeSkinnedPoints(const UsdPrim& prim,
const std::vector<VtVec3fArray> subShapePointOffsets =
blendShapeQuery.ComputeSubShapePointOffsets();

// Compute mapper for remapping blend shape weights.
UsdSkelAnimMapper blendShapeMapper;
bool haveBlendShapes = false;

if (skinningQuery.HasBlendShapes()) {

// We have bindings for blend shapes, but these only mean
// something if we have an animation source to provide weight values.
if (const auto& animQuery = skelQuery.GetAnimQuery()) {
VtTokenArray blendShapeOrder;
if (skinningQuery.GetBlendShapesAttr().Get(&blendShapeOrder)) {
blendShapeMapper =
UsdSkelAnimMapper(animQuery.GetBlendShapeOrder(),
blendShapeOrder);
haveBlendShapes = true;
}
}
}

for (size_t i = 0; i < times.size(); ++i) {
const VtValue& points = pointsValues[i];
if (!points.IsHolding<VtVec3fArray>()) {
Expand All @@ -1989,25 +1970,10 @@ _BakeSkinnedPoints(const UsdPrim& prim,
"(sample %zu of %zu)\n",
TfStringify(time).c_str(), i, times.size());

// XXX: More complete and sophisticated skinning code would compute
// skinning transforms and blend shape weights once for all prims
// deformed by a single skeleton, instead of recomputing them for each
// individual prim skinned.
// However, since this method is intended only for testing, simplicity
// and correctness are greater priorities than performance.

VtMatrix4dArray xforms;
if (!skelQuery.ComputeSkinningTransforms(&xforms, time)) {
TF_DEBUG(USDSKEL_BAKESKINNING).Msg(
"[UsdSkelBakeSkinning] Failed computing "
"skinning transforms\n");
return false;
}

VtVec3fArray skinnedPoints(points.UncheckedGet<VtVec3fArray>());

// Apply blend shapes before skinning.
if (haveBlendShapes) {
if (skinningQuery.HasBlendShapes()) {

TF_DEBUG(USDSKEL_BAKESKINNING).Msg(
"[UsdSkelBakeSkinning] Applying blend shapes\n");
Expand All @@ -2024,7 +1990,8 @@ _BakeSkinnedPoints(const UsdPrim& prim,
// Remap the weights from the order on the animation source
// to the order of the shapes bound to this skinnable prim.
VtFloatArray weightsForPrim;
if (!blendShapeMapper.Remap(weights, &weightsForPrim)) {
if (!skinningQuery.GetBlendShapeMapper()->Remap(
weights, &weightsForPrim)) {
return false;
}

Expand All @@ -2049,6 +2016,21 @@ _BakeSkinnedPoints(const UsdPrim& prim,
TF_DEBUG(USDSKEL_BAKESKINNING).Msg(
"[UsdSkelBakeSkinning] Applying linear blend skinning\n");

// XXX: More complete and sophisticated skinning code would compute
// skinning transforms and blend shape weights once for all prims
// deformed by a single skeleton, instead of recomputing them for each
// individual prim skinned.
// However, since this method is intended only for testing, simplicity
// and correctness are greater priorities than performance.

VtMatrix4dArray xforms;
if (!skelQuery.ComputeSkinningTransforms(&xforms, time)) {
TF_DEBUG(USDSKEL_BAKESKINNING).Msg(
"[UsdSkelBakeSkinning] Failed computing "
"skinning transforms\n");
return false;
}

if (!skinningQuery.ComputeSkinnedPoints(
xforms, &skinnedPoints, time)) {
TF_DEBUG(USDSKEL_BAKESKINNING).Msg(
Expand Down
1 change: 1 addition & 0 deletions pxr/usd/lib/usdSkel/wrapBlendShapeQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ void wrapUsdSkelBlendShapeQuery()

.def("GetBlendShape", &This::GetBlendShape)
.def("GetInbetween", &This::GetInbetween)
.def("GetBlendShapeIndex", &This::GetBlendShapeIndex)

.def("GetNumBlendShapes", &This::GetNumBlendShapes)
.def("GetNumSubShapes", &This::GetNumSubShapes)
Expand Down
7 changes: 7 additions & 0 deletions pxr/usd/lib/usdSkel/wrapSkinningQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,16 @@ void wrapUsdSkelSkinningQuery()
.def("GetJointWeightsPrimvar", &This::GetJointWeightsPrimvar,
return_value_policy<return_by_value>())

// deprecated
.def("GetMapper", &This::GetMapper,
return_value_policy<return_by_value>())

.def("GetJointMapper", &This::GetJointMapper,
return_value_policy<return_by_value>())

.def("GetBlendShapeMapper", &This::GetBlendShapeMapper,
return_value_policy<return_by_value>())

.def("GetJointOrder", &_GetJointOrder)

.def("GetTimeSamples", &_GetTimeSamples)
Expand Down
Loading

0 comments on commit 39cb9b4

Please sign in to comment.