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) 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 /*! @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().
315  GEO_RWAttributeCapturePath paths(gdp);
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_Array<exint> transform_indices;
410  UT_Array<UT_Vector3F> bounds_scales;
411 
412  // Simply bind the skin geometry to the 'skin' transform which we know is
413  // transform index 0.
414  shape_names.append(SOP_DEFAULT_SKIN_NAME);
415  transform_indices.append(SOP_SKIN_RIG_INDEX);
416 
417  // Use either the normal linear skin deformer, or the blendshape + skin
418  // deformer.
419  // For a shape that is rigidly transformed, no deformer is needed (nullptr)
420  if (enable_blendshapes)
422  else
424 
425  bounds_scales.append(UT_Vector3F(1.0));
426 
427  UT_String unique_name = path;
428  unique_name += "?default_layer";
430  = GU_AgentLayer::addLayer(unique_name, rig, shapelib);
431  if (!layer->construct(
432  shape_names, transform_indices, deformers, bounds_scales))
433  {
434  return nullptr;
435  }
436 
438  layer->setName(layer_name);
439 
440  return layer;
441 }
442 
443 // See also comments for sopCreateDefaultLayer().
444 static GU_AgentLayerPtr
445 sopCreateCollisionLayer(
446  const char *path,
447  const GU_AgentRigPtr &rig,
448  const GU_AgentShapeLibPtr &shapelib)
449 {
450  UT_StringArray shape_names;
451  UT_Array<exint> transform_indices;
453  UT_Array<UT_Vector3F> bounds_scales;
454 
455  // For character rigs, the collision shapes are typically attached to the
456  // joint transforms so that they can proxy for skin deformation.
457  shape_names.append(SOP_COLLISION_SKIN_NAME);
458  transform_indices.append(SOP_PARENT_RIG_INDEX);
459  deformers.append(nullptr); // has no deformer
460  bounds_scales.append(UT_Vector3F(1.0));
461 
462  UT_String unique_name = path;
463  unique_name += "?collision_layer";
465  layer = GU_AgentLayer::addLayer(unique_name, rig, shapelib);
466  if (!layer->construct(
467  shape_names, transform_indices, deformers, bounds_scales))
468  {
469  return nullptr;
470  }
471 
473  layer->setName(layer_name);
474 
475  return layer;
476 }
477 
478 static fpreal*
479 sopAddTrack(CL_Clip &chans, const GU_AgentRig &rig, int i, const char *trs_name)
480 {
481  UT_WorkBuffer str;
482  str.sprintf("%s:%s", rig.transformName(i).buffer(), trs_name);
483  return chans.addTrack(str.buffer())->getData();
484 }
485 
486 // Create some bouncy animation for the agent
487 static GU_AgentClipPtr
488 sopCreateBounceClip(CL_Clip &chans, const GU_AgentRigPtr &rig, fpreal height,
489  bool enable_blendshapes)
490 {
491  int num_samples = chans.getTrackLength();
492 
493  // Set the ty of the 'parent' transform that bounces. Note that these
494  // transforms here are in local space. The valid channel names are:
495  // Translate: tx ty tz
496  // Rotate: rx ry rz (euler angles in degrees, XYZ rotation order)
497  // Scale: sx sy sz
498  fpreal *ty = sopAddTrack(chans, *rig, SOP_PARENT_RIG_INDEX, "ty");
499  fpreal *sy = sopAddTrack(chans, *rig, SOP_PARENT_RIG_INDEX, "sy");
500  for (int i = 0; i < num_samples; ++i)
501  {
502  ty[i] = height * SYSsin(i * M_PI / (num_samples-1));
503  // add some squash and stretch
504  sy[i] = 1.0 - SYSabs(0.5 * SYSsin(i * M_PI / (num_samples-1)));
505  }
506 
507  // Set the ty of the 'child' transform. Note that these transforms here are
508  // in local space.
509  fpreal *tz = sopAddTrack(chans, *rig, SOP_CHILD_RIG_INDEX, "tz");
510  ty = sopAddTrack(chans, *rig, SOP_CHILD_RIG_INDEX, "ty");
511  for (int i = 0; i < num_samples; ++i)
512  {
513  ty[i] = 2.0; // sphere diameter
514  tz[i] = 1.5 * SYSsin(i * M_PI / (num_samples-1)); // sway a bit forwards
515  }
516 
517  // Set up channels to drive the blendshapes.
518  if (enable_blendshapes)
519  {
520  fpreal *chan1 = chans.addTrack(theChannel1Name.asHolder())->getData();
521  fpreal *chan2 = chans.addTrack(theChannel2Name.asHolder())->getData();
522 
523  for (int i = 0; i < num_samples; ++i)
524  {
525  chan1[i] = 0.5 + 0.5 * SYScos(i * 2 * M_PI / num_samples + M_PI);
526  chan2[i] = 0.5 + 0.5 * SYScos(i * 2 * M_PI / num_samples);
527  }
528  }
529 
530  // Finally load the agent clip from the CL_Clip animation we created
531  GU_AgentClipPtr clip = GU_AgentClip::addClip("bounce", rig);
532  if (!clip)
533  return nullptr;
534  clip->load(chans);
535 
536  return clip;
537 }
538 
539 static constexpr UT_StringLit theCustomDataItemType("bouncyagentdata");
540 static constexpr UT_StringLit theValueToken("myvalue");
541 static constexpr UT_StringLit theNameToken("myname");
542 
543 /// Example implementation of a custom data item that can be added to a
544 /// GU_AgentDefinition.
546 {
547 public:
548  GU_BouncyAgentCustomData() : myValue(0) {}
549 
551  : GU_AgentCustomDataItem(), myName(name), myValue(value)
552  {
553  }
554 
556  {
557  return UTmakeIntrusive<GU_BouncyAgentCustomData>();
558  }
559 
560  const UT_StringHolder &dataItemType() const override
561  {
562  return theCustomDataItemType.asHolder();
563  }
564 
565  int64 getMemoryUsage(bool inclusive) const override
566  {
567  int64 mem = inclusive ? sizeof(*this) : 0;
568  mem += myName.getMemoryUsage(false);
569  return mem;
570  }
571 
572  const UT_StringHolder &name() const override { return myName; }
573  int value() const { return myValue; }
574 
575  /// @{
576  /// Implements serialization to and from JSON.
577  /// For debugging, save to an ASCII (.geo) geometry file.
578  bool load(UT_JSONParser &p) override
579  {
580  UT_StringHolder key;
581  for (auto it = p.beginMap(); !it.atEnd(); ++it)
582  {
583  if (!p.parseKey(key))
584  return false;
585 
586  if (key == theValueToken.asHolder())
587  {
588  if (!p.parseInt(myValue))
589  return false;
590  }
591  else if (key == theNameToken.asHolder())
592  {
593  if (!p.parseString(myName))
594  return false;
595  }
596  else
597  {
598  p.addWarning("Unknown key '%s'", key.buffer());
599  if (!p.skipNextObject())
600  return false;
601  }
602  }
603 
604  return true;
605  }
606 
607  bool save(UT_JSONWriter &w) const override
608  {
609  bool ok = true;
610  ok = ok && w.jsonBeginMap();
611 
612  ok = ok && w.jsonKey(theNameToken.asHolder());
613  ok = ok && w.jsonValue(myName);
614 
615  ok = ok && w.jsonKey(theValueToken.asHolder());
616  ok = ok && w.jsonValue(myValue);
617 
618  ok = ok && w.jsonEndMap();
619  return ok;
620  }
621  /// @}
622 
623 private:
624  UT_StringHolder myName;
625  int64 myValue;
626 };
627 
628 /// Entry point for registering GU_BouncyAgentCustomData.
629 void
631 {
633  theCustomDataItemType.asHolder(), GU_BouncyAgentCustomData::construct);
634 }
635 
636 // Enable this to debug our definition
637 #define SOP_SAVE_AGENT_DEFINITION 0
638 
639 // Create the agent definition
641 SOP_BouncyAgent::createDefinition(fpreal t) const
642 {
643  // Typically, the definition is loaded from disk which has a filename for
644  // each of the different parts. Since we're doing this procedurally, we're
645  // going to just make up some arbitrary unique names using our node path.
646  UT_String path;
647  getFullPath(path);
648 
649  const bool enable_blendshapes = BLENDSHAPES(t);
650 
651  GU_AgentRigPtr rig = sopCreateRig(path, enable_blendshapes);
652  if (!rig)
653  return nullptr;
654 
655  GU_AgentShapeLibPtr shapelib =
656  sopCreateShapeLib(path, *rig, enable_blendshapes);
657  if (!shapelib)
658  return nullptr;
659 
660  GU_AgentLayerPtr default_layer =
661  sopCreateDefaultLayer(path, rig, shapelib, enable_blendshapes);
662  if (!default_layer)
663  return nullptr;
664 
665  GU_AgentLayerPtr collision_layer = sopCreateCollisionLayer(path,
666  rig, shapelib);
667  if (!collision_layer)
668  return nullptr;
669 
670  CL_Clip chans(CHgetManager()->getSample(CLIPLENGTH(t)));
671  chans.setSampleRate(CHgetManager()->getSamplesPerSec());
672  GU_AgentClipPtr clip =
673  sopCreateBounceClip(chans, rig, HEIGHT(t), enable_blendshapes);
674  if (!clip)
675  return nullptr;
676 
677 #if SOP_SAVE_AGENT_DEFINITION
678  // Once we have the definition, we can save out the files for loading with
679  // the Agent SOP as well. Or, we can examine them for debugging purposes.
680  {
681  UT_AutoJSONWriter writer("bouncy_rig.rig", /*binary*/false);
682  rig->save(writer);
683  }
684  {
685  UT_AutoJSONWriter writer("bouncy_shapelib.bgeo", /*binary*/true);
686  shapelib->save(writer);
687  }
688  {
689  UT_AutoJSONWriter writer("bouncy_layer.default.lay", /*binary*/false);
690  default_layer->save(writer);
691  }
692  {
693  UT_AutoJSONWriter writer("bouncy_layer.collision.lay", /*binary*/false);
694  collision_layer->save(writer);
695  }
696  {
697  chans.save("bouncy_bounce.bclip");
698  }
699 #endif
700 
701  // The agent definition is used to create agent primitives. Many agent
702  // primitives can share the same definition.
703  auto def = UTmakeIntrusive<GU_AgentDefinition>(rig, shapelib);
704  // The definition has a number of layers that be can assigned
705  def->addLayer(default_layer);
706  def->addLayer(collision_layer);
707  // ... and a number of clips that can be assigned to specific agent prims
708  def->addClip(clip);
709 
710  // Add some custom data for demonstration purposes.
711  def->addCustomDataItem(
712  UTmakeIntrusive<GU_BouncyAgentCustomData>("mycustomdata", 42));
713 
714  // Optionally, sort the list of items by name.
715  def->sortItemsIfNeeded();
716 
717  return def;
718 }
719 
720 /*static*/ int
721 SOP_BouncyAgent::onReload(
722  void *data, int index, fpreal t, const PRM_Template *tplate)
723 {
724  SOP_BouncyAgent* sop = static_cast<SOP_BouncyAgent*>(data);
725  if (!sop->getHardLock()) // only allow reloading if we're not locked
726  {
727  sop->myDefinition.reset();
728  sop->forceRecook();
729  }
730  return 1;
731 }
732 
733 // Compute the output geometry for the SOP.
734 OP_ERROR
736 {
737  fpreal t = context.getTime();
738 
739  // We must lock our inputs before we try to access their geometry.
740  // OP_AutoLockInputs will automatically unlock our inputs when we return.
741  // NOTE: Don't call unlockInputs yourself when using this!
742  OP_AutoLockInputs inputs(this);
743  if (inputs.lock(context) >= UT_ERROR_ABORT)
744  return error();
745 
746  // Duplicate the input geometry, but only if it was changed
747  int input_changed;
748  duplicateChangedSource(/*input*/0, context, &input_changed);
749 
750  // Detect if we need to rebuild the agent definition. For simplicity, we'll
751  // rebuild if any of our agent parameters changed. Note the use of the
752  // bitwise or operator to ensure that isParmDirty() is always called for
753  // all parameters.
754  bool agent_changed = isParmDirty(sopAgentName.getToken(), t);
755  agent_changed = isParmDirty(sopHeight.getToken(), t);
756  agent_changed |= isParmDirty(sopClipLength.getToken(), t);
757  agent_changed |= isParmDirty(sopEnableBlendshapes.getToken(), t);
758 
759  if (!myDefinition)
760  {
761  agent_changed = true;
762  input_changed = true;
763  }
764 
765  if (agent_changed)
766  {
767  myDefinition = createDefinition(t);
768  if (!myDefinition)
769  {
770  addError(SOP_MESSAGE, "Failed to create definition");
771  return error();
772  }
773  }
774 
775  if (input_changed)
776  {
777  // Delete all the primitives, keeping only the points
779 
780  // Create the agent primitives
781  myPrims.clear();
782  for (GA_Offset ptoff : gdp->getPointRange())
783  {
784  myPrims.append(GU_Agent::agent(*gdp, ptoff));
785  }
786 
787  // Bumping these 2 attribute owners is what we need to do when adding
788  // pack agent prims because it has a single vertex.
791  gdp->getPrimitiveList().bumpDataId(); // modified primitives
792  }
793 
794  if (agent_changed || input_changed)
795  {
796  // Create a name attribute for the agents
798  "name", 1));
799 
800  UT_Array<GU_AgentLayerConstPtr> current_layers;
801  current_layers.append(
802  myDefinition->layer(UTmakeUnsafeRef(GU_AGENT_LAYER_DEFAULT)));
803  UT_Array<GU_AgentLayerConstPtr> collision_layers;
804  collision_layers.append(
805  myDefinition->layer(UTmakeUnsafeRef(GU_AGENT_LAYER_COLLISION)));
806 
807  // Set the agent definition to the agent prims
809  UT_String agent_name;
810  AGENTNAME(agent_name, t);
811  int name_i = 0;
812  for (GU_PrimPacked *pack : myPrims)
813  {
814  GU_Agent* agent = UTverify_cast<GU_Agent*>(pack->hardenImplementation());
815  agent->setDefinition(pack, myDefinition);
816 
817  agent->setCurrentLayers(pack, current_layers);
818  agent->setCollisionLayers(pack, collision_layers);
819 
820  // We only have 1 clip that can be used in the definition here.
821  UT_StringArray clips;
822  clips.append(myDefinition->clip(0).name());
823  agent->setClipsByNames(pack, clips);
824 
825  // Convention for the agent primitive names is agentname_0,
826  // agentname_1, agentname_2, etc.
827  name.sprintf("%s_%d", agent_name.buffer(), name_i);
828  name_attrib.set(pack->getMapOffset(), name.buffer());
829  ++name_i;
830  }
831 
832  // Mark what modified
833  gdp->getPrimitiveList().bumpDataId();
834  name_attrib.bumpDataId();
835  }
836 
837  // Set the clip information for the agents. In general, agents can be set
838  // to evaluate an blended array of clips to evaluated at a specific clip
839  // offset.
840  for (GU_PrimPacked *pack : myPrims)
841  {
842  GU_Agent* agent = UTverify_cast<GU_Agent*>(pack->hardenImplementation());
843  agent->setClipTime(pack, /*clip index*/0, CLIPOFFSET(t));
844  }
845  gdp->getPrimitiveList().bumpDataId(); // we modified primitives
846 
847  return error();
848 }
849 
850 // Provide input labels.
851 const char *
852 SOP_BouncyAgent::inputLabel(unsigned /*input_index*/) const
853 {
854  return "Points to attach agents";
855 }
CL_Track * addTrack(const UT_StringHolder &name)
bool jsonValue(bool value)
const GA_AIFIndexPair * getAIFIndexPair() const
bool parseString(UT_WorkBuffer &v)
PRM_API const PRM_Type PRM_CALLBACK
Generic Attribute Interface class to get/set data as index pairs.
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.
GLboolean * data
Definition: glcorearb.h:131
#define SOP_COLLISION_SKIN_NAME
fpreal getTime() const
Definition: OP_Context.h:62
#define M_PI
Definition: fmath.h:90
SIM_API const UT_StringHolder agent
static GU_AgentCustomDataItemPtr construct(const GU_AgentDefinition &)
GLsizei const GLchar *const * path
Definition: glcorearb.h:3341
void setClipsByNames(GU_PrimPacked *prim, const UT_StringArray &clip_names)
void setCollisionLayers(GU_PrimPacked *prim, const UT_Array< GU_AgentLayerConstPtr > &layers)
Sets the agent's collision layers.
const UT_StringHolder & transformName(exint i) const
Return the name of the given transform.
Definition: GU_AgentRig.h:104
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:1540
UT_ErrorSeverity
Definition: UT_Error.h:25
JSON reader class which handles parsing of JSON or bJSON files.
Definition: UT_JSONParser.h:87
#define SOP_DEFAULT_SKIN_NAME
bool save(UT_JSONWriter &w) const override
Class which writes ASCII or binary JSON streams.
Definition: UT_JSONWriter.h:37
SYS_FORCE_INLINE TO_T UTverify_cast(FROM_T from)
Definition: UT_Assert.h:229
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
Convenience class to store a bone capture region.
SYS_FORCE_INLINE UT_Vector3 getPos3(GA_Offset ptoff) const
The ptoff passed is the point offset.
Definition: GA_Detail.h:185
void GUregisterAgentCustomDataItemType()
Entry point for registering GU_BouncyAgentCustomData.
void addError(int code, const char *msg=0)
Definition: SOP_Node.h:1158
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:235
GU_BouncyAgentCustomData(const UT_StringHolder &name, int value)
GA_Size destroyPrimitives(const GA_Range &it, bool and_points=false)
Definition: GA_Detail.h:680
OP_ERROR cookMySop(OP_Context &context) override
Method to cook geometry for the SOP.
GLenum GLuint GLint GLint layer
Definition: glcorearb.h:1299
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:641
bool parseInt(int64 &v)
Alternate short-form.
const char * buffer() const
Definition: UT_String.h:509
GLint GLsizei GLsizei height
Definition: glcorearb.h:103
This class provides a way to manage a reference to an attribute permitting Read-Write access...
GLdouble n
Definition: glcorearb.h:2008
A rig for the agent primitive.
Definition: GU_AgentRig.h:38
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:1730
CH_Manager * CHgetManager()
Definition: CH_Manager.h:2079
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:330
void bumpDataId()
Use this to mark primitives or their intrinsic data as dirty.
bool getHardLock() const
Definition: OP_Node.h:1217
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.
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:796
int save(UT_OStream &os, const CL_ClipPrivateIO *priv=0, bool use_blosc_compresssion=false) const
void appendMultiple(const T &t, exint count)
Definition: UT_ArrayImpl.h:795
Wrapper around hboost::intrusive_ptr.
long long int64
Definition: SYS_Types.h:116
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
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:64
bool load(UT_JSONParser &p) override
#define GU_AGENT_LAYER_COLLISION
Definition: GU_Agent.h:34
GLuint const GLchar * name
Definition: glcorearb.h:786
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:635
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.
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:36
exint append()
Definition: UT_Array.h:142
PRM_API PRM_Range PRMrulerRange
GLdouble t
Definition: glad.h:2397
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.
UT_Vector3T< fpreal32 > UT_Vector3F
SYS_FORCE_INLINE const UT_StringRef & asRef() const
PRM_API PRM_Default PRMoneDefaults[]
static GU_AgentClipPtr addClip(const UT_StringHolder &name, const GU_AgentRigConstPtr &rig)
Create an empty clip.
void setSampleRate(fpreal rate)
Definition: CL_Clip.h:87
SOP_BouncyAgent(OP_Network *net, const char *name, OP_Operator *op)
SYS_FORCE_INLINE void setPos3(GA_Offset ptoff, const UT_Vector3 &pos)
Set P from a UT_Vector3.
Definition: GA_Detail.h:237
virtual bool setEntries(GA_Attribute *attrib, int n) const =0
void translate(T dx, T dy, T dz=0)
Definition: UT_Matrix4.h:773
void setDefinition(GU_PrimPacked *prim, const GU_AgentDefinitionConstPtr &definition)
SYS_FORCE_INLINE void set(GA_Offset off, const T &val) const
Definition: GA_Handle.h:354
const char * inputLabel(unsigned input_index) const override
Method to provide input labels.
fpreal64 fpreal
Definition: SYS_Types.h:277
#define GU_AGENT_LAYER_DEFAULT
Definition: GU_Agent.h:33
GLuint index
Definition: glcorearb.h:786
void setClipTime(GU_PrimPacked *prim, exint i, fpreal seconds)
SYS_FORCE_INLINE const UT_StringHolder & asHolder() const
static PRM_Template myTemplateList[]
int getTrackLength() const
Definition: CL_Clip.h:82
bool jsonEndMap()
End the object.
const GA_PrimitiveList & getPrimitiveList() const
Definition: GA_Detail.h:792
static GU_AgentRigPtr addRig(const UT_StringHolder &name)
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
virtual void setObjectCount(int nobj)=0
Definition: core.h:1131
void bumpAllDataIds(GA_AttributeOwner owner)
Bumps all data IDs of attributes of the specified owner.
GLboolean r
Definition: glcorearb.h:1222
void clear()
Resets list to an empty list.
Definition: UT_Array.h:716
iterator beginMap()
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1733
static void registerCustomDataItemType(const UT_StringHolder &dataitemtype, CustomDataItemConstructor constructor)
Register a new custom data item type.
PRM_API PRM_Default PRMzeroDefaults[]
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)
IMATH_INTERNAL_NAMESPACE_HEADER_ENTER IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T clip(const T &p, const Box< T > &box) IMATH_NOEXCEPT
Definition: ImathBoxAlgo.h:29
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:345
const UT_StringHolder & dataItemType() const override
void GU_API addInputsToBaseShape(GU_Detail &base_shape, const UT_StringArray &shape_names, const UT_StringArray &channel_names)
Definition: format.h:895
void setCurrentLayers(GU_PrimPacked *prim, const UT_Array< GU_AgentLayerConstPtr > &layers)
Sets the agent's display layers.
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)
const char * getToken() const
Definition: PRM_Name.h:79
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:334
GLenum src
Definition: glcorearb.h:1793
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)