HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_IKSample.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  *
27  * The IKSample SOP
28  *
29  * Demonstrates example use of the Inverse Kinematics (IK) Solver found in the
30  * KIN library.
31  *
32  */
33 
34 #include "SOP_IKSample.h"
35 #include <KIN/KIN_Bone.h>
36 #include <OP/OP_AutoLockInputs.h>
37 #include <OP/OP_Operator.h>
38 #include <OP/OP_OperatorTable.h>
39 #include <PRM/PRM_Include.h>
40 #include <UT/UT_Array.h>
41 #include <UT/UT_DSOVersion.h>
42 #include <UT/UT_Interrupt.h>
43 #include <UT/UT_Matrix4.h>
44 #include <UT/UT_Quaternion.h>
45 #include <UT/UT_Vector3.h>
46 #include <UT/UT_WorkBuffer.h>
47 #include <stdlib.h>
48 
49 using namespace HDK_Sample;
50 
51 // Provide entry point for installing this SOP.
52 void
54 {
55  OP_Operator *op = new OP_Operator(
56  "hdk_iksample",
57  "IKSample",
60  1, // min inputs
61  1 // max inputs
62  );
63  table->addOperator(op);
64 }
65 
66 OP_Node *
68 {
69  return new SOP_IKSample(net, name, op);
70 }
71 
72 // SOP Parameters.
73 static PRM_Name names[] =
74 {
75  PRM_Name("goal", "Goal Position"),
76  PRM_Name("twist", "Twist"),
77  PRM_Name("dampen", "Dampening"),
78  PRM_Name("straighten", "Straighten Solution"),
79  PRM_Name("threshold", "Threshold"),
80 };
81 static PRM_Default sopThresholdDefault(1e-03f);
82 
84 {
85  PRM_Template(PRM_FLT_J, 3, &names[0]),
86  PRM_Template(PRM_ANGLE_J, 1, &names[1], 0, 0, &PRMangleRange),
87  PRM_Template(PRM_FLT_J, 1, &names[2]),
88  PRM_Template(PRM_TOGGLE_J, 1, &names[3]),
89  PRM_Template(PRM_FLT_J, 1, &names[4], &sopThresholdDefault),
90  PRM_Template() // sentinel
91 };
92 
93 // Constructor
95  : SOP_Node(net, name, op)
96 {
97  // This indicates that this SOP manually manages its data IDs,
98  // so that Houdini can identify what attributes may have changed,
99  // e.g. to reduce work for the viewport, or other SOPs that
100  // check whether data IDs have changed.
101  // By default, (i.e. if this line weren't here), all data IDs
102  // would be bumped after the SOP cook, to indicate that
103  // everything might have changed.
104  // If some data IDs don't get bumped properly, the viewport
105  // may not update, or SOPs that check data IDs
106  // may not cook correctly, so be *very* careful!
108 }
109 
110 // Destructor
112 {
113 }
114 
115 // Evaluate parameters for the solver.
116 bool
117 SOP_IKSample::evaluateSolverParms(OP_Context &context, KIN_InverseParm &parms)
118 {
119  fpreal t = context.getTime();
120 
121  // the goal position is relative to chain root
122  GOAL(t, parms.myEndAffectorPos);
123  UT_Vector3 origin = gdp->getPos3(gdp->pointOffset(0));
124  parms.myEndAffectorPos -= UT_Vector3R{ origin };
125 
126  parms.myTwist = TWIST(t);
127  parms.myDampen = DAMPEN(t);
128  parms.myResistStraight = STRAIGHTEN(t);
129  parms.myTrackingThresholdFactor = THRESHOLD(t);
130  parms.myTwistAffectorFlag = false;
131  // if myTwistAffectorFlag is true, then also set the twist affector pos
132  // parms.myInverseParms.myTwistAffectorPos = ...
133 
134  // Return true only if we didn't encounter any errors during parameter
135  // evaluation.
136  return (error() < UT_ERROR_ABORT);
137 }
138 
139 // Setup myRestChain. The rest chain is used by the IK solver to determine
140 // the initial chain that is iteratively solved towards the goal position.
141 bool
142 SOP_IKSample::setupRestChain()
143 {
144  GA_Size num_points = gdp->getNumPoints();
145  GA_Size num_bones = num_points - 1;
146 
147  if (num_bones < 1)
148  {
149  UT_WorkBuffer str;
150  str.sprintf("%d", 2 - (int)num_points);
152  return false;
153  }
154 
155  myRestChain.setNbones(num_bones);
156  UT_Vector3R prev_pos = gdp->getPos3(gdp->pointOffset(0));
157  UT_Vector3R prev_dir(0, 0, -1);
158  for (GA_Size i = 0; i < num_bones; i++)
159  {
161  UT_Vector3R dir = pos - prev_pos;
162  fpreal length = dir.length();
163  fpreal damp = 0; // only used by "constraint" solver
164  UT_Matrix4R pre_xform(1); // assume identity
165  void *data = NULL; // not used
167  UT_Quaternion quat;
168 
169  // Since we're dealing with only point positions, calculate a natural
170  // twist rotation using quaternions from the previous bone.
171  dir.normalize();
172  quat.updateFromVectors(prev_dir, dir);
174  rot.radToDeg();
175 
176  // Update the bone in the chain.
177  myRestChain.updateBone(i, length, rot.data(), damp, pre_xform, data);
178 
179  // If we're dealing with the "constraint" solver, then we need to
180  // do more setup here.
181  //myRestChain.setConstraint(i, ...);
182 
183  // Update position and direction for next iteration.
184  prev_pos = pos;
185  prev_dir = dir;
186  }
187 
188  return true;
189 }
190 
191 // Compute the output geometry for the SOP.
192 OP_ERROR
194 {
195  // We must lock our inputs before we try to access their geometry.
196  // OP_AutoLockInputs will automatically unlock our inputs when we return.
197  // NOTE: Don't call unlockInputs yourself when using this!
198  OP_AutoLockInputs inputs(this);
199  if (inputs.lock(context) >= UT_ERROR_ABORT)
200  return error();
201 
202  // Setup the rest chain if needed.
203  int input_changed;
204  duplicateChangedSource(/*input*/0, context, &input_changed);
205 
206  GA_RWHandleQ orient_attrib;
207  if (input_changed)
208  {
209  if (!setupRestChain())
210  return error();
211 
212  // Create the "orient" attribute for storing our solved rotations.
214  fpreal64(0), fpreal64(0), fpreal64(0), fpreal64(1) );
215  orient_attrib = GA_RWHandleQ(gdp->addFloatTuple(GA_ATTRIB_POINT,"orient", 4, def));
216 
217  // Compute the "pscale" attribute using the bone lengths for
218  // completeness. This allows us to easily use the BoneLink SOP along
219  // with the Copy SOP.
220  GA_RWHandleF pscale_attrib(gdp->addFloatTuple(GA_ATTRIB_POINT,"pscale", 1,
221  GA_Defaults(1.0)));
222  exint i = 0;
223  GA_Offset ptoff;
224  GA_FOR_ALL_PTOFF(gdp, ptoff)
225  {
226  float length = 0;
227  if (i < myRestChain.getNbones())
228  length = myRestChain.getBone(i).getLength();
229  pscale_attrib.set(ptoff, length);
230  i++;
231  }
232 
233  // NOTE: Even though we used addFloatTuple, there could have been
234  // a pscale attribute already, which could have been reused,
235  // so we need to bump its data ID to indicate that we've
236  // modified it.
237  pscale_attrib.bumpDataId();
238  }
239 
240  // Evaluate parameters for solver.
241  const char *solver_name = "inverse";
242  KIN_InverseParm parms;
243  if (!evaluateSolverParms(context, parms))
244  return error();
245 
246  // Perform solve.
247  KIN_Chain solution;
248  if (!myRestChain.solve(solver_name, &parms, solution))
249  {
250  addError(SOP_MESSAGE, "Failed to solve.");
251  return error();
252  }
253 
254  // We store the solved orientations into the "orient" attribute that is
255  // understood by the Copy SOP. We also compute "pscale" as well for
256  // completeness. This allows us to easily use the BoneLink SOP along with
257  // the Copy SOP.
258  if (!orient_attrib.isValid())
259  {
260  orient_attrib = gdp->findFloatTuple(GA_ATTRIB_POINT, "orient", 4);
261  if (!orient_attrib.isValid())
262  {
263  addError(SOP_MESSAGE, "Failed to create orient attribute.");
264  return error();
265  }
266  }
267 
268  // Nothing to do if 1 or fewer points, and the code below
269  // may crash for 0 points.
270  if (gdp->getNumPoints() <= 1)
271  {
272  return error();
273  }
274 
275  // Output geometry.
276  UT_Matrix4R xform(1); // identity, this is the world transform
277 
279  xform.setTranslates(pos); // set chain origin
280 
281  GA_Size num_bones = gdp->getNumPoints() - 1;
282  fpreal prev_length = 0;
283  for (GA_Index i = 0; i < num_bones; i++)
284  {
285  auto &&bone = solution.getBone(i);
286 
287  // Since we never actually set any pre-transforms, this leftMult()
288  // ends up doing nothing.
289  xform.leftMult(UT_R_FROM_F(bone.getExtraXform()));
290 
291  // Take the bone length into account for the point position.
292  xform.pretranslate(0, 0, -1 * prev_length);
293 
294  xform.getTranslates(pos);
295  GA_Offset ptoff = gdp->pointOffset(i);
296  gdp->setPos3(ptoff, pos);
297 
298  // Update our world transform with the bone rotations. Note that
299  // bone->getRotates() returns them in degrees.
301  bone.getRotates(rot.data());
302  rot.degToRad();
303  xform.prerotate(rot.x(), rot.y(), rot.z(), KIN_Chain::getXformOrder());
304 
305  // Stash the world transform's rotation into our orient attribute.
306  UT_Matrix3R rot_xform(xform);
308  q.updateFromRotationMatrix(rot_xform);
309  orient_attrib.set(ptoff, q);
310 
311  prev_length = bone.getLength();
312  }
313 
314  // set chain end position
315  xform.pretranslate(0, 0, -1 * prev_length);
316  xform.getTranslates(pos);
317  GA_Offset ptoff = gdp->pointOffset(num_bones);
318  gdp->setPos3(ptoff, pos);
319 
320  // We've modified orient and P, so we need to bump their data IDs.
321  orient_attrib.bumpDataId();
322  gdp->getP()->bumpDataId();
323 
324  return error();
325 }
326 
327 // Provide input labels.
328 const char *
329 SOP_IKSample::inputLabel(unsigned /*input_index*/) const
330 {
331  return "Points for IK";
332 }
333 
OP_ERROR cookMySop(OP_Context &context) override
Method to cook geometry for the SOP.
Definition: SOP_IKSample.C:193
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
PRM_API PRM_Range PRMangleRange
virtual OP_ERROR error()
Class which stores the default values for a GA_Attribute.
Definition: GA_Defaults.h:35
OP_ERROR lock(OP_Context &context)
Locks all inputs.
GLboolean * data
Definition: glcorearb.h:131
GA_Attribute * addFloatTuple(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size, const GA_Defaults &defaults=GA_Defaults(0.0), const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, GA_Storage storage=GA_STORE_REAL32, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
fpreal getTime() const
Definition: OP_Context.h:62
void radToDeg()
conversion between degrees and radians
GA_API const UT_StringHolder rot
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector3.h:667
int64 exint
Definition: SYS_Types.h:125
GA_Attribute * getP()
Convenience method to access the P attribute.
Definition: GA_Detail.h:164
UT_Vector3T< T > computeRotations(const UT_XformOrder &) const
SYS_FORCE_INLINE const char * buffer() const
UT_ErrorSeverity
Definition: UT_Error.h:25
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
constexpr SYS_FORCE_INLINE T length() const noexcept
Definition: UT_Vector3.h:361
const KIN_Bone & getBone(int index) const
Definition: KIN_Chain.h:113
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
SYS_FORCE_INLINE UT_Vector3 getPos3(GA_Offset ptoff) const
The ptoff passed is the point offset.
Definition: GA_Detail.h:185
GLdouble GLdouble GLdouble q
Definition: glad.h:2445
fpreal getLength() const
Definition: KIN_Bone.h:70
void addError(int code, const char *msg=0)
Definition: SOP_Node.h:1158
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:235
constexpr SYS_FORCE_INLINE const T * data() const noexcept
Definition: UT_Vector3.h:292
void degToRad()
conversion between degrees and radians
int solve(const UT_StringHolder &type, const void *parms, KIN_Chain &solution)
double fpreal64
Definition: SYS_Types.h:201
GA_Size GA_Offset
Definition: GA_Types.h:641
GLfloat f
Definition: glcorearb.h:1926
fpreal myTwist
Definition: KIN_Chain.h:68
OP_ERROR duplicateChangedSource(unsigned idx, OP_Context &ctx, int *changed=0, bool force=false)
Only duplicates the source if the source has changed since the last call to this method.
fpreal myDampen
Definition: KIN_Chain.h:69
PRM_API const PRM_Type PRM_ANGLE_J
PRM_API const PRM_Type PRM_TOGGLE_J
int getNbones() const
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
void updateFromVectors(const UT_Vector3T< T > &v1, const UT_Vector3T< T > &v2)
GLuint const GLchar * name
Definition: glcorearb.h:786
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
Definition: SOP_IKSample.C:67
#define GA_FOR_ALL_PTOFF(gdp, ptoff)
Definition: GA_GBMacros.h:88
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:635
void bumpDataId() const
Definition: GA_Handle.h:341
PRM_API const PRM_Type PRM_FLT_J
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:36
GLdouble t
Definition: glad.h:2397
void setTranslates(const UT_Vector3T< S > &translates)
Definition: UT_Matrix4.h:1442
void prerotate(UT_Vector3T< S > &axis, T theta, int norm=1)
SOP_IKSample(OP_Network *net, const char *name, OP_Operator *op)
Definition: SOP_IKSample.C:94
int sprintf(const char *fmt,...) SYS_PRINTF_CHECK_ATTRIBUTE(2
GU_Detail * gdp
Definition: SOP_Node.h:1622
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:187
const GA_Attribute * findFloatTuple(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringRef &name, int min_size=1, int max_size=-1) const
const char * inputLabel(unsigned input_index) const override
Method to provide input labels.
Definition: SOP_IKSample.C:329
SYS_FORCE_INLINE void setPos3(GA_Offset ptoff, const UT_Vector3 &pos)
Set P from a UT_Vector3.
Definition: GA_Detail.h:237
fpreal myTrackingThresholdFactor
Definition: KIN_Chain.h:89
SYS_FORCE_INLINE void set(GA_Offset off, const T &val) const
Definition: GA_Handle.h:354
static PRM_Template myTemplateList[]
Definition: SOP_IKSample.h:56
fpreal64 fpreal
Definition: SYS_Types.h:277
void setNbones(int bones)
static const UT_XformOrder & getXformOrder()
Definition: KIN_Chain.h:102
int myTwistAffectorFlag
Definition: KIN_Chain.h:67
void leftMult(const UT_Matrix4T< T > &m)
Definition: UT_Matrix4.h:1593
SYS_FORCE_INLINE UT_StorageMathFloat_t< T > normalize() noexcept
Definition: UT_Vector3.h:376
GA_RWHandleT< UT_QuaternionF > GA_RWHandleQ
Definition: GA_Handle.h:1376
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector3.h:665
int myResistStraight
Definition: KIN_Chain.h:90
void updateBone(int index, fpreal length, fpreal *rot, fpreal damp, const UT_Matrix4R &xform, const void *data)
SYS_FORCE_INLINE GA_Offset pointOffset(GA_Index index) const
Given a point's index (in append order), return its data offset.
Definition: GA_Detail.h:345
UT_Vector3R myEndAffectorPos
Definition: KIN_Chain.h:65
void pretranslate(T dx, T dy, T dz=0)
Definition: UT_Matrix4.h:792
Definition: format.h:895
void getTranslates(UT_Vector3T< S > &translates) const
Definition: UT_Matrix4.h:1432
void newSopOperator(OP_OperatorTable *table)
Definition: SOP_IKSample.C:53
void updateFromRotationMatrix(const UT_Matrix3 &)
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:334
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector3.h:663