HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_BouncyAgent.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020
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 /*! @file SOP_BouncyAgent.C
29 
30  @brief Demonstrates example for creating a procedural agent primitive.
31 
32  The node creates an agent primitive for every point from its input geometry
33  that uses the same shared agent definition. The agent definition itself is a
34  unit polygonal sphere that has skin weights bound to two joints (a parent and
35  a child) with animation that bounces it in TY.
36 
37 */
38 
39 #include "SOP_BouncyAgent.h"
40 
41 #include <OP/OP_AutoLockInputs.h>
42 #include <OP/OP_Operator.h>
43 #include <OP/OP_OperatorTable.h>
44 #include <PRM/PRM_Include.h>
45 #include <PRM/PRM_Type.h>
46 #include <CH/CH_Manager.h>
47 #include <GU/GU_Agent.h>
49 #include <GU/GU_AgentRig.h>
50 #include <GU/GU_AgentShapeLib.h>
51 #include <GU/GU_AgentLayer.h>
52 #include <GU/GU_PrimPacked.h>
53 #include <GU/GU_PrimSphere.h>
56 #include <GA/GA_AIFIndexPair.h>
57 #include <GA/GA_Names.h>
58 #include <CL/CL_Clip.h>
59 #include <CL/CL_Track.h>
60 #include <UT/UT_Array.h>
61 #include <UT/UT_DSOVersion.h>
62 #include <UT/UT_JSONParser.h>
63 #include <UT/UT_JSONWriter.h>
64 #include <UT/UT_Interrupt.h>
65 #include <UT/UT_String.h>
66 #include <UT/UT_StringArray.h>
67 #include <UT/UT_WorkBuffer.h>
68 #include <stdlib.h>
69 
70 using namespace HDK_Sample;
71 
72 // Provide entry point for installing this SOP.
73 void
75 {
76  OP_Operator *op = new OP_Operator(
77  "hdk_bouncyagent",
78  "BouncyAgent",
81  1, // min inputs
82  1 // max inputs
83  );
84  table->addOperator(op);
85 }
86 
87 OP_Node *
89  OP_Network *net, const char *name, OP_Operator *op)
90 {
91  return new SOP_BouncyAgent(net, name, op);
92 }
93 
94 // SOP Parameters.
95 static PRM_Name sopAgentName("agentname", "Agent Name");
96 static PRM_Default sopAgentNameDef(0, "agent1");
97 static PRM_Name sopHeight("height", "Height");
98 static PRM_Default sopHeightDef(5.0);
99 static PRM_Name sopClipLength("cliplength", "Clip Length"); // seconds
100 static PRM_Name sopClipOffset("clipoffset", "Clip Offset"); // seconds
101 static PRM_Default sopClipOffsetDef(0, "$T");
102 static PRM_Range sopClipOffsetRange(PRM_RANGE_UI, 0, PRM_RANGE_UI, 10);
103 static PRM_Name sopEnableBlendshapes("enableblendshapes",
104  "Enable Blendshapes");
105 static PRM_Name sopReload("reload", "Reload");
106 
109 {
110  PRM_Template(PRM_ALPHASTRING, 1, &sopAgentName, &sopAgentNameDef),
111  PRM_Template(PRM_FLT_J, 1, &sopHeight, &sopHeightDef),
112  PRM_Template(PRM_FLT_J, 1, &sopClipLength, PRMoneDefaults,
113  0, &PRMrulerRange),
114  PRM_Template(PRM_FLT_J, 1, &sopClipOffset, &sopClipOffsetDef,
115  0, &sopClipOffsetRange),
116  PRM_Template(PRM_TOGGLE_J, 1, &sopEnableBlendshapes, PRMzeroDefaults),
117  PRM_Template(PRM_CALLBACK, 1, &sopReload, 0, 0, 0,
118  &SOP_BouncyAgent::onReload),
119  PRM_Template() // sentinel
120 };
121 
122 // Constructor
124  OP_Network *net, const char *name, OP_Operator *op)
125  : SOP_Node(net, name, op)
126 {
127  // This indicates that this SOP manually manages its data IDs,
128  // so that Houdini can identify what attributes may have changed,
129  // e.g. to reduce work for the viewport, or other SOPs that
130  // check whether data IDs have changed.
131  // By default, (i.e. if this line weren't here), all data IDs
132  // would be bumped after the SOP cook, to indicate that
133  // everything might have changed.
134  // If some data IDs don't get bumped properly, the viewport
135  // may not update, or SOPs that check data IDs
136  // may not cook correctly, so be *very* careful!
138 }
139 
140 // Destructor
142 {
143 }
144 
145 enum
146 {
150  // Set up exclusive rig index range for joints
153 };
154 
155 static constexpr UT_StringLit theChannel1Name("channel1");
156 static constexpr UT_StringLit theChannel2Name("channel2");
157 
158 // Create the rig.
159 // Rigs define the names of the transforms in a tree hierarchy.
160 static GU_AgentRigPtr
161 sopCreateRig(const char *path, bool enable_blendshapes)
162 {
163  UT_String rig_name = path;
164  rig_name += "?rig";
165  GU_AgentRigPtr rig = GU_AgentRig::addRig(rig_name);
166 
167  UT_StringArray transforms;
168  transforms.append("skin"); // SOP_SKIN_RIG_INDEX
169  transforms.append("parent"); // SOP_PARENT_RIG_INDEX
170  transforms.append("child"); // SOP_CHILD_RIG_INDEX
171 
172  UT_IntArray child_counts;
173  child_counts.append(0); // 'skin' has 0 children
174  child_counts.append(1); // 'parent' has 1 child
175  child_counts.append(0); // 'child' has 0 children
176 
177  UT_IntArray children;
178  // 'skin' has no children so we don't need to append anything for it
179  children.append(SOP_CHILD_RIG_INDEX); // 'parent' has 'child' as the child
180  // 'child' has no children so we don't need to append anything for it
181 
182  // now construct the rig
183  if (!rig->construct(transforms, child_counts, children))
184  return nullptr;
185 
186  // Define a couple channels for use with the blendshape deformer.
187  if (enable_blendshapes)
188  {
190  channels.append(theChannel1Name.asHolder());
191  channels.append(theChannel2Name.asHolder());
192 
193  UT_Array<GU_AgentRig::FloatType> default_values;
194  default_values.appendMultiple(0.0, 2);
195 
196  // These channels are not associated with any joint.
197  UT_IntArray transforms;
198  transforms.appendMultiple(-1, 2);
199 
200  UT_StringArray errors;
201  if (!rig->addChannels(channels, default_values, transforms, errors))
202  return nullptr;
203  }
204 
205  return rig;
206 }
207 
208 static GU_Detail *
209 sopCreateSphere(bool for_default)
210 {
211  GU_Detail *shape = new GU_Detail;
212  GU_PrimSphereParms sphere(shape);
213  if (for_default)
214  {
215  sphere.freq = 2;
216  sphere.type = GEO_PATCH_TRIANGLE;
218  }
219  else
220  {
221  // Position and scale the collision sphere to account for the
222  // deformation animation.
223  sphere.xform.scale(1.5, 1, 1.5);
224  sphere.xform.translate(0, 1, 0.5);
226  }
227  return shape;
228 }
229 
230 /// Create a blendshape input from a sparse subset of the base shape's points.
231 static void
232 sopAddBlendshapeInput(const GU_Detail &src, int increment,
233  const UT_StringRef &channel_name, GU_DetailHandle &gdh,
234  UT_StringHolder &shape_name)
235 {
236  shape_name.format("{}.blendshape.{}", GU_AGENT_LAYER_DEFAULT,
237  channel_name);
238 
239  GU_Detail *gdp = new GU_Detail;
240  gdh.allocateAndSet(gdp);
241 
242  // The 'id' attribute is used to match up points from the base shape.
243  GA_RWHandleI id_attrib =
245 
246  UT_Matrix4D xform(1.0);
247  xform.scale(1.5);
248 
249  const GA_Size n = src.getNumPoints() / increment;
250  GA_Offset ptoff = gdp->appendPointBlock(n);
251  for (GA_Index i = 0; i < n; ++i, ++ptoff)
252  {
253  const GA_Index src_idx = i * increment;
254  UT_Vector3 pos = src.getPos3(src.pointOffset(src_idx)) * xform;
255 
256  gdp->setPos3(ptoff, pos);
257  id_attrib.set(ptoff, src_idx);
258  }
259 }
260 
261 static void
262 sopAddBlendshapes(const GU_AgentRig &rig, GU_AgentShapeLib &shapelib,
263  GU_DetailHandle &base_shape)
264 {
265  GU_DetailHandleAutoWriteLock base_shape_gdp(base_shape);
266 
267  // Create the input shapes and add them to the shape library.
268  GU_DetailHandle shape1;
269  UT_StringHolder shape1_name;
270  sopAddBlendshapeInput(*base_shape_gdp, 3, theChannel1Name.asRef(), shape1,
271  shape1_name);
272  GU_DetailHandle shape2;
273  UT_StringHolder shape2_name;
274  sopAddBlendshapeInput(*base_shape_gdp, 4, theChannel2Name.asRef(), shape2,
275  shape2_name);
276 
277  shapelib.addShape(shape1_name, shape1);
278  shapelib.addShape(shape2_name, shape2);
279 
280  // Add these shapes as inputs for the base shape, so that the blendshape
281  // deformer can locate them.
282  UT_StringArray shape_names;
283  shape_names.append(shape1_name);
284  shape_names.append(shape2_name);
285 
286  UT_StringArray channel_names;
287  channel_names.append(theChannel1Name.asHolder());
288  channel_names.append(theChannel2Name.asHolder());
289 
290  GU_AgentBlendShapeUtils::addInputsToBaseShape(*base_shape_gdp, shape_names,
291  channel_names);
292 }
293 
294 // For simplicity, we bind all points to all the transforms except for
295 // SOP_SKIN_RIG_INDEX.
296 static void
297 sopAddSkinWeights(const GU_AgentRig &rig, const GU_DetailHandle &shape)
298 {
299  GU_DetailHandleAutoWriteLock gdl(shape);
300  GU_Detail *gdp = gdl.getGdp();
301 
302  // Create skinning attribute with 2 regions and each point is bound to both
303  // rig transforms.
304  int num_regions = (SOP_JOINT_END - SOP_JOINT_BEGIN);
305  GEO_Detail::geo_NPairs num_pairs_per_pt(num_regions);
306  GA_RWAttributeRef capt = gdp->addPointCaptureAttribute(num_pairs_per_pt);
307  int regions_i = -1;
308  GA_AIFIndexPairObjects *regions
310  capt, regions_i);
311  regions->setObjectCount(num_regions);
312 
313  // Tell the skinning attribute the names of the rig transforms. This needs
314  // to be done after calling regions->setObjectCount().
316  for (int i = 0; i < num_regions; ++i)
317  paths.setPath(i, rig.transformName(SOP_JOINT_BEGIN + i));
318 
319  // Set up the rest transforms for the skin weight bindings. For efficiency,
320  // these are actually stored in the skin weight bindings as the INVERSE of
321  // the world space rest transform of the joint.
322  for (int i = 0; i < num_regions; ++i)
323  {
325  // Recall that the unit sphere is created at the origin. So to position
326  // the parent joint at the base of the sphere, it should be a position
327  // (0, -1, 0). However, since these are actually inverses, we'll
328  // translate by the opposite sign instead.
329  if (i == 0)
330  r.myXform.translate(0, +1.0, 0);
331  else
332  r.myXform.translate(0, -1.0, 0);
333  regions->setObjectValues(i, regions_i, r.floatPtr(),
335  }
336 
337  // Set up the weights
338  const GA_AIFIndexPair *weights = capt->getAIFIndexPair();
339  weights->setEntries(capt, num_regions);
340  for (GA_Offset ptoff : gdp->getPointRange())
341  {
342  for (int i = 0; i < num_regions; ++i)
343  {
344  // Set the region index that the point is captured by.
345  // Note that these index into 'paths' above.
346  weights->setIndex(capt, ptoff, /*entry*/i, /*region*/i);
347  // Set the weight that the point is captured by transform i.
348  // Notice that all weights for the point should sum to 1.
349  fpreal weight = 1.0/num_regions;
350  weights->setData(capt, ptoff, /*entry*/i, weight);
351  }
352  }
353 }
354 
355 // Default convention is to prefix the shape name by the layer name
356 #define SOP_DEFAULT_SKIN_NAME GU_AGENT_LAYER_DEFAULT".skin"
357 #define SOP_COLLISION_SKIN_NAME GU_AGENT_LAYER_COLLISION".skin"
358 
359 // Create the shape library which contains a list of all the shape geometries
360 // that can be attached to the rig transforms.
361 static GU_AgentShapeLibPtr
362 sopCreateShapeLib(const char *path, const GU_AgentRig &rig,
363  bool enable_blendshapes)
364 {
365  UT_String shapelib_name = path;
366  shapelib_name += "?shapelib";
367  GU_AgentShapeLibPtr shapelib = GU_AgentShapeLib::addLibrary(shapelib_name);
368 
369  GU_DetailHandle skin_geo;
370  skin_geo.allocateAndSet(sopCreateSphere(true), /*own*/true);
371  sopAddSkinWeights(rig, skin_geo);
372 
373  if (enable_blendshapes)
374  sopAddBlendshapes(rig, *shapelib, skin_geo);
375 
376  shapelib->addShape(SOP_DEFAULT_SKIN_NAME, skin_geo);
377 
378  // The collision geometry is intended to be simplified versions of the
379  // default layer shapes. The bounding box for the default layer shape is
380  // computed from the corresponding collision layer shape.
381  GU_DetailHandle coll_geo;
382  coll_geo.allocateAndSet(sopCreateSphere(false), /*own*/true);
383  shapelib->addShape(SOP_COLLISION_SKIN_NAME, coll_geo);
384 
385  return shapelib;
386 }
387 
388 // Create a default layer with the sphere as the geometry bound to the rig.
389 // Layers assign geometry from the shapelib to be used for the rig
390 // transforms.
391 // Agents must have at least 2 layers:
392 // GU_AGENT_LAYER_DEFAULT ("default"
393 // - Used for display/render
394 // GU_AGENT_LAYER_COLLISION ("collision")
395 // - Simple geometry to be used for the bounding box that
396 // encompasses the corresponding shape in default layer for all
397 // possible local deformations.
398 // In general, it can have more layers and we can set which of those we use
399 // for the default and collision.
400 static GU_AgentLayerPtr
401 sopCreateDefaultLayer(
402  const char *path,
403  const GU_AgentRigPtr &rig,
404  const GU_AgentShapeLibPtr &shapelib,
405  bool enable_blendshapes)
406 {
407  UT_StringArray shape_names;
408  UT_IntArray transform_indices;
410 
411  // Simply bind the skin geometry to the 'skin' transform which we know is
412  // transform index 0.
413  shape_names.append(SOP_DEFAULT_SKIN_NAME);
414  transform_indices.append(SOP_SKIN_RIG_INDEX);
415 
416  // Use either the normal linear skin deformer, or the blendshape + skin
417  // deformer.
418  // For a shape that is rigidly transformed, no deformer is needed (nullptr)
419  if (enable_blendshapes)
421  else
423 
424  UT_String unique_name = path;
425  unique_name += "?default_layer";
427  = GU_AgentLayer::addLayer(unique_name, rig, shapelib);
428  if (!layer->construct(shape_names, transform_indices, deformers))
429  return nullptr;
430 
432  layer->setName(layer_name);
433 
434  return layer;
435 }
436 
437 // See also comments for sopCreateDefaultLayer().
438 static GU_AgentLayerPtr
439 sopCreateCollisionLayer(
440  const char *path,
441  const GU_AgentRigPtr &rig,
442  const GU_AgentShapeLibPtr &shapelib)
443 {
444  UT_StringArray shape_names;
445  UT_IntArray transform_indices;
446  UT_Array<bool> deforming;
447 
448  // For character rigs, the collision shapes are typically attached to the
449  // joint transforms so that they can proxy for skin deformation.
450  shape_names.append(SOP_COLLISION_SKIN_NAME);
451  transform_indices.append(SOP_PARENT_RIG_INDEX);
452  deforming.append(false); // has NO skin weights
453 
454  UT_String unique_name = path;
455  unique_name += "?collision_layer";
457  layer = GU_AgentLayer::addLayer(unique_name, rig, shapelib);
458  if (!layer->construct(shape_names, transform_indices, deforming))
459  return nullptr;
460 
462  layer->setName(layer_name);
463 
464  return layer;
465 }
466 
467 static fpreal*
468 sopAddTrack(CL_Clip &chans, const GU_AgentRig &rig, int i, const char *trs_name)
469 {
470  UT_WorkBuffer str;
471  str.sprintf("%s:%s", rig.transformName(i).buffer(), trs_name);
472  return chans.addTrack(str.buffer())->getData();
473 }
474 
475 // Create some bouncy animation for the agent
476 static GU_AgentClipPtr
477 sopCreateBounceClip(CL_Clip &chans, const GU_AgentRigPtr &rig, fpreal height,
478  bool enable_blendshapes)
479 {
480  int num_samples = chans.getTrackLength();
481 
482  // Set the ty of the 'parent' transform that bounces. Note that these
483  // transforms here are in local space. The valid channel names are:
484  // Translate: tx ty tz
485  // Rotate: rx ry rz (euler angles in degrees, XYZ rotation order)
486  // Scale: sx sy sz
487  fpreal *ty = sopAddTrack(chans, *rig, SOP_PARENT_RIG_INDEX, "ty");
488  fpreal *sy = sopAddTrack(chans, *rig, SOP_PARENT_RIG_INDEX, "sy");
489  for (int i = 0; i < num_samples; ++i)
490  {
491  ty[i] = height * sin(i * M_PI / (num_samples-1));
492  // add some squash and stretch
493  sy[i] = 1.0 - SYSabs(0.5 * sin(i * M_PI / (num_samples-1)));
494  }
495 
496  // Set the ty of the 'child' transform. Note that these transforms here are
497  // in local space.
498  fpreal *tz = sopAddTrack(chans, *rig, SOP_CHILD_RIG_INDEX, "tz");
499  ty = sopAddTrack(chans, *rig, SOP_CHILD_RIG_INDEX, "ty");
500  for (int i = 0; i < num_samples; ++i)
501  {
502  ty[i] = 2.0; // sphere diameter
503  tz[i] = 1.5 * sin(i * M_PI / (num_samples-1)); // sway a bit forwards
504  }
505 
506  // Set up channels to drive the blendshapes.
507  if (enable_blendshapes)
508  {
509  fpreal *chan1 = chans.addTrack(theChannel1Name.asHolder())->getData();
510  fpreal *chan2 = chans.addTrack(theChannel2Name.asHolder())->getData();
511 
512  for (int i = 0; i < num_samples; ++i)
513  {
514  chan1[i] = 0.5 + 0.5 * SYScos(i * 2 * M_PI / num_samples + M_PI);
515  chan2[i] = 0.5 + 0.5 * SYScos(i * 2 * M_PI / num_samples);
516  }
517  }
518 
519  // Finally load the agent clip from the CL_Clip animation we created
520  GU_AgentClipPtr clip = GU_AgentClip::addClip("bounce", rig);
521  if (!clip)
522  return nullptr;
523  clip->load(chans);
524 
525  return clip;
526 }
527 
528 static constexpr UT_StringLit theCustomDataItemType("bouncyagentdata");
529 static constexpr UT_StringLit theValueToken("myvalue");
530 static constexpr UT_StringLit theNameToken("myname");
531 
532 /// Example implementation of a custom data item that can be added to a
533 /// GU_AgentDefinition.
535 {
536 public:
537  GU_BouncyAgentCustomData() : myValue(0) {}
538 
540  : GU_AgentCustomDataItem(), myName(name), myValue(value)
541  {
542  }
543 
545  {
546  return new GU_BouncyAgentCustomData();
547  }
548 
549  const UT_StringHolder &dataItemType() const override
550  {
551  return theCustomDataItemType.asHolder();
552  }
553 
554  int64 getMemoryUsage(bool inclusive) const override
555  {
556  int64 mem = inclusive ? sizeof(*this) : 0;
557  mem += myName.getMemoryUsage(false);
558  return mem;
559  }
560 
561  const UT_StringHolder &name() const override { return myName; }
562  int value() const { return myValue; }
563 
564  /// @{
565  /// Implements serialization to and from JSON.
566  /// For debugging, save to an ASCII (.geo) geometry file.
567  bool load(UT_JSONParser &p) override
568  {
569  UT_StringHolder key;
570  for (auto it = p.beginMap(); !it.atEnd(); ++it)
571  {
572  if (!p.parseKey(key))
573  return false;
574 
575  if (key == theValueToken.asHolder())
576  {
577  if (!p.parseInt(myValue))
578  return false;
579  }
580  else if (key == theNameToken.asHolder())
581  {
582  if (!p.parseString(myName))
583  return false;
584  }
585  else
586  {
587  p.addWarning("Unknown key '%s'", key.buffer());
588  if (!p.skipNextObject())
589  return false;
590  }
591  }
592 
593  return true;
594  }
595 
596  bool save(UT_JSONWriter &w) const override
597  {
598  bool ok = true;
599  ok = ok && w.jsonBeginMap();
600 
601  ok = ok && w.jsonKey(theNameToken.asHolder());
602  ok = ok && w.jsonValue(myName);
603 
604  ok = ok && w.jsonKey(theValueToken.asHolder());
605  ok = ok && w.jsonValue(myValue);
606 
607  ok = ok && w.jsonEndMap();
608  return ok;
609  }
610  /// @}
611 
612 private:
613  UT_StringHolder myName;
614  int64 myValue;
615 };
616 
617 /// Entry point for registering GU_BouncyAgentCustomData.
618 void
620 {
622  theCustomDataItemType.asHolder(), GU_BouncyAgentCustomData::construct);
623 }
624 
625 // Enable this to debug our definition
626 #define SOP_SAVE_AGENT_DEFINITION 0
627 
628 // Create the agent definition
630 SOP_BouncyAgent::createDefinition(fpreal t) const
631 {
632  // Typically, the definition is loaded from disk which has a filename for
633  // each of the different parts. Since we're doing this procedurally, we're
634  // going to just make up some arbitrary unique names using our node path.
635  UT_String path;
636  getFullPath(path);
637 
638  const bool enable_blendshapes = BLENDSHAPES(t);
639 
640  GU_AgentRigPtr rig = sopCreateRig(path, enable_blendshapes);
641  if (!rig)
642  return nullptr;
643 
644  GU_AgentShapeLibPtr shapelib =
645  sopCreateShapeLib(path, *rig, enable_blendshapes);
646  if (!shapelib)
647  return nullptr;
648 
649  GU_AgentLayerPtr default_layer =
650  sopCreateDefaultLayer(path, rig, shapelib, enable_blendshapes);
651  if (!default_layer)
652  return nullptr;
653 
654  GU_AgentLayerPtr collision_layer = sopCreateCollisionLayer(path,
655  rig, shapelib);
656  if (!collision_layer)
657  return nullptr;
658 
659  CL_Clip chans(CHgetManager()->getSample(CLIPLENGTH(t)));
660  chans.setSampleRate(CHgetManager()->getSamplesPerSec());
661  GU_AgentClipPtr clip =
662  sopCreateBounceClip(chans, rig, HEIGHT(t), enable_blendshapes);
663  if (!clip)
664  return nullptr;
665 
666 #if SOP_SAVE_AGENT_DEFINITION
667  // Once we have the definition, we can save out the files for loading with
668  // the Agent SOP as well. Or, we can examine them for debugging purposes.
669  {
670  UT_AutoJSONWriter writer("bouncy_rig.rig", /*binary*/false);
671  rig->save(writer);
672  }
673  {
674  UT_AutoJSONWriter writer("bouncy_shapelib.bgeo", /*binary*/true);
675  shapelib->save(writer);
676  }
677  {
678  UT_AutoJSONWriter writer("bouncy_layer.default.lay", /*binary*/false);
679  default_layer->save(writer);
680  }
681  {
682  UT_AutoJSONWriter writer("bouncy_layer.collision.lay", /*binary*/false);
683  collision_layer->save(writer);
684  }
685  {
686  chans.save("bouncy_bounce.bclip");
687  }
688 #endif
689 
690  // The agent definition is used to create agent primitives. Many agent
691  // primitives can share the same definition.
692  GU_AgentDefinitionPtr def(new GU_AgentDefinition(rig, shapelib));
693  // The definition has a number of layers that be can assigned
694  def->addLayer(default_layer);
695  def->addLayer(collision_layer);
696  // ... and a number of clips that can be assigned to specific agent prims
697  def->addClip(clip);
698 
699  // Add some custom data for demonstration purposes.
700  def->addCustomDataItem(new GU_BouncyAgentCustomData("mycustomdata", 42));
701 
702  return def;
703 }
704 
705 /*static*/ int
706 SOP_BouncyAgent::onReload(
707  void *data, int index, fpreal t, const PRM_Template *tplate)
708 {
709  SOP_BouncyAgent* sop = static_cast<SOP_BouncyAgent*>(data);
710  if (!sop->getHardLock()) // only allow reloading if we're not locked
711  {
712  sop->myDefinition.reset();
713  sop->forceRecook();
714  }
715  return 1;
716 }
717 
718 // Compute the output geometry for the SOP.
719 OP_ERROR
721 {
722  fpreal t = context.getTime();
723 
724  // We must lock our inputs before we try to access their geometry.
725  // OP_AutoLockInputs will automatically unlock our inputs when we return.
726  // NOTE: Don't call unlockInputs yourself when using this!
727  OP_AutoLockInputs inputs(this);
728  if (inputs.lock(context) >= UT_ERROR_ABORT)
729  return error();
730 
731  // Duplicate the input geometry, but only if it was changed
732  int input_changed;
733  duplicateChangedSource(/*input*/0, context, &input_changed);
734 
735  // Detect if we need to rebuild the agent definition. For simplicity, we'll
736  // rebuild if any of our agent parameters changed. Note the use of the
737  // bitwise or operator to ensure that isParmDirty() is always called for
738  // all parameters.
739  bool agent_changed = isParmDirty(sopAgentName.getToken(), t);
740  agent_changed = isParmDirty(sopHeight.getToken(), t);
741  agent_changed |= isParmDirty(sopClipLength.getToken(), t);
742  agent_changed |= isParmDirty(sopEnableBlendshapes.getToken(), t);
743 
744  if (!myDefinition)
745  {
746  agent_changed = true;
747  input_changed = true;
748  }
749 
750  if (agent_changed)
751  {
752  myDefinition = createDefinition(t);
753  if (!myDefinition)
754  {
755  addError(SOP_MESSAGE, "Failed to create definition");
756  return error();
757  }
758  }
759 
760  if (input_changed)
761  {
762  // Delete all the primitives, keeping only the points
764 
765  // Create the agent primitives
766  myPrims.clear();
767  for (GA_Offset ptoff : gdp->getPointRange())
768  {
769  myPrims.append(GU_Agent::agent(*gdp, ptoff));
770  }
771 
772  // Bumping these 2 attribute owners is what we need to do when adding
773  // pack agent prims because it has a single vertex.
776  gdp->getPrimitiveList().bumpDataId(); // modified primitives
777  }
778 
779  if (agent_changed || input_changed)
780  {
781  // Create a name attribute for the agents
783  "name", 1));
784 
785  GU_AgentLayerConstPtr current_layer =
786  myDefinition->layer(UTmakeUnsafeRef(GU_AGENT_LAYER_DEFAULT));
787  GU_AgentLayerConstPtr collision_layer =
788  myDefinition->layer(UTmakeUnsafeRef(GU_AGENT_LAYER_COLLISION));
789 
790  // Set the agent definition to the agent prims
792  UT_String agent_name;
793  AGENTNAME(agent_name, t);
794  int name_i = 0;
795  for (GU_PrimPacked *pack : myPrims)
796  {
797  GU_Agent* agent = UTverify_cast<GU_Agent*>(pack->hardenImplementation());
798  agent->setDefinition(pack, myDefinition);
799 
800  agent->setCurrentLayer(pack, current_layer);
801  agent->setCollisionLayer(pack, collision_layer);
802 
803  // We only have 1 clip that can be used in the definition here.
804  UT_StringArray clips;
805  clips.append(myDefinition->clip(0).name());
806  agent->setClipsByNames(pack, clips);
807 
808  // Convention for the agent primitive names is agentname_0,
809  // agentname_1, agentname_2, etc.
810  name.sprintf("%s_%d", agent_name.buffer(), name_i);
811  name_attrib.set(pack->getMapOffset(), name.buffer());
812  ++name_i;
813  }
814 
815  // Mark what modified
816  gdp->getPrimitiveList().bumpDataId();
817  name_attrib.bumpDataId();
818  }
819 
820  // Set the clip information for the agents. In general, agents can be set
821  // to evaluate an blended array of clips to evaluated at a specific clip
822  // offset.
823  for (GU_PrimPacked *pack : myPrims)
824  {
825  GU_Agent* agent = UTverify_cast<GU_Agent*>(pack->hardenImplementation());
826  agent->setClipTime(pack, /*clip index*/0, CLIPOFFSET(t));
827  }
828  gdp->getPrimitiveList().bumpDataId(); // we modified primitives
829 
830  return error();
831 }
832 
833 // Provide input labels.
834 const char *
835 SOP_BouncyAgent::inputLabel(unsigned /*input_index*/) const
836 {
837  return "Points to attach agents";
838 }
GLbyte * weights
Definition: glew.h:7551
CL_Track * addTrack(const UT_StringHolder &name)
bool jsonValue(bool value)
SYS_FORCE_INLINE void setPos3(GA_Offset ptoff, const UT_Vector3 &P)
Set P from a UT_Vector3.
Definition: GA_Detail.h:207
PRM_API PRM_Default PRMzeroDefaults[]
const GA_AIFIndexPair * getAIFIndexPair() const
bool parseString(UT_WorkBuffer &v)
PRM_API const PRM_Type PRM_CALLBACK
GLenum src
Definition: glew.h:2410
Generic Attribute Interface class to get/set data as index pairs.
GLuint const GLchar * name
Definition: glew.h:1814
virtual OP_ERROR error()
static GU_PrimPacked * agent(GU_Detail &dest, GA_Offset ptoff=GA_INVALID_OFFSET)
Convenience method to create a packed agent primitive.
virtual bool setIndex(GA_Attribute *attrib, GA_Offset ai, int entry, int32 index) const =0
Class which stores the default values for a GA_Attribute.
Definition: GA_Defaults.h:35
bool jsonBeginMap()
Begin a map/object dictionary.
OP_ERROR lock(OP_Context &context)
Locks all inputs.
GLuint index
Definition: glew.h:1814
#define SOP_COLLISION_SKIN_NAME
fpreal getTime() const
Definition: OP_Context.h:60
GLuint GLuint GLfloat weight
Definition: glew.h:13609
static GU_AgentCustomDataItemPtr construct(const GU_AgentDefinition &)
GLenum GLsizei GLenum GLenum const void * table
Definition: glew.h:4940
void setClipsByNames(GU_PrimPacked *prim, const UT_StringArray &clip_names)
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1252
const UT_StringHolder & transformName(exint i) const
Return the name of the given transform.
Definition: GU_AgentRig.h:99
virtual void forceRecook(bool evensmartcache=true)
SYS_FORCE_INLINE const char * buffer() const
const UT_StringHolder & name() const override
#define SYSabs(a)
Definition: SYS_Math.h:1449
UT_ErrorSeverity
Definition: UT_Error.h:25
JSON reader class which handles parsing of JSON or bJSON files.
Definition: UT_JSONParser.h:75
#define SOP_DEFAULT_SKIN_NAME
PRM_API PRM_Default PRMoneDefaults[]
basic_writer< back_insert_range< internal::buffer > > writer
Definition: format.h:361
bool save(UT_JSONWriter &w) const override
Class which writes ASCII or binary JSON streams.
Definition: UT_JSONWriter.h:32
SYS_FORCE_INLINE TO_T UTverify_cast(FROM_T from)
Definition: UT_Assert.h:208
void setDefinition(GU_PrimPacked *prim, const GU_AgentDefinitionPtr &definition)
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
Convenience class to store a bone capture region.
Read-Write string handle.
Definition: GA_Handle.h:861
SYS_FORCE_INLINE UT_Vector3 getPos3(GA_Offset ptoff) const
The ptoff passed is the point offset.
Definition: GA_Detail.h:174
void GUregisterAgentCustomDataItemType()
Entry point for registering GU_BouncyAgentCustomData.
void addError(int code, const char *msg=0)
Definition: SOP_Node.h:1153
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:231
#define M_PI
Definition: ImathPlatform.h:51
GU_BouncyAgentCustomData(const UT_StringHolder &name, int value)
GA_Size destroyPrimitives(const GA_Range &it, bool and_points=false)
Definition: GA_Detail.h:623
int getHardLock() const
Definition: OP_Node.h:1198
static GEO_Primitive * build(const GU_PrimSphereParms &parms, GA_PrimitiveTypeId type=GEO_PRIMSPHERE)
void allocateAndSet(GU_Detail *gdp, bool own=true)
GA_Size GA_Offset
Definition: GA_Types.h:637
void setCurrentLayer(GU_PrimPacked *prim, const GU_AgentLayerConstPtr &layer)
Sets the agent's display layer.
bool parseInt(int64 &v)
Alternate short-form.
long long int64
Definition: SYS_Types.h:111
const char * buffer() const
Definition: UT_String.h:503
This class provides a way to manage a reference to an attribute permitting Read-Write access...
A rig for the agent primitive.
Definition: GU_AgentRig.h:34
static OP_Node * myConstructor(OP_Network *, const char *, OP_Operator *)
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1644
CH_Manager * CHgetManager()
Definition: CH_Manager.h:1718
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.
SYS_FORCE_INLINE const char * buffer() const
SYS_FORCE_INLINE GA_Offset appendPointBlock(GA_Size npoints)
Append new points, returning the first offset of the contiguous block.
Definition: GA_Detail.h:281
void bumpDataId()
Use this to mark primitives or their intrinsic data as dirty.
GLint GLenum GLsizei GLint GLsizei const void * data
Definition: glew.h:1379
static GU_AgentLayerPtr addLayer(const UT_StringHolder &unique_name, const GU_AgentRigConstPtr &rig, const GU_AgentShapeLibConstPtr &shapelib)
PRM_API const PRM_Type PRM_TOGGLE_J
SYS_FORCE_INLINE const UT_StringHolder & UTmakeUnsafeRef(const UT_StringRef &ref)
Convert a UT_StringRef into a UT_StringHolder that is a shallow reference.
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1890
void newSopOperator(OP_OperatorTable *table)
bool skipNextObject()
Simple convenience method to skip the next object in the stream.
GA_AttributeSet & getAttributes()
Definition: GA_Detail.h:739
int getTrackLength(void) const
Definition: CL_Clip.h:82
int save(UT_OStream &os, const CL_ClipPrivateIO *priv=0, bool use_blosc_compresssion=false) const
GLsizei n
Definition: glew.h:4040
void appendMultiple(const T &t, exint count)
Definition: UT_ArrayImpl.h:195
Wrapper around hboost::intrusive_ptr.
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1620
virtual void setObjectValues(int objid, int propid, const fpreal32 *v, int tuple_size)=0
virtual bool setData(GA_Attribute *attrib, GA_Offset ai, int entry, fpreal32 data, int data_component=0) const =0
static GA_AIFIndexPairObjects * getBoneCaptureRegionObjects(const GA_RWAttributeRef &h, int &property_i)
void void addWarning(const char *fmt,...) SYS_PRINTF_CHECK_ATTRIBUTE(2
bool jsonKey(const char *value, int64 length=0)
UT_StringHolder getFullPath() const
Definition: PRM_ParmOwner.h:60
bool load(UT_JSONParser &p) override
#define GU_AGENT_LAYER_COLLISION
Definition: GU_Agent.h:33
size_t format(const char *fmt, const Args &...args)
Format a string using the same formatting codes as UTformat.
GA_API const UT_StringHolder id
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:631
PRM_API const PRM_Type PRM_FLT_J
bool parseKey(UT_WorkBuffer &v)
bool addShape(const UT_StringHolder &key, const GU_ConstDetailHandle &gdp, bool replace_existing=true)
Add entire geometry as a shape in the library.
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:34
PRM_API const PRM_Type PRM_ALPHASTRING
static GU_AgentShapeLibPtr addLibrary(const UT_StringHolder &name)
Create a new library using the given name.
int sprintf(const char *fmt,...) SYS_PRINTF_CHECK_ATTRIBUTE(2
int64 getMemoryUsage(bool inclusive) const override
The amount of memory used by this item.
GLsizei const GLchar *const * path
Definition: glew.h:6461
SYS_FORCE_INLINE const UT_StringRef & asRef() const
GLfloat GLfloat p
Definition: glew.h:16321
static GU_AgentClipPtr addClip(const UT_StringHolder &name, const GU_AgentRigConstPtr &rig)
Create an empty clip.
double fpreal
Definition: SYS_Types.h:276
void setSampleRate(fpreal rate)
Definition: CL_Clip.h:87
IMATH_INTERNAL_NAMESPACE_HEADER_ENTER T clip(const T &p, const Box< T > &box)
Definition: ImathBoxAlgo.h:89
SOP_BouncyAgent(OP_Network *net, const char *name, OP_Operator *op)
virtual bool setEntries(GA_Attribute *attrib, int n) const =0
SYS_FORCE_INLINE void set(GA_Offset off, const T &val) const
Definition: GA_Handle.h:320
#define GU_AGENT_LAYER_DEFAULT
Definition: GU_Agent.h:32
void setClipTime(GU_PrimPacked *prim, exint i, fpreal seconds)
GLdouble GLdouble GLdouble r
Definition: glew.h:1406
SYS_FORCE_INLINE const UT_StringHolder & asHolder() const
GLint GLboolean GLint layer
Definition: glew.h:3601
PRM_API PRM_Range PRMrulerRange
static PRM_Template myTemplateList[]
bool jsonEndMap()
End the object.
exint append(void)
Definition: UT_Array.h:95
const GA_PrimitiveList & getPrimitiveList() const
Definition: GA_Detail.h:735
static GU_AgentRigPtr addRig(const UT_StringHolder &name)
virtual void setObjectCount(int nobj)=0
void bumpAllDataIds(GA_AttributeOwner owner)
Bumps all data IDs of attributes of the specified owner.
void clear()
Resets list to an empty list.
Definition: UT_Array.h:519
iterator beginMap()
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1647
static void registerCustomDataItemType(const UT_StringHolder &dataitemtype, CustomDataItemConstructor constructor)
Register a new custom data item type.
GA_Attribute * addIntTuple(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size, const GA_Defaults &defaults=GA_Defaults(0), const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, GA_Storage storage=GA_STORE_INT32, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
GA_Attribute * addPointCaptureAttribute(geo_NPairs n_pairs, CaptureType t=CAPTURE_BONE, GA_Storage s=GA_STORE_INVALID)
Add the (index, weight) point attribute for capture type t.
static GU_AgentShapeDeformerConstPtr getBlendShapeAndSkinDeformer(GU_AgentLinearSkinDeformer::Method skinning_method=GU_AgentLinearSkinDeformer::Method::Linear)
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:296
virtual const char * inputLabel(unsigned input_index) const
Method to provide input labels.
GLsizei const GLfloat * value
Definition: glew.h:1849
const UT_StringHolder & dataItemType() const override
void translate(T dx, T dy, T dz=0.0f)
Definition: UT_Matrix4.h:724
GLdouble GLdouble t
Definition: glew.h:1398
void GU_API addInputsToBaseShape(GU_Detail &base_shape, const UT_StringArray &shape_names, const UT_StringArray &channel_names)
SYS_API double sin(double x)
Definition: SYS_FPUMath.h:71
virtual OP_ERROR cookMySop(OP_Context &context)
Method to cook geometry for the SOP.
ImageBuf OIIO_API channels(const ImageBuf &src, int nchannels, cspan< int > channelorder, cspan< float > channelvalues={}, cspan< std::string > newchannelnames={}, bool shuffle_channel_names=false, int nthreads=0)
void setCollisionLayer(GU_PrimPacked *prim, const GU_AgentLayerConstPtr &layer)
Sets the agent's collision layer.
const char * getToken() const
Definition: PRM_Name.h:83
GLenum const void * paths
Definition: glew.h:13589
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:285
bool isParmDirty(int idx, fpreal t)
GA_Attribute * addStringTuple(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size, const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
static GU_AgentShapeDeformerConstPtr getLinearSkinDeformer(GU_AgentLinearSkinDeformer::Method method=GU_AgentLinearSkinDeformer::Method::Linear)