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