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) 2022
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_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 new 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  GU_AgentDefinitionPtr def(new 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(new GU_BouncyAgentCustomData("mycustomdata", 42));
712 
713  return def;
714 }
715 
716 /*static*/ int
717 SOP_BouncyAgent::onReload(
718  void *data, int index, fpreal t, const PRM_Template *tplate)
719 {
720  SOP_BouncyAgent* sop = static_cast<SOP_BouncyAgent*>(data);
721  if (!sop->getHardLock()) // only allow reloading if we're not locked
722  {
723  sop->myDefinition.reset();
724  sop->forceRecook();
725  }
726  return 1;
727 }
728 
729 // Compute the output geometry for the SOP.
730 OP_ERROR
732 {
733  fpreal t = context.getTime();
734 
735  // We must lock our inputs before we try to access their geometry.
736  // OP_AutoLockInputs will automatically unlock our inputs when we return.
737  // NOTE: Don't call unlockInputs yourself when using this!
738  OP_AutoLockInputs inputs(this);
739  if (inputs.lock(context) >= UT_ERROR_ABORT)
740  return error();
741 
742  // Duplicate the input geometry, but only if it was changed
743  int input_changed;
744  duplicateChangedSource(/*input*/0, context, &input_changed);
745 
746  // Detect if we need to rebuild the agent definition. For simplicity, we'll
747  // rebuild if any of our agent parameters changed. Note the use of the
748  // bitwise or operator to ensure that isParmDirty() is always called for
749  // all parameters.
750  bool agent_changed = isParmDirty(sopAgentName.getToken(), t);
751  agent_changed = isParmDirty(sopHeight.getToken(), t);
752  agent_changed |= isParmDirty(sopClipLength.getToken(), t);
753  agent_changed |= isParmDirty(sopEnableBlendshapes.getToken(), t);
754 
755  if (!myDefinition)
756  {
757  agent_changed = true;
758  input_changed = true;
759  }
760 
761  if (agent_changed)
762  {
763  myDefinition = createDefinition(t);
764  if (!myDefinition)
765  {
766  addError(SOP_MESSAGE, "Failed to create definition");
767  return error();
768  }
769  }
770 
771  if (input_changed)
772  {
773  // Delete all the primitives, keeping only the points
775 
776  // Create the agent primitives
777  myPrims.clear();
778  for (GA_Offset ptoff : gdp->getPointRange())
779  {
780  myPrims.append(GU_Agent::agent(*gdp, ptoff));
781  }
782 
783  // Bumping these 2 attribute owners is what we need to do when adding
784  // pack agent prims because it has a single vertex.
787  gdp->getPrimitiveList().bumpDataId(); // modified primitives
788  }
789 
790  if (agent_changed || input_changed)
791  {
792  // Create a name attribute for the agents
794  "name", 1));
795 
796  UT_Array<GU_AgentLayerConstPtr> current_layers;
797  current_layers.append(
798  myDefinition->layer(UTmakeUnsafeRef(GU_AGENT_LAYER_DEFAULT)));
799  UT_Array<GU_AgentLayerConstPtr> collision_layers;
800  collision_layers.append(
801  myDefinition->layer(UTmakeUnsafeRef(GU_AGENT_LAYER_COLLISION)));
802 
803  // Set the agent definition to the agent prims
805  UT_String agent_name;
806  AGENTNAME(agent_name, t);
807  int name_i = 0;
808  for (GU_PrimPacked *pack : myPrims)
809  {
810  GU_Agent* agent = UTverify_cast<GU_Agent*>(pack->hardenImplementation());
811  agent->setDefinition(pack, myDefinition);
812 
813  agent->setCurrentLayers(pack, current_layers);
814  agent->setCollisionLayers(pack, collision_layers);
815 
816  // We only have 1 clip that can be used in the definition here.
817  UT_StringArray clips;
818  clips.append(myDefinition->clip(0).name());
819  agent->setClipsByNames(pack, clips);
820 
821  // Convention for the agent primitive names is agentname_0,
822  // agentname_1, agentname_2, etc.
823  name.sprintf("%s_%d", agent_name.buffer(), name_i);
824  name_attrib.set(pack->getMapOffset(), name.buffer());
825  ++name_i;
826  }
827 
828  // Mark what modified
829  gdp->getPrimitiveList().bumpDataId();
830  name_attrib.bumpDataId();
831  }
832 
833  // Set the clip information for the agents. In general, agents can be set
834  // to evaluate an blended array of clips to evaluated at a specific clip
835  // offset.
836  for (GU_PrimPacked *pack : myPrims)
837  {
838  GU_Agent* agent = UTverify_cast<GU_Agent*>(pack->hardenImplementation());
839  agent->setClipTime(pack, /*clip index*/0, CLIPOFFSET(t));
840  }
841  gdp->getPrimitiveList().bumpDataId(); // we modified primitives
842 
843  return error();
844 }
845 
846 // Provide input labels.
847 const char *
848 SOP_BouncyAgent::inputLabel(unsigned /*input_index*/) const
849 {
850  return "Points to attach agents";
851 }
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
Generic Attribute Interface class to get/set data as index pairs.
virtual OP_ERROR error()
GLenum GLuint GLint GLint layer
Definition: glcorearb.h:1298
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.
#define SOP_COLLISION_SKIN_NAME
fpreal getTime() const
Definition: OP_Context.h:60
static GU_AgentCustomDataItemPtr construct(const GU_AgentDefinition &)
GLsizei const GLchar *const * path
Definition: glcorearb.h:3340
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:1537
UT_ErrorSeverity
Definition: UT_Error.h:25
JSON reader class which handles parsing of JSON or bJSON files.
Definition: UT_JSONParser.h:88
#define SOP_DEFAULT_SKIN_NAME
PRM_API PRM_Default PRMoneDefaults[]
bool save(UT_JSONWriter &w) const override
Class which writes ASCII or binary JSON streams.
Definition: UT_JSONWriter.h:35
SYS_FORCE_INLINE TO_T UTverify_cast(FROM_T from)
Definition: UT_Assert.h:244
void setDefinition(GU_PrimPacked *prim, const GU_AgentDefinitionPtr &definition)
GLuint const GLchar * name
Definition: glcorearb.h:785
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:174
GLenum src
Definition: glcorearb.h:1792
void GUregisterAgentCustomDataItemType()
Entry point for registering GU_BouncyAgentCustomData.
GLdouble GLdouble t
Definition: glew.h:1403
void addError(int code, const char *msg=0)
Definition: SOP_Node.h:1149
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:234
#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
OP_ERROR cookMySop(OP_Context &context) override
Method to cook geometry for the SOP.
GLuint GLuint GLfloat weight
Definition: glew.h:13892
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:856
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:640
bool parseInt(int64 &v)
Alternate short-form.
const char * buffer() const
Definition: UT_String.h:499
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: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:1666
CH_Manager * CHgetManager()
Definition: CH_Manager.h:1758
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.
bool getHardLock() const
Definition: OP_Node.h:1222
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:739
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:203
Wrapper around hboost::intrusive_ptr.
long long int64
Definition: SYS_Types.h:116
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1641
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
GLfloat GLfloat p
Definition: glew.h:16656
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:58
bool load(UT_JSONParser &p) override
#define GU_AGENT_LAYER_COLLISION
Definition: GU_Agent.h:34
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:634
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
GLint GLsizei GLsizei height
Definition: glcorearb.h:102
exint append()
Definition: UT_Array.h:95
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.
GLdouble n
Definition: glcorearb.h:2007
UT_Vector3T< fpreal32 > UT_Vector3F
GLboolean * data
Definition: glcorearb.h:130
SYS_FORCE_INLINE const UT_StringRef & asRef() const
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
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:322
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:785
void setClipTime(GU_PrimPacked *prim, exint i, fpreal seconds)
GLbyte * weights
Definition: glew.h:7581
SYS_FORCE_INLINE const UT_StringHolder & asHolder() const
PRM_API PRM_Range PRMrulerRange
static PRM_Template myTemplateList[]
int getTrackLength() const
Definition: CL_Clip.h:82
GLsizei const GLfloat * value
Definition: glcorearb.h:823
bool jsonEndMap()
End the object.
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:549
iterator beginMap()
GLboolean r
Definition: glcorearb.h:1221
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1669
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
const UT_StringHolder & dataItemType() const override
void translate(T dx, T dy, T dz=0.0f)
Definition: UT_Matrix4.h:747
void GU_API addInputsToBaseShape(GU_Detail &base_shape, const UT_StringArray &shape_names, const UT_StringArray &channel_names)
GLenum GLsizei GLenum GLenum const void * table
Definition: glew.h:4970
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)
GLenum const void * paths
Definition: glew.h:13872
const char * getToken() const
Definition: PRM_Name.h:83
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)