HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CHOP_Blend.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  * Blends inputs 2,3,4... using the weights in input 1.
27  */
28 
29 #include <SYS/SYS_Math.h>
30 #include <UT/UT_DSOVersion.h>
31 #include <OP/OP_OperatorTable.h>
32 
33 #include <CH/CH_LocalVariable.h>
34 #include <PRM/PRM_Include.h>
35 
36 #include <UT/UT_Interrupt.h>
37 
38 #include "CHOP_Blend.h"
39 
40 using namespace HDK_Sample;
41 
42 CHOP_SWITCHER(2,"Blend");
43 
44 static PRM_Name methodItems[] = {
45  PRM_Name("prop", "Proportional"),
46  PRM_Name("diff", "Difference"),
47  PRM_Name(0),
48 };
49 
50 static PRM_ChoiceList methodMenu(PRM_CHOICELIST_SINGLE,
51  methodItems);
52 
53 static PRM_Name names[] = {
54  PRM_Name("method", "Method"),
55  PRM_Name("firstweight", "Omit First Weight Channel"),
56 };
57 
60 {
62  PRM_Template(PRM_ORD, 1, &names[0], PRMoneDefaults,&methodMenu),
63  PRM_Template(PRM_TOGGLE, 1, &names[1], PRMzeroDefaults),
64 
65  PRM_Template(),
66 };
69 
70 bool
72 {
73  bool changes = CHOP_Node::updateParmsFlags();
74 
75  // can only omit the first weight when differencing.
76  changes |= enableParm("firstweight", GETDIFFERENCE());
77 
78  return changes;
79 }
80 
81 
82 
85  { 0, 0, 0 }
86 };
89 
90 
91 OP_Node *
93  const char *name,
94  OP_Operator *op)
95 {
96  return new CHOP_Blend(net, name, op);
97 }
98 
99 //----------------------------------------------------------------------------
100 
101 
103  const char *name,
104  OP_Operator *op)
105  : CHOP_Node(net, name, op)
106 {
107  myParmBase = getParmList()->getParmIndex( names[0].getToken() );
108 }
109 
111 {
112 }
113 
114 const CL_Clip *
115 CHOP_Blend::getCacheInputClip(int j)
116 {
117  // Convenience method to return the clip cached by the method
118  // CHOP_Blend::findInputClips();
119  return (j>=0 && j<myInputClip.entries()) ?
120  myInputClip(j) : 0;
121 }
122 
123 OP_ERROR
125 {
126  const CL_Clip *blendclip;
127  const CL_Clip *clip;
128  const CL_Track *track, *blend;
129  int num_motion_tracks;
130  int num_clips;
131  fpreal weight, *data, *total;
132  int i,j,k,samples;
133  fpreal time;
134  fpreal val;
135  int difference;
136  int fweight;
137  UT_Interrupt *boss = UTgetInterrupt();
138  short int percent = -1;
139  int stopped = 0;
140  fpreal adjust[2] = {1.0, -1.0};
141  const fpreal *src, *w;
142 
143  difference = GETDIFFERENCE();
144  if(difference)
145  fweight = FIRST_WEIGHT();
146  else
147  fweight = 0;
148 
149  // This copies the attributes of the clip without copying the tracks
150  // (length, start, sample rate, rotation order).
151  blendclip = copyInputAttributes(context);
152  if(!blendclip)
153  return error();
154 
155  // Determine which clips are being blended together (the first is a control
156  // clip containing weights)
157  num_clips = findInputClips(context, blendclip);
158 
159  // Determine the tracks that are being blended together.
160  num_motion_tracks = findFirstAvailableTracks(context);
161 
162  samples = myClip->getTrackLength();
163 
164  // Create a working array for blending containing the sum of all the blend
165  // weights for a given sample.
166  myTotalArray.setCapacity(samples);
167  myTotalArray.entries(samples);
168  total = myTotalArray.array();
169 
170  if(boss->opStart("Blending Channels"))
171  {
172  for(i=0; i<num_motion_tracks; i++)
173  {
174  // layer the motion tracks, by looping through the clips for
175  // each track
176 
177  // Zero out the track's data and grab a pointer to it for processing
178  myClip->getTrack(i)->constant(0);
179  data = myClip->getTrack(i)->getData();
180 
181  // Start off with a constant track.
182  myTotalArray.constant(difference ? 1.0 : 0.0);
183 
184  // When differencing, subtract the contributions of the weights from
185  // the initial weight of '1' for the first blend input.
186  // Otherwise, add all weights together.
187  for(j=difference?1:0; j<num_clips; j++)
188  {
189  clip = getCacheInputClip(j+1);
190  if(!clip)
191  continue;
192 
193  // Grab the control track for the blend track we're
194  // processing. It contains an array of weights.
195  if(difference && fweight)
196  blend = blendclip->getTrack(j-1);
197  else
198  blend = blendclip->getTrack(j);
199  track = clip->getTrack(i);
200 
201  if(!track || !blend)
202  continue;
203 
204  // go through all samples and blend into the result.
205 
206  w = blend->getData();
207 
208  // Optimization for input clips that share the same frame range
209  // as our output clip -- access the data directly.
210  if (myClip->isSameRange(*clip))
211  {
212  src = track->getData();
213  for(k=0; k<samples; k++)
214  {
215  if (w[k])
216  {
217  // Add the weighted sample into the output data.
218  data[k] += w[k]*src[k];
219 
220  // add or subtract the weight from the total.
221  // If differencing is used, adjust[] = -1, else 1.
222  total[k] += w[k]*adjust[difference];
223  }
224  }
225  }
226  else
227  {
228  // Slightly slower; evaluate at each sample, but otherwise
229  // the same as the first case.
230  for(k=0; k<samples; k++)
231  {
232  time = myClip->getTime(k + myClip->getStart());
233  if (w[k])
234  {
235  data[k] += w[k] *
236  clip->evaluateSingleTime(track, time);
237  total[k] += w[k]*adjust[difference];
238  }
239  }
240  }
241 
242  // Check for user interrupts.
243  if(boss->opInterrupt())
244  {
245  stopped = 1;
246  break;
247  }
248  }
249 
250  // Now normalize the results to 1.
251  if(!stopped)
252  {
253  if(!difference)
254  {
255  // when not difference, just divide the samples by the
256  // total weight value for each sample.
257  j = myAvailableTracks(i);
258  if ( j!=-1 && getCacheInputClip(j))
259  {
260  track = getCacheInputClip(j)->getTrack(i);
261  blend = blendclip->getTrack(j);
262  if(blend)
263  w = blend->getData();
264  else
265  continue;
266  }
267  else
268  track = 0;
269 
270  for(k=0; k<samples; k++)
271  {
272  if(!SYSequalZero(total[k], 0.001))
273  {
274  // normalize so all weights add to 1
275  data[k] /= total[k];
276  }
277  else
278  {
279  // hm... weights all zero. use 1st available input.
280 
281  if(track && blend)
282  {
283  time = myClip->getTime(k + myClip->getStart());
284  val = clip->evaluateSingleTime(track,time);
285 
286  data[k] -= w[k]*val;
287  total[k]-= w[k];
288 
289  weight = 1.0 - total[k];
290  total[k]+= weight;
291  data[k] += weight*val;
292 
293  data[k] /= total[k];
294  }
295  }
296  }
297  }
298  else
299  {
300  // when differencing, add in the tracks from the first blend
301  // input using the remaining weight value (the second and
302  // subsequent blend clips subtract their weights from 1).
303  j = myAvailableTracks(i);
304  if (j != -1)
305  {
306  clip = getCacheInputClip(j);
307  track = clip ? clip->getTrack(i) : 0;
308 
309  if (track)
310  {
311  if (myClip->isSameRange(*clip))
312  {
313  const fpreal *src = track->getData();
314  for(k=0; k<samples; k++)
315  data[k] += src[k] * total[k];
316  }
317  else
318  {
319  for(k=0; k<samples; k++)
320  {
321  time= myClip->getTime(k+myClip->getStart());
322  val = clip->evaluateSingleTime(track, time);
323  data[k] += val * total[k];
324  }
325  }
326  }
327  }
328  if(boss->opInterrupt(percent))
329  {
330  stopped = 1;
331  break;
332  }
333 
334  }
335  }
336  if(stopped)
337  break;
338  }
339  }
340  boss->opEnd();
341 
342  return error();
343 }
344 
345 int
346 CHOP_Blend::findInputClips(OP_Context &context, const CL_Clip *blendclip)
347 {
348  // The number of clips we use is the number of tracks in the control input.
349  int num_clips = blendclip->getNumTracks();
350 
351  // When using differencing, you can omit the first channel's weight
352  // assumed to be one always) and blend in other clips.
353  if(GETDIFFERENCE() && FIRST_WEIGHT())
354  num_clips ++;
355 
356  // more weights than clips? Ignore them.
357  if (num_clips >= nInputs())
358  num_clips = nInputs()-1;
359 
360  // marshall the blending clips into a list.
361  myInputClip.setCapacity(num_clips+1);
362  myInputClip.entries(num_clips+1);
363 
364  for (int i=0; i<=num_clips; i++)
365  myInputClip(i) = inputClip(i, context);
366 
367  return num_clips;
368 }
369 
370 
371 
372 int
373 CHOP_Blend::findFirstAvailableTracks(OP_Context &context)
374 {
375  // This creates a union of all tracks from the blending clips, and creates
376  // these tracks in the output clip.
377  int num_motion_tracks = 0;
378  for (int i=1; i<nInputs(); i++)
379  {
380  const CL_Clip *clip = inputClip(i, context);
381 
382  if (!clip)
383  continue;
384 
385  if (clip->getNumTracks() > num_motion_tracks)
386  num_motion_tracks = clip->getNumTracks();
387  }
388 
389 
390  // myAvailableTracks contains the index of the first blend input to contain
391  // each track, normally 1, unless some blend clips have more tracks than
392  // others.
393  myAvailableTracks.setCapacity(num_motion_tracks);
394  myAvailableTracks.entries(num_motion_tracks);
395 
396  for (int i=0; i<num_motion_tracks; i++)
397  {
398  const CL_Track *track = NULL;
399  int j = 1;
400 
401  while (!track && j<nInputs())
402  {
403  const CL_Clip *clip = inputClip(j, context);
404  if (!clip)
405  {
406  j++;
407  continue;
408  }
409 
410  track = clip->getTrack(i);
411  if (!track)
412  j++;
413  }
414 
415  myAvailableTracks(i) = track ? j : -1;
416  }
417 
418  // Create new output tracks for each input track that will be blended.
419  // Copy everything except the track's data.
420  for (int i=0; i<num_motion_tracks; i++)
421  {
422  int j = myAvailableTracks(i);
423 
424  if (j == -1)
425  continue;
426 
427  const CL_Track *track = inputClip(j, context)->getTrack(i);
428  if (track)
429  myClip->dupTrackInfo(track);
430  }
431 
432 
433  return num_motion_tracks;
434 }
435 
436 // install the chop.
438 {
439  table->addOperator(new OP_Operator("hdk_blend", // node name
440  "HDK Blend", // pretty name
443  2, // min inputs
444  9999, // max inputs
446 }
CL_Clip * myClip
Definition: CHOP_Node.h:588
fpreal getStart() const
Definition: CL_Clip.h:76
virtual OP_ERROR error()
bool updateParmsFlags() override
Definition: CHOP_Blend.C:71
void constant(fpreal value=0)
GLboolean * data
Definition: glcorearb.h:131
GT_API const UT_StringHolder time
static OP_TemplatePair myTemplatePair
Definition: CHOP_Node.h:162
CL_Track * getTrack(int index)
Definition: CL_Clip.h:176
PRM_API const PRM_Type PRM_ORD
CHOP_SWITCHER(2,"Blend")
UT_ErrorSeverity
Definition: UT_Error.h:25
T * array()
Definition: UT_Array.h:819
void setCapacity(exint new_capacity)
CHOP_Blend(OP_Network *net, const char *name, OP_Operator *op)
Definition: CHOP_Blend.C:102
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
static OP_VariablePair myVariablePair
Definition: CHOP_Node.h:163
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
Definition: CHOP_Blend.C:92
bool updateParmsFlags() override
short myParmBase
Definition: CHOP_Node.h:585
static OP_TemplatePair myTemplatePair
Definition: CHOP_Blend.h:47
virtual unsigned nInputs() const
vint4 blend(const vint4 &a, const vint4 &b, const vbool4 &mask)
Definition: simd.h:4784
int opInterrupt(int percent=-1)
const CL_Clip * inputClip(int idx, OP_Context &context)
bool enableParm(int pi, int state, int v=-1)
static OP_VariablePair myVariablePair
Definition: CHOP_Blend.h:48
OP_ERROR cookMyChop(OP_Context &context) override
Definition: CHOP_Blend.C:124
static CH_LocalVariable myVariableList[]
Definition: CHOP_Blend.h:50
GLuint const GLchar * name
Definition: glcorearb.h:786
~CHOP_Blend() override
Definition: CHOP_Blend.C:110
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
PRM_API const PRM_Type PRM_SWITCHER
GLsizei samples
Definition: glcorearb.h:1298
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:648
bool SYSequalZero(const UT_Vector3T< T > &v)
Definition: UT_Vector3.h:1069
GLint j
Definition: glad.h:2733
CL_Track * dupTrackInfo(const CL_Track *, int data_too=0, const UT_StringHolder &name=UT_StringHolder::theEmptyString)
PRM_API PRM_Default PRMoneDefaults[]
PRM_API PRM_Name PRMswitcherName
void newChopOperator(OP_OperatorTable *table)
Definition: CHOP_Blend.C:437
fpreal64 fpreal
Definition: SYS_Types.h:277
UT_API UT_Interrupt * UTgetInterrupt()
Obtain global UT_Interrupt singleton.
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
static PRM_Template myTemplateList[]
Definition: CHOP_Blend.h:49
int getTrackLength() const
Definition: CL_Clip.h:82
void constant(const T &v)
Quickly set the array to a single value.
int opStart(const char *opname=0, int npoll=0, int immediate_dialog=0, int *opid=0)
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
int isSameRange(const CL_Clip &v) const
const CL_Clip * copyInputAttributes(OP_Context &context, int destroy=1)
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
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
GLenum src
Definition: glcorearb.h:1793