#include "SOP_IKSample.h"
#include <GEO/GEO_Point.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include <KIN/KIN_Bone.h>
#include <UT/UT_DSOVersion.h>
#include <UT/UT_Quaternion.h>
#include <UT/UT_Interrupt.h>
#include <UT/UT_Matrix4.h>
#include <UT/UT_RefArray.h>
#include <UT/UT_WorkBuffer.h>
#include <UT/UT_Vector3.h>
#include <stdlib.h>
using namespace HDK_Sample;
void
newSopOperator(OP_OperatorTable *table)
{
OP_Operator *op = new OP_Operator(
"hdk_iksample",
"IKSample",
SOP_IKSample::myConstructor,
SOP_IKSample::myTemplateList,
1,
1
);
table->addOperator(op);
}
OP_Node *
SOP_IKSample::myConstructor(OP_Network *net, const char *name, OP_Operator *op)
{
return new SOP_IKSample(net, name, op);
}
static PRM_Name names[] =
{
PRM_Name("goal", "Goal Position"),
PRM_Name("twist", "Twist"),
PRM_Name("dampen", "Dampening"),
PRM_Name("straighten", "Straighten Solution"),
PRM_Name("threshold", "Threshold"),
};
static PRM_Default sopThresholdDefault(1e-03f);
PRM_Template SOP_IKSample::myTemplateList[] =
{
PRM_Template(PRM_FLT_J, 3, &names[0]),
PRM_Template(PRM_ANGLE_J, 1, &names[1], 0, 0, &PRMangleRange),
PRM_Template(PRM_FLT_J, 1, &names[2]),
PRM_Template(PRM_TOGGLE_J, 1, &names[3]),
PRM_Template(PRM_FLT_J, 1, &names[4], &sopThresholdDefault),
PRM_Template()
};
SOP_IKSample::SOP_IKSample(OP_Network *net, const char *name, OP_Operator *op)
: SOP_Node(net, name, op)
{
}
SOP_IKSample::~SOP_IKSample()
{
}
bool
SOP_IKSample::evaluateSolverParms(OP_Context &context, KIN_InverseParm &parms)
{
fpreal t = context.getTime();
UT_Vector3R pos;
GOAL(t, pos);
parms.myEndAffectorPos = pos;
UT_Vector3 origin = gdp->points()(0)->getPos();
parms.myEndAffectorPos -= origin;
parms.myTwist = TWIST(t);
parms.myDampen = DAMPEN(t);
parms.myResistStraight = STRAIGHTEN(t);
parms.myTrackingThresholdFactor = THRESHOLD(t);
parms.myTwistAffectorFlag = false;
return (error() < UT_ERROR_ABORT);
}
bool
SOP_IKSample::setupRestChain()
{
int num_points = gdp->points().entries();
int num_bones = num_points - 1;
UT_Vector3R prev_pos(0, 0, 0);
UT_Vector3R prev_dir(0, 0, -1);
if (num_bones < 1)
{
UT_WorkBuffer str;
str.sprintf("%d", 2 - num_points);
addError(SOP_NEED_MORE_POINTS, str.buffer());
return false;
}
myRestChain.setNbones(num_bones);
prev_pos = gdp->points()[0]->getPos();
for (int i = 0; i < num_bones; i++)
{
UT_Vector3R pos = gdp->points()(i+1)->getPos();
UT_Vector3R dir = pos - prev_pos;
fpreal length = dir.length();
fpreal damp = 0;
UT_Matrix4 pre_xform(1);
void *data = NULL;
UT_Vector3R rot;
UT_Quaternion quat;
dir.normalize();
quat.updateFromVectors(prev_dir, dir);
rot = quat.computeRotations(KIN_Chain::getXformOrder());
rot.radToDeg();
myRestChain.updateBone(i, length, rot.data(), damp, pre_xform, data);
prev_pos = pos;
prev_dir = dir;
}
return true;
}
OP_ERROR
SOP_IKSample::cookMySop(OP_Context &context)
{
GB_AttributeRef orient_attrib;
if (lockInputs(context) >= UT_ERROR_ABORT)
return error();
int input_changed;
duplicateChangedSource(0, context, &input_changed);
if (input_changed)
{
if (!setupRestChain())
{
unlockInputs();
return error();
}
float def[4] = { 0, 0, 0, 1 };
orient_attrib = gdp->addPointAttrib("orient", 4 * sizeof(float),
GB_ATTRIB_FLOAT, &def[0]);
GB_AttributeRef pscale_attrib;
pscale_attrib = gdp->addPointAttrib("pscale", 1 * sizeof(float),
GB_ATTRIB_FLOAT, &def[3]);
GEO_Point *ppt;
int i = 0;
FOR_ALL_GPOINTS(gdp, ppt)
{
float length = 0;
if (i < myRestChain.getNbones())
length = myRestChain.getBone(i)->getLength();
ppt->setValue<float>(pscale_attrib, length);
i++;
}
}
const char * solver_name = "inverse";
KIN_InverseParm parms;
if (!evaluateSolverParms(context, parms))
{
unlockInputs();
return error();
}
KIN_Chain solution;
if (!myRestChain.solve(solver_name, &parms, solution))
{
addError(SOP_MESSAGE, "Failed to solve.");
unlockInputs();
return error();
}
if (!orient_attrib.isValid())
{
orient_attrib = gdp->findPointAttrib("orient", 4 * sizeof(float),
GB_ATTRIB_FLOAT);
if (!orient_attrib.isValid())
{
addError(SOP_MESSAGE, "Failed to create orient attribute.");
unlockInputs();
return error();
}
}
UT_Matrix4R xform(1);
int num_bones = gdp->points().entries() - 1;
fpreal prev_length = 0;
UT_Vector3 pos;
pos = gdp->points()(0)->getPos();
xform.setTranslates(pos);
for (int i = 0; i < num_bones; i++)
{
const KIN_Bone *bone = solution.getBone(i);
GEO_Point *ppt = gdp->points()(i);
xform.leftMult(bone->getExtraXform());
xform.pretranslate(0, 0, -1 * prev_length);
xform.getTranslates(pos);
ppt->setPos(pos);
UT_Vector3R rot;
bone->getRotates(rot.data());
rot.degToRad();
xform.prerotate(rot.x(), rot.y(), rot.z(), KIN_Chain::getXformOrder());
UT_Matrix3R rot_xform(xform);
UT_Quaternion q;
q.updateFromRotationMatrix(rot_xform);
ppt->setValue<UT_Quaternion>(orient_attrib, q);
prev_length = bone->getLength();
}
xform.pretranslate(0, 0, -1 * prev_length);
xform.getTranslates(pos);
gdp->points()(num_bones)->setPos(pos);
unlockInputs();
return error();
}
const char *
SOP_IKSample::inputLabel(unsigned ) const
{
return "Points for IK";
}