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