Adjusting the Extraction

You can adjust or override the extraction to prevent locking of the elbows/knees. You can refer to the constraintextraction sample at http://area.autodesk.com/downloads/plugins/sample_plug_ins_for_the_flexible_mocap_workflow.

The sample shows how to read the original extraction and decide whether to override the extraction.

void ORConstraintExtraction::SetupAllAnimationNodes()
{
  if(Character.GetSrcCount() > 0)
  {
    FBCharacter* lCharacter = (FBCharacter*)Character.GetSrc(0);
    FBCharacterMarkerSet* lMarkerSet = lCharacter ? lCharacter->GetCharacterMarkerSet():NULL;

    if(lMarkerSet)
    {
      for(int i = 0; i < mNodes.GetCount(); i++)
      {
        mNodes[i]->Setup(lCharacter, this, i); 
      }
    }
  }
}

void ORConstraintExtraction::Node::Setup(FBCharacter* pCharacter, FBConstraint* pConstraint, int pIndex)
{
  FBModel* lStartGoalModel = pCharacter->GetGoalModel(mStartNode);
  FBModel* lEndGoalModel = pCharacter->GetGoalModel(mEndNode);

  if(lStartGoalModel && lEndGoalModel)
  {
    FBTVector lT_A,lT_B,lV;

    // Get the characterization stance pose to calculate the maximum length.
    pCharacter->GetTOffset(mStartNode, &lT_A);
    pCharacter->GetTOffset(mEndNode, &lT_B);

    FBSub(lV, lT_B, lT_A);
    // Calculate the maximum allowed length per body part.
    mLength = FBLength(lV);

    mReadT = lEndGoalModel->Translation.GetAnimationNode();
    // Create the input connector to end the effector translation (global data)
    mWriteT = pConstraint->AnimationNodeInCreate  (pIndex+1, lEndGoalModel, ANIMATIONNODE_TYPE_TRANSLATION);
  }
}

void ORConstraintExtraction::Node::Solve(FBCharacter* pCharacter, FBConstraint* pConstraint, FBEvaluateInfo* pEvaluateInfo)
{
  if(mReadT == NULL || mWriteT == NULL) return;

  FBModel* lStart = pCharacter->GetGoalModel(mStartNode);
  FBModel* lEnd = pCharacter->GetGoalModel(mEndNode);

  if(lStart && lEnd)
  {
    FBTVector lT_A,lT_B,lV;

    lStart->GetVector(*(FBVector3d*)(lT_A.mValue), kModelTranslation, true, pEvaluateInfo);
    // Read from the connector directly, knowing that data over there is always 
    // written globally by the Character Marker Set solver.
    mReadT->ReadData(lT_B.mValue, pEvaluateInfo);

    FBSub(lV, lT_B, lT_A);
    double lCurrentLength = FBLength(lV);

    // Very simple test that can be expanded to more sophisticated logic.
    if(lCurrentLength > mLength)
    {
      FBMult(lV,lV,mLength/lCurrentLength);

      FBAdd(lT_B, lT_A, lV);

      // Write the clamp global translation.
      mWriteT->WriteData(lT_B.mValue, pEvaluateInfo);
    }
  }
}

bool ORConstraintExtraction::AnimationNodeNotify(FBAnimationNode* pConnector,
     FBEvaluateInfo* pEvaluateInfo,FBConstraintInfo* pConstraintInfo)
{
  // If any character is connected (Just in case, as it is used blindly afterwards)
  if(Character.GetSrcCount() > 0)
  {
    FBCharacter* lCharacter = (FBCharacter*)Character.GetSrc(0);

    // Solve all the constraints.
    for(int i = 0; i < mNodes.GetCount(); i++)
    {
      mNodes[i]->Solve(lCharacter, this, pEvaluateInfo); 
    }
  }
  // Disable all the animation nodes that you did not write to.
  // This optimization stops the evaluation engine
  // from calling AnimationNodeNotify again in the same evaluation ID
  AnimationNodesOutDisableIfNotWritten(pEvaluateInfo);

  return true;
}