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