HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CHOP_Spring.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 
28 #include <UT/UT_DSOVersion.h>
29 #include <OP/OP_OperatorTable.h>
30 
31 #include <CH/CH_LocalVariable.h>
32 #include <PRM/PRM_Include.h>
33 #include <CHOP/PRM_ChopShared.h>
34 
35 #include <UT/UT_Interrupt.h>
36 #include <UT/UT_IStream.h>
37 #include <UT/UT_OStream.h>
38 
39 #include "CHOP_Spring.h"
40 
41 using namespace HDK_Sample;
42 
43 CHOP_SWITCHER(7, "Spring");
44 
45 static PRM_Name names[] = {
46  PRM_Name("springk", "Spring Constant"),
47  PRM_Name("mass", "Mass"),
48  PRM_Name("dampingk", "Damping Constant"),
49  PRM_Name("method", "Input Effect"),
50  PRM_Name("condfromchan", "Initial Conditions From Channel"),
51  PRM_Name("initpos", "Initial Position"),
52  PRM_Name("initspeed", "Initial Speed"),
53  PRM_Name(0),
54 };
55 
56 static PRM_Name springMethodItems[] = {
57  PRM_Name("disp", "Position"),
58  PRM_Name("force", "Force"),
59  PRM_Name(0),
60 };
61 
62 static PRM_ChoiceList springMethodMenu(PRM_CHOICELIST_SINGLE,
63  springMethodItems);
64 
65 static PRM_Range springConstantRange(PRM_RANGE_RESTRICTED, 0.0,
66  PRM_RANGE_UI, 1000.0);
67 
68 static PRM_Range massRange(PRM_RANGE_UI, 0.1,
69  PRM_RANGE_UI, 10.0);
70 
71 static PRM_Range dampingConstantRange(PRM_RANGE_RESTRICTED, 0.0,
72  PRM_RANGE_UI, 10.0);
73 
74 static PRM_Range initDispRange(PRM_RANGE_UI, -10.0,
75  PRM_RANGE_UI, 10.0);
76 
77 static PRM_Range initVelRange(PRM_RANGE_UI, -100.0,
78  PRM_RANGE_UI, 100.0);
79 
80 static PRM_Default springConstantDefault(100.0);
81 static PRM_Default massDefault(1.0);
82 static PRM_Default dampingConstantDefault(1.0);
83 static PRM_Default initDispDefault(0.0);
84 static PRM_Default initVelDefault(0.0);
85 
88 {
90 
91  // First Page
92  PRM_Template(PRM_FLT, 1, &names[0], &springConstantDefault, 0,
93  &springConstantRange),
94  PRM_Template(PRM_FLT, 1, &names[1], &massDefault, 0,
95  &massRange),
96  PRM_Template(PRM_FLT, 1, &names[2], &dampingConstantDefault, 0,
97  &dampingConstantRange),
98  PRM_Template(PRM_ORD, 1, &names[3], PRMzeroDefaults,
99  &springMethodMenu),
100  PRM_Template(PRM_TOGGLE, 1, &names[4], PRMoneDefaults),
101  PRM_Template(PRM_FLT, 1, &names[5], &initDispDefault, 0,
102  &initDispRange),
103  PRM_Template(PRM_FLT, 1, &names[6], &initVelDefault, 0,
104  &initVelRange),
105  PRM_Template(),
106 };
107 
110 
111 bool
113 {
114  bool changes = CHOP_Node::updateParmsFlags();
115  bool grab = GRAB_INITIAL();
116 
117  changes |= enableParm("initpos", !grab);
118  changes |= enableParm("initspeed", !grab);
119 
120  return changes;
121 }
122 
123 
124 // 2 local variables, 'C' (the currently cooking track), 'NC' total # tracks.
125 enum
126 {
127  VAR_C = 200,
128  VAR_NC = 201
129 };
132  { "C", VAR_C, 0 },
133  { "NC", VAR_NC, 0 },
134  { 0, 0, 0 }
135 };
138 
139 bool
141 {
142  switch(index)
143  {
144  case VAR_C:
145  // C marks the parameter as channel dependent - it must be re-eval'd
146  // for each track processed.
147  myChannelDependent=1;
148  val = (fpreal)my_C;
149  return true;
150 
151  case VAR_NC:
152  val = (fpreal)my_NC;
153  return true;
154 
155  }
156 
157  return CHOP_Node::evalVariableValue(val, index, thread);
158 }
159 
160 //----------------------------------------------------------------------------
161 
162 OP_Node *
164  const char *name,
165  OP_Operator *op)
166 {
167  return new CHOP_Spring(net, name, op);
168 }
169 
170 
172  const char *name,
173  OP_Operator *op)
174  : CHOP_Realtime(net, name, op)
175 {
176  myParmBase = getParmList()->getParmIndex( names[0].getToken() );
177  mySteady = 0;
178 }
179 
181 {
182 }
183 
184 // Regular cook method
185 OP_ERROR
187 {
188  const CL_Clip *clip = 0;
189  const CL_Track *track = 0;
190  CL_Track *new_track = 0;
191  int force_method;
192  int i, j,length, num_tracks, animated_parms;
193  fpreal spring_constant;
194  fpreal d1,d2,f,inc,d;
195  fpreal mass;
196  fpreal damping_constant;
197  fpreal initial_displacement;
198  fpreal initial_velocity;
199  fpreal acc, vel;
200  UT_Interrupt *boss;
201  int stop;
202  int count = 0xFFFF;
203  bool grab_init = GRAB_INITIAL();
204 
205  // Copy the structure of the input, but not the data itself.
206  clip = copyInput(context, 0, 0, 1);
207  if (!clip)
208  return error();
209 
210  force_method = METHOD();
211 
212  // Initialize local variables
213  my_NC = clip->getNumTracks();
214  my_C= 0;
215 
216  // evaluate all parameters and check if any are animated per channel with C
217  myChannelDependent=0;
218  spring_constant = SPRING_CONSTANT(context.getTime());
219  mass = MASS(context.getTime());
220  damping_constant = DAMPING_CONSTANT(context.getTime());
221  animated_parms = myChannelDependent;
222 
223  inc = 1.0 / myClip->getSampleRate();
224 
225  // If 'grab initial' is true, we determine the initial values from the
226  // input channel data instead of using the parameter settings (using
227  // the position and slope of the track at the first sample).
228  if(!grab_init)
229  {
230  initial_displacement = INITIAL_DISPLACEMENT(context.getTime());
231  initial_velocity = INITIAL_VELOCITY(context.getTime());
232  }
233 
234  // An evaluation error occurred; exit.
235  if (error() >= UT_ERROR_ABORT)
236  return error();
237 
238  // Mass must be > 0.
239  if (mass < 0.001)
240  {
241  mass = 0.001;
242  SET_MASS(context.getTime(), mass);
243  }
244 
245  // Begin the interruptable operation
246  boss = UTgetInterrupt();
247  stop = 0;
248  if(boss->opStart("Calculating Spring"))
249  {
250  if (clip)
251  {
252  num_tracks = clip->getNumTracks();
253  length = clip->getTrackLength();
254 
255  // Loop over all the tracks
256  for (i=0; i<num_tracks; i++)
257  {
258  // update the local variable 'C' with the track number
259  my_C = i;
260 
261  track = clip->getTrack(i);
262  new_track = myClip->getTrack(i);
263 
264  // If the track is not scoped, copy it and continue to the next
265  if (!isScoped(track->getName()))
266  {
267  *new_track = *track;
268  continue;
269  }
270 
271  if(grab_init || animated_parms)
272  {
273  // re-evaluate parameters if one of them was determined to
274  // depend on the local var 'C' (track number)
275  if(animated_parms)
276  {
277  spring_constant = SPRING_CONSTANT(context.getTime());
278  mass = MASS(context.getTime());
279  if (mass < 0.001)
280  mass = 0.001;
281  damping_constant = DAMPING_CONSTANT(context.getTime());
282  }
283 
284  // If determining the position and speed from the track,
285  // evaluate the first 2 samples and difference them.
286  if(grab_init)
287  {
288  initial_displacement = clip->evaluateSingle(track,0);
289  initial_velocity=(clip->evaluateSingle(track,1) -
290  initial_displacement);
291  }
292  }
293 
294  // Run the spring algorithm on the track's data.
295  d1 = initial_displacement;
296  d2 = d1 - initial_velocity * inc;
297 
298  for(j=0; j<length; j++)
299  {
300  // Periodically check for interrupts.
301  if(count--==0 && boss->opInterrupt())
302  {
303  stop = 1;
304  break;
305  }
306 
307  // run the spring equation
308  f = track->getData()[j];
309  if(!force_method)
310  f *= spring_constant;
311 
312  vel = (d1-d2) / inc;
313 
314  acc = (f - vel*damping_constant - d1*spring_constant)/mass;
315  vel += acc * inc;
316  d = d1 + vel * inc;
317 
318  new_track->getData()[j] = d;
319 
320  // update the previous displacements
321  d2 = d1;
322  d1 = d;
323  }
324 
325  if(stop || boss->opInterrupt())
326  {
327  stop = 1;
328  break;
329  }
330  }
331  }
332  }
333  // opEnd must always be called, even if opStart() returned 0.
334  boss->opEnd();
335 
336  return error();
337 }
338 
339 // -------------------------------------------------------------------------
340 // Time Slice cooking
341 
342 // ---------------------------------------------------------------------------
343 // Realtime data block for stashing intermediate values between realtime cooks
344 // Only used in Time Slice mode.
345 
346 namespace HDK_Sample {
348 {
349 public:
350  ut_SpringData(const char *name,fpreal d,fpreal v);
351  ~ut_SpringData() override {}
352 
355 
356  bool loadStates(UT_IStream &is, int version) override;
357  bool saveStates(UT_OStream &os) override;
358 };
359 }
360 
362  : ut_RealtimeData(name),
363  myDn1(d1),
364  myDn2(d2)
365 {
366 }
367 
368 bool
370 {
371  if (!ut_RealtimeData::loadStates(is, version))
372  return false;
373 
374  if (!is.read<fpreal64>(&myDn1))
375  return false;
376  if (!is.read<fpreal64>(&myDn2))
377  return false;
378  return true;
379 }
380 
381 bool
383 {
385 
386  os.write<fpreal64>(&myDn1);
387  os.write<fpreal64>(&myDn2);
388  return true;
389 }
390 
391 int
393 {
394  // 'Steady' indicates that the CHOP has reached a steady state and can
395  // stop cooking every frame. An input must change in order to begin
396  // springing again.
397  return mySteady;
398 }
399 
400 OP_ERROR
402 {
403  const CL_Clip *clip = inputClip(0,context);
404  const CL_Track *track = 0;
405  CL_Track *new_track = 0;
406  int force_method;
407  int i, j;
408  fpreal spring_constant;
409  fpreal mass;
410  fpreal d1,d2,f,t,inc,d, acc,vel,oldp;
411  fpreal damping_constant;
412  ut_SpringData *block;
413  fpreal delta;
414  int animated_parms;
415 
416  force_method = METHOD();
417 
418  my_NC = clip->getNumTracks();
419  my_C= 0;
420 
421  // Again, evaluate the parameters and see if they depend on C
422  myChannelDependent=0;
423  spring_constant = SPRING_CONSTANT(context.getTime());
424  mass = MASS(context.getTime());
425  damping_constant = DAMPING_CONSTANT(context.getTime());
426  animated_parms = myChannelDependent;
427  inc = 1.0 / myClip->getSampleRate();
428 
429  if (mass < 0.001)
430  mass = 0.001;
431 
432  mySteady = 1;
433 
434  for(i=0; i<myClip->getNumTracks(); i++)
435  {
436  my_C = i;
437 
438  track = clip->getTrack(i);
439  new_track = myClip->getTrack(i);
440 
441  // If this track isn't scoped, just copy the data.
442  if(!isScoped(new_track->getName()))
443  {
444  clip->evaluateTime(track,
445  myClip->getTime(start+myClip->getStart()),
446  myClip->getTime(end+myClip->getStart()),
447  new_track->getData(), myClip->getTrackLength());
448  continue;
449  }
450 
451  // Re-evaluate the spring parameters if one of them depends on C
452  if(animated_parms)
453  {
454  spring_constant = SPRING_CONSTANT(context.getTime());
455  mass = MASS(context.getTime());
456  if (mass < 0.001)
457  mass = 0.001;
458  damping_constant = DAMPING_CONSTANT(context.getTime());
459  }
460 
461  // Create or grab the realtime data block associated with this track.
462  // This will keep our results from the previous cook, in this case,
463  // the previous 2 displacements.
464  block = (ut_SpringData *) getDataBlock(i);
465 
466  d1 = block->myDn1;
467  d2 = block->myDn2;
468 
469  // Loop over each sample in the track.
470  for(j=0; j<myClip->getTrackLength(); j++)
471  {
472  t = myClip->getTime(myClip->getStart() + j);
473  oldp = f = clip->evaluateSingleTime(track, t);
474 
475  // Run the spring equation
476  if(!force_method)
477  f *= spring_constant;
478  else
479  oldp /=spring_constant;
480 
481  vel = (d1-d2) / inc;
482 
483  acc = (f - vel*damping_constant - d1*spring_constant)/mass;
484  vel += acc * inc;
485  d = d1 + vel * inc;
486 
487  delta = SYSabs(oldp - d);
488  if (delta > 0.001)
489  mySteady = 0;
490 
491  new_track->getData()[j] = d;
492 
493  d2 = d1;
494  d1 = d;
495  }
496 
497  // update the displacements in the realtime data block for the next cook
498  // to use.
499  block->myDn1 = d1;
500  block->myDn2 = d2;
501  }
502 
503  return error();
504 }
505 
508  const CL_Track *track,
509  fpreal t)
510 {
511  fpreal d, d1, v, rate;
512 
513  // This creates a new realtime data block to stash intermediate values into.
514  // In this case, the previous two displacements.
515  if(GRAB_INITIAL() && track)
516  {
517  const CL_Clip *clip = track ? track->getClip() : 0;
518 
519  d = clip->evaluateSingle(track,clip->getIndex(t));
520  v = clip->evaluateSingle(track,clip->getIndex(t)+1) - d;
521  }
522  else
523  {
524  d = INITIAL_DISPLACEMENT(t);
525  v = INITIAL_VELOCITY(t);
526  }
527 
528  // The n-1 displacement is extrapolated from the slope at n and the
529  // displacement (position) at n.
530  rate = myClip->getSampleRate();
531  if(rate != 0.0)
532  d1 = d - v/rate;
533  else
534  d1 = d;
535 
536  return new ut_SpringData(name, d,d1);
537 }
538 
539 
540 // install the chop.
542 {
543  table->addOperator(new OP_Operator("hdk_spring", // node name
544  "HDK Spring", // pretty name
547  1, // min inputs
548  1, // max inputs
550 }
CL_Clip * myClip
Definition: CHOP_Node.h:588
const UT_StringHolder & getName() const
Definition: CL_Track.h:98
fpreal getStart() const
Definition: CL_Clip.h:76
static OP_VariablePair myVariablePair
Definition: CHOP_Spring.h:56
virtual OP_ERROR error()
PRM_API const PRM_Type PRM_FLT
static CH_LocalVariable myVariableList[]
Definition: CHOP_Spring.h:58
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
Definition: CHOP_Spring.C:163
ut_SpringData(const char *name, fpreal d, fpreal v)
Definition: CHOP_Spring.C:361
CHOP_Spring(OP_Network *net, const char *name, OP_Operator *op)
Definition: CHOP_Spring.C:171
virtual bool saveStates(UT_OStream &os)
UT_OStream & write(const char_type *str, int64 count)
Definition: UT_OStream.h:348
OP_ERROR cookMyChop(OP_Context &context) override
Definition: CHOP_Spring.C:186
const GLdouble * v
Definition: glcorearb.h:837
fpreal getTime() const
Definition: OP_Context.h:62
static OP_TemplatePair myTemplatePair
Definition: CHOP_Spring.h:55
static OP_TemplatePair myTemplatePair
Definition: CHOP_Node.h:162
GLuint start
Definition: glcorearb.h:475
CL_Track * getTrack(int index)
Definition: CL_Clip.h:176
PRM_API const PRM_Type PRM_ORD
fpreal getSampleRate() const
Definition: CL_Clip.h:90
#define SYSabs(a)
Definition: SYS_Math.h:1572
UT_ErrorSeverity
Definition: UT_Error.h:25
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
static OP_VariablePair myVariablePair
Definition: CHOP_Node.h:163
bool updateParmsFlags() override
Definition: CHOP_Spring.C:112
bool updateParmsFlags() override
short myParmBase
Definition: CHOP_Node.h:585
bool saveStates(UT_OStream &os) override
Definition: CHOP_Spring.C:382
void evaluateTime(const CL_Track *track, fpreal start_time, fpreal stop_time, fpreal *data, int size) const
Definition: CL_Clip.h:217
double fpreal64
Definition: SYS_Types.h:201
int opInterrupt(int percent=-1)
const CL_Clip * inputClip(int idx, OP_Context &context)
GLfloat f
Definition: glcorearb.h:1926
bool enableParm(int pi, int state, int v=-1)
exint read(bool *array, exint sz=1)
Definition: UT_IStream.h:276
GLuint GLuint end
Definition: glcorearb.h:475
static PRM_Template myTemplateList[]
Definition: CHOP_Spring.h:57
fpreal evaluateSingle(const CL_Track *track, fpreal index) const
Definition: CL_Clip.h:196
GLuint const GLchar * name
Definition: glcorearb.h:786
const CL_Clip * copyInput(OP_Context &context, int idx, int data, int slerps)
ut_RealtimeData * newRealtimeDataBlock(const char *name, const CL_Track *track, fpreal t) override
Definition: CHOP_Spring.C:507
SYS_FORCE_INLINE UT_StringHolder getToken(Add enum_value)
Definition: SOP_Add.proto.h:35
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
PRM_API const PRM_Type PRM_SWITCHER
GLdouble t
Definition: glad.h:2397
CHOP_SWITCHER(7,"Spring")
GT_API const UT_StringHolder version
CL_Clip * getClip()
Definition: CL_Track.h:142
GLint j
Definition: glad.h:2733
GA_API const UT_StringHolder mass
PRM_API PRM_Default PRMoneDefaults[]
PRM_API PRM_Name PRMswitcherName
**Note that the tasks the is the thread number *for the or if it s being executed by a non pool thread(this *can happen in cases where the whole pool is occupied and the calling *thread contributes to running the work load).**Thread pool.Have fun
ut_RealtimeData * getDataBlock(int n)
bool loadStates(UT_IStream &is, int version) override
Definition: CHOP_Spring.C:369
fpreal64 fpreal
Definition: SYS_Types.h:277
UT_API UT_Interrupt * UTgetInterrupt()
Obtain global UT_Interrupt singleton.
GLuint index
Definition: glcorearb.h:786
void opEnd(int opid=-1)
fpreal evaluateSingleTime(const CL_Track *track, fpreal t) const
Definition: CL_Clip.h:199
fpreal * getData()
Definition: CL_Track.h:89
GLuint GLfloat * val
Definition: glcorearb.h:1608
PRM_API const PRM_Type PRM_TOGGLE
int getTrackLength() const
Definition: CL_Clip.h:82
fpreal getIndex(fpreal time) const
Definition: CL_Clip.h:267
virtual bool evalVariableValue(UT_String &val, int index, int thread)
int isScoped(const UT_StringRef &name)
int opStart(const char *opname=0, int npoll=0, int immediate_dialog=0, int *opid=0)
void newChopOperator(OP_OperatorTable *table)
Definition: CHOP_Spring.C:541
virtual bool loadStates(UT_IStream &is, int version)
OP_ERROR cookMySlice(OP_Context &context, int start, int end) override
Definition: CHOP_Spring.C:401
bool evalVariableValue(fpreal &val, int i, int thread) override
Definition: CHOP_Spring.C:140
int getParmIndex(const PRM_Parm *parm) const
int getNumTracks() const
Definition: CL_Clip.h:173
PRM_API PRM_Default PRMzeroDefaults[]
SYS_FORCE_INLINE PRM_ParmList * getParmList()
Definition: PRM_ParmOwner.h:71
int isSteady() const override
Definition: CHOP_Spring.C:392
IMATH_INTERNAL_NAMESPACE_HEADER_ENTER IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T clip(const T &p, const Box< T > &box) IMATH_NOEXCEPT
Definition: ImathBoxAlgo.h:29
fpreal getTime(fpreal index) const
Definition: CL_Clip.h:270
GLint GLsizei count
Definition: glcorearb.h:405