HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_CopyPacked.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021
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  * This SOP copies packed primitives from the first input onto the specified
27  * points in the second input.
28  */
29 
30 // A .proto.h file is an automatically generated header file based on theDsFile,
31 // below, to provide SOP_CopyPackedParms, an easy way to access parameter
32 // values from SOP_CopyPackedVerb::cook with the correct type, and
33 // SOP_CopyPackedEnums, a namespace containing enum types for any ordinal
34 // menu parameters.
35 #include "SOP_CopyPacked.proto.h"
36 
37 #include <SOP/SOP_Node.h>
38 #include <SOP/SOP_NodeVerb.h>
39 #include <GU/GU_Detail.h>
40 #include <GU/GU_PrimPacked.h>
41 #include <GOP/GOP_Manager.h>
42 #include <GA/GA_ATITopology.h>
43 #include <GA/GA_AttributeRefMap.h>
44 #include <GA/GA_ElementGroup.h>
45 #include <GA/GA_Handle.h>
46 #include <GA/GA_Names.h>
47 #include <GA/GA_SplittableRange.h>
48 #include <GA/GA_Types.h>
49 #include <OP/OP_Operator.h>
50 #include <OP/OP_OperatorTable.h>
52 #include <UT/UT_Assert.h>
53 #include <UT/UT_DSOVersion.h>
54 #include <UT/UT_Interrupt.h>
55 #include <UT/UT_Lock.h>
56 #include <UT/UT_ParallelUtil.h>
57 #include <UT/UT_StringHolder.h>
58 #include <UT/UT_StringMap.h>
59 #include <UT/UT_UniquePtr.h>
60 #include <SYS/SYS_MemoryOrder.h>
61 
62 namespace HDK_Sample {
63 
64 //******************************************************************************
65 //* Setup *
66 //******************************************************************************
67 
69 {
70 public:
72  {}
73 };
74 
75 
77 {
78 public:
79  virtual SOP_NodeParms *allocParms() const { return new SOP_CopyPackedParms(); }
80  virtual SOP_NodeCache *allocCache() const { return new SOP_CopyPackedCache(); }
81  virtual UT_StringHolder name() const { return theSOPTypeName; }
82 
83  virtual CookMode cookMode(const SOP_NodeParms *parms) const { return COOK_GENERIC; }
84 
85  virtual void cook(const CookParms &cookparms) const;
86 
87  /// This is the internal name of the SOP type.
88  /// It isn't allowed to be the same as any other SOP's type name.
90 
91  /// This static data member automatically registers
92  /// this verb class at library load time.
94 
95  /// This is the parameter interface string, below.
96  static const char *const theDsFile;
97 };
98 
99 // The static member variable definitions have to be outside the class definition.
100 // The declarations are inside the class.
101 const UT_StringHolder SOP_CopyPackedVerb::theSOPTypeName("hdk_copypacked"_sh);
103 
104 /// This is the SOP class definition.
105 class SOP_CopyPacked : public SOP_Node
106 {
107 public:
108  static inline PRM_Template *buildTemplates(const UT_StringHolder &filename, const char *ds_file)
109  {
110  static PRM_TemplateBuilder templ(filename, ds_file);
111  if (templ.justBuilt())
112  {
113  templ.setChoiceListPtr("pointgroup", &SOP_Node::pointGroupMenu);
114  }
115  return templ.templates();
116  }
117  static OP_Node *myConstructor(OP_Network *net, const char *name, OP_Operator *op)
118  {
119  return new SOP_CopyPacked(net, name, op);
120  }
121 
122 protected:
123  virtual const SOP_NodeVerb *cookVerb() const override
124  {
125  return SOP_CopyPackedVerb::theVerb.get();
126  }
127 
128  SOP_CopyPacked(OP_Network *net, const char *name, OP_Operator *op)
129  : SOP_Node(net, name, op)
130  {
131  // All verb SOPs must manage data IDs, to track what's changed
132  // from cook to cook.
134  }
135 
136  virtual ~SOP_CopyPacked() {}
137 
138  /// Since this SOP implements a verb, cookMySop just delegates to the verb.
140  {
141  return cookMyselfAsVerb(context);
142  }
143 
144  /// These are the labels that appear when hovering over the inputs.
145  virtual const char *inputLabel(unsigned idx) const override
146  {
147  switch (idx)
148  {
149  case 0: return "Packed Primitives";
150  case 1: return "Points";
151  default: return "Invalid Source";
152  }
153  }
154 
155  /// This just indicates whether an input wire gets drawn with a dotted line
156  /// in the network editor. If something is usually copied directly
157  /// into the output, a solid line (false) is used, but this SOP very often
158  /// doesn't do that for either input.
159  virtual int isRefInput(unsigned i) const override
160  {
161  // First or second input both use dotted lines
162  return (i == 0 || i == 1);
163  }
164 };
165 } // End HDK_Sample namespace
166 
167 /// newSopOperator is the hook that Houdini grabs from this dll
168 /// and invokes to register the SOP. In this case, we add ourselves
169 /// to the specified operator table.
170 void
172 {
173  using namespace HDK_Sample;
174  table->addOperator(new OP_Operator(
175  SOP_CopyPackedVerb::theSOPTypeName, // Internal name
176  "Copy Packed", // UI name
177  SOP_CopyPacked::myConstructor, // How to build the SOP
178  SOP_CopyPacked::buildTemplates( // My parameters
179  "SOP_CopyPacked.C"_sh,
181  2, 2, // Min, Max # of inputs
182  nullptr, // Custom local variables (none)
183  0)); // No special flags (OP_FLAG_UNORDERED to have a multi-input like Merge SOP)
184 }
185 
186 namespace HDK_Sample {
187 
188 //******************************************************************************
189 //* Parameters *
190 //******************************************************************************
191 
192 /// This is a multi-line raw string specifying the parameter interface for this SOP.
193 const char *const SOP_CopyPackedVerb::theDsFile = R"THEDSFILE(
194 {
195  name parameters
196  parm {
197  name "pointgroup"
198  cppname "PointGroup"
199  label "Point Group"
200  type string
201  default { "" }
202  parmtag { "script_action" "import soputils\nkwargs['geometrytype'] = (hou.geometryType.Points,)\nkwargs['inputindex'] = 1\nsoputils.selectGroupParm(kwargs)" }
203  parmtag { "script_action_help" "Select geometry from an available viewport.\nShift-click to turn on Select Groups." }
204  parmtag { "script_action_icon" "BUTTONS_reselect" }
205  parmtag { "sop_input" "1" }
206  }
207 }
208 )THEDSFILE";
209 
210 //******************************************************************************
211 //* Cooking *
212 //******************************************************************************
213 
214 template<typename FUNCTOR>
215 static void
216 copyPrimitiveData(
217  GEO_Detail *output_geo,
218  const GEO_Detail *packed_prims_input,
219  GA_PrimitiveGroupUPtr &bad_prim_group_deleter,
220  const GA_AttributeRefMap &primitive_attribs,
221  const GA_AttributeRefMap &vertex_attribs,
222  FUNCTOR &&pt_to_prim_functor)
223 {
224  GA_PrimitiveGroup *volatile bad_prim_group = nullptr;
225  UT_Lock bad_prim_group_lock;
226 
227  // NOTE: We're using a GA_SplittableRange over the destination primitives so that we can
228  // safely write to unordered primitive groups in parallel without locking around the writes.
230  [output_geo, packed_prims_input,
231  &bad_prim_group, &bad_prim_group_deleter, &bad_prim_group_lock,
232  &primitive_attribs, &vertex_attribs, &pt_to_prim_functor](const GA_SplittableRange &r) {
234  GA_Offset end;
235  for (GA_Iterator it(r); it.blockAdvance(start, end); ) {
236  GA_Index i = output_geo->primitiveIndex(start);
237  for (GA_Offset dest_primoff = start; dest_primoff < end; ++dest_primoff, ++i) {
238  GA_Primitive *dest_prim = output_geo->getPrimitive(dest_primoff);
239  // There can only be packed primitives here, so this should be safe.
240  GEO_PrimPacked *dest_packed_prim = UTverify_cast<GEO_PrimPacked *>(dest_prim);
241  GA_Offset dest_ptoff = output_geo->pointOffset(i);
242  GA_Offset source_primoff = pt_to_prim_functor(dest_ptoff);
243  if (GAisValid(source_primoff)) {
244  const GA_Primitive *source_prim = packed_prims_input->getPrimitive(source_primoff);
245  const GEO_PrimPacked *source_packed_prim = UTverify_cast<const GEO_PrimPacked *>(source_prim);
246  dest_packed_prim->copyMemberDataFrom(*source_packed_prim);
247 
248  if (primitive_attribs.entries()) {
249  primitive_attribs.copyValue(GA_ATTRIB_PRIMITIVE, dest_primoff, GA_ATTRIB_PRIMITIVE, source_primoff);
250  }
251  if (vertex_attribs.entries()) {
252  vertex_attribs.copyValue(GA_ATTRIB_VERTEX, dest_prim->getVertexOffset(0), GA_ATTRIB_VERTEX, source_prim->getVertexOffset(0));
253  }
254  }
255  else {
256  if (!bad_prim_group) {
257  UT_Lock::Scope lock(bad_prim_group_lock);
258  if (!bad_prim_group) {
259  // Making a detached primitive group (owned by us, not output_geo).
260  bad_prim_group_deleter = UTmakeUnique<GA_PrimitiveGroup>(*output_geo);
261  SYSstoreFence();
262  bad_prim_group = bad_prim_group_deleter.get();
263  }
264  }
265  bad_prim_group->addOffset(dest_primoff);
266  }
267  }
268  }
269  });
270 }
271 
272 /// This is the function that does the actual work.
273 void SOP_CopyPackedVerb::cook(const CookParms &cookparms) const
274 {
275  // This gives easy access to all of the current parameter values
276  auto &&sopparms = cookparms.parms<SOP_CopyPackedParms>();
277  auto sopcache = (SOP_CopyPackedCache *)cookparms.cache();
278 
279  // The output detail
280  GEO_Detail *output_geo = cookparms.gdh().gdpNC();
281 
282  const GEO_Detail *const packed_prims_input = cookparms.inputGeo(0);
283  const GEO_Detail *const points_input = cookparms.inputGeo(1);
284 
285  // GOP_Manager will own any temporary groups it creates
286  // and automatically destroy them when it goes out of scope.
287  GOP_Manager group_manager;
288  const GA_PointGroup *input_point_group = nullptr;
289  if (sopparms.getPointGroup().isstring()) {
290  // Parse point group on points_input detail.
291  bool success;
292  input_point_group = group_manager.parsePointDetached(
293  sopparms.getPointGroup().c_str(),
294  points_input, true, success);
295  }
296  if (input_point_group) {
297  output_geo->mergePoints(*points_input, input_point_group, true, false);
298  }
299  else {
300  output_geo->replaceWithPoints(*points_input);
301  }
302 
303  if (packed_prims_input->getNumPrimitives() == 0) {
304  return;
305  }
306 
307  GA_PrimitiveTypeId primitive_type_id(packed_prims_input->getPrimitiveTypeId(packed_prims_input->primitiveOffset(GA_Index(0))));
308  if (!GU_PrimPacked::isPackedPrimitive(primitive_type_id)) {
309  cookparms.sopAddError(SOP_MESSAGE, "All input primitives for copying must be packed primitives, at the moment.");
310  return;
311  }
312  if (packed_prims_input->countPrimitiveType(primitive_type_id) != packed_prims_input->getNumPrimitives()) {
313  cookparms.sopAddError(SOP_MESSAGE, "All input packed primitives must be the same primitive type, at the moment.");
314  return;
315  }
316 
317  GA_Size npoints = output_geo->getNumPoints();
318  GA_Offset start_primoff = output_geo->appendPrimitiveBlock(primitive_type_id, npoints);
319  GA_Offset start_vtxoff = output_geo->appendVertexBlock(npoints);
320 
321  output_geo->bumpDataIdsForAddOrRemove(false, true, true);
322 
323  // Initialize the vertex lists in parallel
324  UTparallelForLightItems(UT_BlockedRange<exint>(0, npoints), [output_geo, start_vtxoff, start_primoff](const UT_BlockedRange<exint> &r) {
325  for (exint i = r.begin(), n = r.end(); i < n; ++i) {
326  GA_Primitive *prim = output_geo->getPrimitive(start_primoff+i);
327  // There can only be packed primitives here, so this should be safe.
328  GEO_PrimPacked *packed_prim = UTverify_cast<GEO_PrimPacked *>(prim);
329  packed_prim->assignVertex(start_vtxoff+i, false);
330  }
331  });
332 
333  // Initialize the vertex-to-primitive topology attribute in parallel
334  GA_ATITopology *vtx_to_prim = output_geo->getTopology().getPrimitiveRef();
335  vtx_to_prim->hardenAllPages();
336  UTparallelForLightItems(UT_BlockedRange<exint>(0, npoints), [vtx_to_prim, start_vtxoff, start_primoff](const UT_BlockedRange<exint> &r) {
337  for (exint i = r.begin(), n = r.end(); i < n; ++i) {
338  vtx_to_prim->setLink(start_vtxoff+i, start_primoff+i);
339  }
340  });
341 
342  // Initialize the vertex-to-point and point-to-vertex topology attributes in parallel.
343  // NOTE: vertex-to-next-vertex and vertex-to-prev-vertex topology attributes
344  // don't need to be updated in this case, since their default GA_INVALID_OFFSET (-1)
345  // is what we want.
346  GA_ATITopology *vtx_to_pt = output_geo->getTopology().getPointRef();
347  vtx_to_pt->hardenAllPages();
348  GA_ATITopology *pt_to_vtx = output_geo->getTopology().getVertexRef();
349  const GA_IndexMap &pt_map = output_geo->getPointMap();
350  if (pt_map.isTrivialMap()) {
351  UTparallelForLightItems(UT_BlockedRange<exint>(0, npoints), [vtx_to_pt, start_vtxoff](const UT_BlockedRange<exint> &r) {
352  for (exint i = r.begin(), n = r.end(); i < n; ++i) {
353  vtx_to_pt->setLink(start_vtxoff+i, GA_Offset(i));
354  }
355  });
356  pt_to_vtx->hardenAllPages();
357  UTparallelForLightItems(UT_BlockedRange<exint>(0, npoints), [pt_to_vtx, start_vtxoff](const UT_BlockedRange<exint> &r) {
358  for (exint i = r.begin(), n = r.end(); i < n; ++i) {
359  pt_to_vtx->setLink(GA_Offset(i), start_vtxoff+i);
360  }
361  });
362  }
363  else {
364  UTparallelForLightItems(UT_BlockedRange<exint>(0, npoints), [vtx_to_pt, start_vtxoff, &pt_map](const UT_BlockedRange<exint> &r) {
365  for (exint i = r.begin(), n = r.end(); i < n; ++i) {
366  vtx_to_pt->setLink(start_vtxoff+i, pt_map.offsetFromIndex(GA_Index(i)));
367  }
368  });
369  pt_to_vtx->hardenAllPages();
370  UTparallelForLightItems(GA_SplittableRange(output_geo->getPointRange()), [output_geo, pt_to_vtx, start_vtxoff](const GA_SplittableRange &r) {
372  GA_Offset end;
373  for (GA_Iterator it(r); it.blockAdvance(start, end); ) {
374  exint i = (exint)output_geo->pointIndex(start);
375  for (GA_Offset ptoff = start; ptoff < end; ++ptoff, ++i) {
376  pt_to_vtx->setLink(ptoff, start_vtxoff+i);
377  }
378  }
379  });
380  }
381 
382  GA_AttributeRefMap primitive_attribs(*output_geo, packed_prims_input);
384 
385  GA_AttributeRefMap vertex_attribs(*output_geo, packed_prims_input);
387 
388  // Below, we're relying on that the vertex attribute pages and primitive attribute pages
389  // line up, in case there are vertex groups we're copying, since it's not safe
390  // for multiple threads to write to the same page of the same group at the same time,
391  // even if we were hardening the pages. Luckily, the vertex and primitive attribute pages
392  // should line up in this case, since there's exactly one vertex per primitive here and
393  // they should both start at GA_Offset(0) and the offsets of each are in a contiguous block.
394  UT_ASSERT_MSG(start_vtxoff == GA_Offset(0) && start_primoff == GA_Offset(0),
395  "start_vtxoff and start_primoff should both be zero, since we appended a block to an empty detail.");
396 
397  // This will automatically delete the detached primitive group
398  // (if one is created) when this goes out of scope.
399  GA_PrimitiveGroupUPtr bad_prim_group(nullptr);
400 
401  // It might seem like overkill to read the id attribute as int64, since
402  // primitive numbers usually don't exceed 2 billion, but if the IDs are
403  // incremented over the course of a simulation, or anything like that,
404  // they could conceivably need integers greater than 2 billion.
405  // Most of the time, the attribute will be int32, and its values will be
406  // automatically converted to int64 upon reading, which is fine.
407  GA_ROHandleID pt_id_attrib(output_geo->findPointAttribute(GA_Names::id));
408  if (pt_id_attrib.isValid()) {
409  GA_ROHandleID prim_id_attrib(packed_prims_input->findPrimitiveAttribute(GA_Names::id));
410  GA_AttributeOwner prim_id_owner = GA_ATTRIB_PRIMITIVE;
411  if (!prim_id_attrib.isValid()) {
412  prim_id_attrib.bind(packed_prims_input->findVertexAttribute(GA_Names::id));
413  prim_id_owner = GA_ATTRIB_VERTEX;
414  if (!prim_id_attrib.isValid()) {
415  prim_id_attrib.bind(packed_prims_input->findPointAttribute(GA_Names::id));
416  prim_id_owner = GA_ATTRIB_POINT;
417  }
418  }
419 
420  if (prim_id_attrib.isValid()) {
421  // Record the mapping from ID to packed primitive offset
422  UT::ArrayMap<exint,GA_Offset> id_to_primoff;
424  GA_Offset end;
425  for (GA_Iterator it(packed_prims_input->getPrimitiveRange()); it.blockAdvance(start, end); ) {
426  for (GA_Offset primoff = start; primoff < end; ++primoff) {
427  GA_Offset attriboff = primoff;
428  if (prim_id_owner != GA_ATTRIB_PRIMITIVE) {
429  // NOTE: Packed primitives in the input should always have one vertex,
430  // so this should be safe.
431  attriboff = packed_prims_input->getPrimitiveVertexOffset(primoff, 0);
432  if (prim_id_owner == GA_ATTRIB_POINT) {
433  attriboff = packed_prims_input->vertexPoint(attriboff);
434  }
435  }
436  exint id = prim_id_attrib.get(attriboff);
438  // UT::ArrayMap<exint,...> doesn't support
439  // 0x8000000000000000LL as a key. Hopefully that's okay.
440  continue;
441  }
442 
443  // NOTE: This will not overwrite if another primitive with the same ID has already been inserted,
444  // so it only keeps the first match.
445  id_to_primoff.insert(id, primoff);
446  }
447  }
448 
449  copyPrimitiveData(output_geo, packed_prims_input, bad_prim_group, primitive_attribs, vertex_attribs,
450  [&pt_id_attrib, &id_to_primoff](GA_Offset dest_ptoff) -> GA_Offset {
451  const exint id = pt_id_attrib.get(dest_ptoff);
453  // UT::ArrayMap<exint,...> doesn't support
454  // 0x8000000000000000LL as a key. Hopefully that's okay.
455  return GA_INVALID_OFFSET;
456  }
457  auto &&it = id_to_primoff.find(id);
458  if (it != id_to_primoff.end()) {
459  return it->second;
460  }
461  return GA_INVALID_OFFSET;
462  });
463  }
464  else {
465  // There's no primitive id attribute, so id represents the primitive number (index).
466  copyPrimitiveData(output_geo, packed_prims_input, bad_prim_group, primitive_attribs, vertex_attribs,
467  [&pt_id_attrib, packed_prims_input](GA_Offset dest_ptoff) -> GA_Offset {
468  const GA_Index source_primind(pt_id_attrib.get(dest_ptoff));
469  if (GAisValid(source_primind) && source_primind < packed_prims_input->getNumPrimitives()) {
470  // Primitive number is in range
471  return packed_prims_input->primitiveOffset(source_primind);
472  }
473  return GA_INVALID_OFFSET;
474  });
475  }
476  }
477  else {
478  GA_ROHandleS pt_name_attrib(output_geo->findPointAttribute(GA_Names::name));
479  GA_ROHandleS prim_name_attrib(output_geo->findPrimitiveAttribute(GA_Names::name));
480  GA_AttributeOwner prim_name_owner = GA_ATTRIB_PRIMITIVE;
481  if (!prim_name_attrib.isValid()) {
482  prim_name_attrib.bind(packed_prims_input->findVertexAttribute(GA_Names::name));
483  prim_name_owner = GA_ATTRIB_VERTEX;
484  if (!prim_name_attrib.isValid()) {
485  prim_name_attrib.bind(packed_prims_input->findPointAttribute(GA_Names::name));
486  prim_name_owner = GA_ATTRIB_POINT;
487  }
488  }
489  if (pt_name_attrib.isValid() && prim_name_attrib.isValid()) {
490  // Record the mapping from name to packed primitive offset
491  UT_ArrayStringMap<GA_Offset> name_to_primoff;
493  GA_Offset end;
494  for (GA_Iterator it(packed_prims_input->getPrimitiveRange()); it.blockAdvance(start, end); ) {
495  for (GA_Offset primoff = start; primoff < end; ++primoff) {
496  GA_Offset attriboff = primoff;
497  if (prim_name_owner != GA_ATTRIB_PRIMITIVE) {
498  // NOTE: Packed primitives in the input should always have one vertex,
499  // so this should be safe.
500  attriboff = packed_prims_input->getPrimitiveVertexOffset(primoff, 0);
501  if (prim_name_owner == GA_ATTRIB_POINT) {
502  attriboff = packed_prims_input->vertexPoint(attriboff);
503  }
504  }
505  const UT_StringHolder &name = prim_name_attrib.get(attriboff);
506  if (!name.isstring()) {
507  // UT_ArrayStringMap doesn't support "" as a key in
508  // Houdini 16.5 and earlier, and if
509  // "" appears in the matching attribute, the user is
510  // probably indicating that they don't want to copy
511  // anything there, so it should be okay.
512  continue;
513  }
514 
515  // NOTE: This will not overwrite if another primitive with the same name has already been inserted,
516  // so it only keeps the first match.
517  name_to_primoff.insert(name, primoff);
518  }
519  }
520 
521  copyPrimitiveData(output_geo, packed_prims_input, bad_prim_group, primitive_attribs, vertex_attribs,
522  [&pt_name_attrib, &name_to_primoff](GA_Offset dest_ptoff) -> GA_Offset {
523  const UT_StringHolder &name = pt_name_attrib.get(dest_ptoff);
524  if (!name.isstring()) {
525  // UT_ArrayStringMap doesn't support "" in 16.5 and earlier,
526  // and it probably means that we're not trying to match
527  // anything, anyway.
528  return GA_INVALID_OFFSET;
529  }
530  auto &&it = name_to_primoff.find(name);
531  if (it != name_to_primoff.end()) {
532  return it->second;
533  }
534  return GA_INVALID_OFFSET;
535  });
536 
537  }
538  else {
539  output_geo->clearAndDestroy();
540  cookparms.sopAddError(SOP_MESSAGE, "Either the points must have an id attribute, or both the points and the packed primitives must have a name attribute.");
541  return;
542  }
543  }
544 
545  if (bad_prim_group) {
546  // If there were any points that didn't match any source primitives,
547  // delete the incompletely initialized primitives we've made.
548  output_geo->deletePrimitives(*bad_prim_group);
549  }
550 }
551 
552 } // End HDK_Sample namespace
This is the SOP class definition.
A class to manage an ordered array which has fixed offset handles.
Definition: GA_IndexMap.h:63
SYS_FORCE_INLINE GA_Offset getPrimitiveVertexOffset(GA_Offset primoff, GA_Size i) const
Definition: GA_Primitive.h:873
GT_API const UT_StringHolder filename
SYS_FORCE_INLINE GA_Primitive * getPrimitive(GA_Offset prim_off)
Definition: GA_Detail.h:380
void hardenAllPages(GA_Offset start_offset=GA_Offset(0), GA_Offset end_offset=GA_INVALID_OFFSET) override
Harden data pages.
GLuint const GLchar * name
Definition: glew.h:1814
Iteration over a range of elements.
Definition: GA_Iterator.h:28
void setChoiceListPtr(const UT_StringRef &name, PRM_ChoiceList *list)
SYS_FORCE_INLINE int getPrimitiveTypeId(GA_Offset primoff) const
Definition: GA_Primitive.h:880
SYS_FORCE_INLINE const GA_Attribute * findVertexAttribute(GA_AttributeScope s, const UT_StringRef &name) const
Definition: GA_Detail.h:984
std::pair< iterator, bool > insert(const UT_StringRef &key, const ITEM_T &val)
bool blockAdvance(GA_Offset &start, GA_Offset &end)
bool GAisValid(GA_Size v)
Definition: GA_Types.h:647
void clearAndDestroy()
Clear all the points/primitives out of this detail.
Definition: GEO_Detail.h:254
virtual UT_StringHolder name() const
GLenum GLsizei GLenum GLenum const void * table
Definition: glew.h:4940
int64 exint
Definition: SYS_Types.h:125
UT_ErrorSeverity
Definition: UT_Error.h:25
SYS_FORCE_INLINE TO_T UTverify_cast(FROM_T from)
Definition: UT_Assert.h:208
void newSopOperator(OP_OperatorTable *table)
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
virtual OP_ERROR cookMySop(OP_Context &context) override
Since this SOP implements a verb, cookMySop just delegates to the verb.
static PRM_ChoiceList pointGroupMenu
Definition: SOP_Node.h:1178
void assignVertex(GA_Offset vtx, bool update_topology)
Called when loading to set the vertex.
SOP_CopyPacked(OP_Network *net, const char *name, OP_Operator *op)
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:233
GA_Size deletePrimitives(const GA_Range &range, bool and_points=false)
Definition: GEO_Detail.h:1205
#define GA_INVALID_OFFSET
Definition: GA_Types.h:676
iterator find(const Key &key)
Definition: UT_ArrayMap.h:158
GA_Size countPrimitiveType(const GA_PrimitiveTypeId &type) const
Definition: GA_Detail.h:2224
UT_ErrorSeverity sopAddError(int code, const char *msg=0, const UT_SourceLocation *loc=0) const
Definition: SOP_NodeVerb.h:726
GA_API const UT_StringHolder name
#define UT_ASSERT_MSG(ZZ,...)
Definition: UT_Assert.h:138
GA_Size GA_Offset
Definition: GA_Types.h:639
const T & parms() const
Definition: SOP_NodeVerb.h:690
SYS_FORCE_INLINE GA_Index primitiveIndex(GA_Offset offset) const
Given a primitive's data offset, return its index.
Definition: GA_Detail.h:374
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1666
const GA_IndexMap & getPointMap() const
Definition: GA_Detail.h:684
Constructs a PRM_Template list from an embedded .ds file or an istream.
GA_Offset appendPrimitiveBlock(const GA_PrimitiveTypeId &type, GA_Size nprimitives)
Append a contiguous block of primitives by GA_PrimitiveTypeId.
void UTparallelForLightItems(const Range &range, const Body &body)
PRM_Template * templates() const
std::pair< iterator, bool > insert(const Key &key, const T &val)
Definition: UT_ArrayMap.h:117
static const SOP_NodeVerb::Register< SOP_CopyPackedVerb > theVerb
virtual SOP_NodeParms * allocParms() const
GLuint GLuint end
Definition: glew.h:1253
OP_ERROR cookMyselfAsVerb(OP_Context &context)
virtual SOP_NodeCache * allocCache() const
GLsizei n
Definition: glew.h:4040
UT_UniquePtr< GA_PrimitiveGroup > GA_PrimitiveGroupUPtr
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1633
A handle to simplify manipulation of multiple attributes.
#define SYSstoreFence()
SYS_FORCE_INLINE GA_Offset vertexPoint(GA_Offset vertex) const
Given a vertex, return the point it references.
Definition: GA_Detail.h:480
static PRM_Template * buildTemplates(const UT_StringHolder &filename, const char *ds_file)
GA_API const UT_StringHolder id
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:633
SYS_FORCE_INLINE const GA_ATITopology * getPrimitiveRef() const
Definition: GA_Topology.h:223
GU_Detail * gdpNC()
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:34
GLuint start
Definition: glew.h:1253
GA_Topology & getTopology()
Definition: GA_Detail.h:741
SYS_FORCE_INLINE const GA_Attribute * findPrimitiveAttribute(GA_AttributeScope s, const UT_StringRef &name) const
Definition: GA_Detail.h:991
SYS_FORCE_INLINE GA_Index pointIndex(GA_Offset offset) const
Given a point's data offset, return its index.
Definition: GA_Detail.h:300
SYS_FORCE_INLINE bool isTrivialMap() const
Definition: GA_IndexMap.h:290
void mergePoints(const GEO_Detail &src, const GA_PointGroup *ptGrp=0, bool merge_groups=true, bool keep_internal_groups=true)
const GA_PointGroup * parsePointDetached(const char *pat, const GEO_Detail *pgdp, bool forceexistence, bool &success)
GA_AttributeOwner
Definition: GA_Types.h:33
virtual CookMode cookMode(const SOP_NodeParms *parms) const
virtual int isRefInput(unsigned i) const override
virtual const char * inputLabel(unsigned idx) const override
These are the labels that appear when hovering over the inputs.
void replaceWithPoints(const GA_Detail &src, const GA_AttributeFilter *skip=nullptr)
SYS_FORCE_INLINE GA_Offset primitiveOffset(GA_Index index) const
Given a primitive's index (in append order), return its data offset.
Definition: GA_Detail.h:370
virtual void cook(const CookParms &cookparms) const
This is the function that does the actual work.
SYS_FORCE_INLINE const GA_ATITopology * getVertexRef() const
Definition: GA_Topology.h:225
static const UT_StringHolder theSOPTypeName
SYS_FORCE_INLINE GA_Offset offsetFromIndex(GA_Index ordered_index) const
Definition: GA_IndexMap.h:117
GLdouble GLdouble GLdouble r
Definition: glew.h:1406
SYS_FORCE_INLINE GA_Size getNumPrimitives() const
Return the number of primitives.
Definition: GA_Detail.h:359
SYS_FORCE_INLINE GA_Offset getVertexOffset(GA_Size primvertexnum) const
Definition: GA_Primitive.h:240
const GU_Detail * inputGeo(exint idx) const
Definition: SOP_NodeVerb.h:678
SOP_NodeCache * cache() const
Definition: SOP_NodeVerb.h:695
void appendAndCreateAllSource(GA_AttributeOwner destowner, GA_AttributeOwner sourceowner, const char *matchpattern, UT_ArraySet< const GA_Attribute * > *alreadymappeddest=nullptr, bool includegroups=true)
void copyMemberDataFrom(const GEO_PrimPacked &src)
virtual const SOP_NodeVerb * cookVerb() const override
static const char *const theDsFile
This is the parameter interface string, below.
void copyValue(GA_AttributeOwner downer, GA_Offset doffset, GA_AttributeOwner sowner, GA_Offset soffset, Cache *cache=0) const
Automatically expand attribute data pages for threading.
SYS_FORCE_INLINE const GA_Attribute * findPointAttribute(GA_AttributeScope s, const UT_StringRef &name) const
Definition: GA_Detail.h:977
static OP_Node * myConstructor(OP_Network *net, const char *name, OP_Operator *op)
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1669
void bumpDataIdsForAddOrRemove(bool added_or_removed_points, bool added_or_removed_vertices, bool added_or_removed_primitives)
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
Declare prior to use.
SYS_FORCE_INLINE const GA_ATITopology * getPointRef() const
Definition: GA_Topology.h:221
SYS_FORCE_INLINE void addOffset(GA_Offset ai)
GU_DetailHandle & gdh() const
The initial state of gdh depends on the cookMode()
Definition: SOP_NodeVerb.h:634
SYS_FORCE_INLINE bool isstring() const
SYS_FORCE_INLINE GA_Offset appendVertexBlock(GA_Size nvertices)
Append new vertices, returning the first offset of the contiguous block.
Definition: GA_Detail.h:451
static bool isPackedPrimitive(const GA_PrimitiveDefinition &pdef)
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:285
SYS_FORCE_INLINE void setLink(GA_Offset ai, GA_Offset v)