HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SOP_CopyToPointsHDK.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 geometry from the first input onto points in the second
27  * input, caching information to speed up subsequent cooks where the output
28  * is cached and the input geometry hasn't changed topology.
29  */
30 
31 // A .proto.h file is an automatically generated header file based on theDsFile,
32 // below, to provide SOP_CopyToPointsHDKParms, an easy way to access parameter
33 // values from SOP_CopyToPointsHDKVerb::cook with the correct type, and
34 // SOP_CopyToPointsHDKEnums, a namespace containing enum types for any ordinal
35 // menu parameters.
36 #include "SOP_CopyToPointsHDK.proto.h"
37 
38 #include "GU_Copy2.h"
39 #include "GEO_BuildPrimitives.h"
40 
41 #include <SOP/SOP_Node.h>
42 #include <SOP/SOP_NodeVerb.h>
43 #include <GU/GU_PackedGeometry.h>
44 #include <GU/GU_PrimPacked.h>
46 #include <GA/GA_Primitive.h>
47 #include <GA/GA_Range.h>
48 #include <GA/GA_SplittableRange.h>
49 #include <OP/OP_AutoLockInputs.h>
50 #include <PRM/PRM_ChoiceList.h>
52 #include <UT/UT_ArrayMap.h>
53 #include <UT/UT_ArrayStringMap.h>
54 #include <UT/UT_DSOVersion.h>
55 #include <UT/UT_PageArrayImpl.h>
56 #include <UT/UT_ParallelUtil.h>
57 #include <UT/UT_StringHolder.h>
58 #include <UT/UT_UndoManager.h>
59 
60 #include <utility> // For std::pair
61 
62 using namespace UT::Literal;
63 
64 namespace HDK_Sample {
65 
66 using namespace GU_Copy;
67 
69 {
70  ~SOP_CopyToPointsHDKCache() override {}
71  // Contents in GU_CopyToPointsCache
72 };
73 
75 {
76 public:
79 
80  SOP_NodeParms *allocParms() const override { return new SOP_CopyToPointsHDKParms(); }
81  SOP_NodeCache *allocCache() const override { return new SOP_CopyToPointsHDKCache(); }
82  UT_StringHolder name() const override { return theSOPTypeName; }
83 
84  CookMode cookMode(const SOP_NodeParms *parms) const override
85  { return COOK_GENERIC; }
86 
87  void cook(const CookParms &cookparms) const override;
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 // Add "::2.0" to the type name to make a version 2.0 of the node without displacing
104 // the original.
105 const UT_StringHolder SOP_CopyToPointsHDKVerb::theSOPTypeName("hdk_copytopoints"_sh);
106 const SOP_NodeVerb::Register<SOP_CopyToPointsHDKVerb> SOP_CopyToPointsHDKVerb::theVerb;
107 
108 //******************************************************************************
109 //* Parameters *
110 //******************************************************************************
111 
112 /// This is a multi-line raw string specifying the parameter interface for this SOP.
113 const char *const SOP_CopyToPointsHDKVerb::theDsFile = R"THEDSFILE(
114 {
115  name parameters
116  parm {
117  name "sourcegroup"
118  cppname "SourceGroup"
119  label "Source Group"
120  type string
121  default { "" }
122  parmtag { "script_action" "import soputils\nkwargs['geometrytype'] = kwargs['node'].parmTuple('sourcegrouptype')\nkwargs['inputindex'] = 0\nsoputils.selectGroupParm(kwargs)" }
123  parmtag { "script_action_help" "Select geometry from an available viewport.\nShift-click to turn on Select Groups." }
124  parmtag { "script_action_icon" "BUTTONS_reselect" }
125  parmtag { "sop_input" "0" }
126  }
127  parm {
128  name "sourcegrouptype"
129  cppname "SourceGroupType"
130  label "Source Group Type"
131  type ordinal
132  default { "0" }
133  menu {
134  "guess" "Guess from Group"
135  "prims" "Primitives"
136  "points" "Points"
137  }
138  }
139  parm {
140  name "targetgroup"
141  cppname "TargetGroup"
142  label "Target Points"
143  type string
144  default { "" }
145  parmtag { "script_action" "import soputils\nkwargs['geometrytype'] = (hou.geometryType.Points,)\nkwargs['inputindex'] = 1\nsoputils.selectGroupParm(kwargs)" }
146  parmtag { "script_action_help" "Select geometry from an available viewport.\nShift-click to turn on Select Groups." }
147  parmtag { "script_action_icon" "BUTTONS_reselect" }
148  parmtag { "sop_input" "1" }
149  }
150  parm {
151  name "useidattrib"
152  cppname "UseIDAttrib"
153  label "Piece Attribute"
154  type toggle
155  joinnext
156  nolabel
157  default { "0" }
158  }
159  parm {
160  name "idattrib"
161  cppname "IDAttrib"
162  label "Piece Attribute"
163  type string
164  default { "variant" }
165  parmtag { "sop_input" "1" }
166  disablewhen "{ useidattrib == 0 }"
167  }
168  parm {
169  name "pack"
170  label "Pack and Instance"
171  type toggle
172  default { "off" }
173  }
174  parm {
175  name "pivot"
176  label "Pivot Location"
177  type ordinal
178  default { "centroid" }
179  hidewhen "{ pack == 0 }"
180  menu {
181  "origin" "Origin"
182  "centroid" "Centroid"
183  }
184  }
185  parm {
186  name "viewportlod"
187  cppname "ViewportLOD"
188  label "Display As"
189  type ordinal
190  default { "full" }
191  hidewhen "{ pack == 0 }"
192  menu {
193  "full" "Full Geometry"
194  "points" "Point Cloud"
195  "box" "Bounding Box"
196  "centroid" "Centroid"
197  "hidden" "Hidden"
198  }
199  }
200  parm {
201  name "transform"
202  label "Transform Using Target Point Orientations"
203  type toggle
204  default { "on" }
205  }
206  parm {
207  name "useimplicitn"
208  cppname "UseImplicitN"
209  label "Transform Using Implicit Target Point Normals If No Point N Attribute"
210  type toggle
211  default { "on" }
212  disablewhen "{ transform == 0 }"
213  }
214  parm {
215  name "resettargetattribs"
216  label "Reset Attributes from Target"
217  type button
218  default { "0" }
219  }
220  multiparm {
221  name "targetattribs"
222  cppname "TargetAttribs"
223  label "Attributes from Target"
224  default 0
225 
226  parm {
227  name "useapply#"
228  label "Apply Attributes"
229  type toggle
230  joinnext
231  nolabel
232  default { "1" }
233  }
234  parm {
235  name "applyto#"
236  label "Apply to"
237  type ordinal
238  joinnext
239  default { "0" }
240  menu {
241  "points" "Points"
242  "verts" "Vertices"
243  "prims" "Primitives"
244  }
245  }
246  parm {
247  name "applymethod#"
248  label "by"
249  type ordinal
250  joinnext
251  default { "0" }
252  menu {
253  "copy" "Copying"
254  "none" "Nothing"
255  "mult" "Multiplying"
256  "add" "Adding"
257  "sub" "Subtracting"
258  }
259  }
260  parm {
261  name "applyattribs#"
262  label "Attributes"
263  type string
264  parmtag { "sop_input" "1" }
265  default { "" }
266  }
267  }
268 }
269 )THEDSFILE";
270 
271 namespace {
272 /// Keep this enum in sync with the menu in the "applymethod#" parameter.
273 using sop_AttribCombineMethod = GU_CopyToPointsCache::AttribCombineMethod;
274 
275 /// Should take the menu out of the DS file so we can have one
276 /// copy of this table
277 static const char *const theViewportLODNames[5] =
278 {
279  "full",
280  "points",
281  "box",
282  "centroid",
283  "hidden"
284 };
285 }
286 
288 static GEO_ViewportLOD sopViewportLODFromParam(
290 {
291  GEO_ViewportLOD lod = GEOviewportLOD( theViewportLODNames[ int(param_lod) ] );
292  return lod;
293 }
295 static GU_CopyToPointsCache::PackedPivot sopCachePivotType(
296  const SOP_CopyToPointsHDKEnums::Pivot pivot_param)
297 {
298  using namespace SOP_CopyToPointsHDKEnums;
299 
300  switch (pivot_param)
301  {
302  case Pivot::ORIGIN:
304  case Pivot::CENTROID:
306  }
307  UT_ASSERT_MSG_P(0, "Unhandled pivot type!");
309 }
310 
311 class SOP_CopyToPointsHDK final : public SOP_Node
312 {
313 public:
314  SOP_CopyToPointsHDK(OP_Network *net, const char *name, OP_Operator *entry);
315  ~SOP_CopyToPointsHDK() override;
316 
317  OP_ERROR cookInputGroups(OP_Context &context, int alone = 0) override;
318 
319  static OP_Node *myConstructor(OP_Network *net, const char *name, OP_Operator *entry);
320 
321  static PRM_Template *buildTemplates();
322  const SOP_NodeVerb *cookVerb() const override;
323 
324  void resetTargetAttribs();
325 protected:
326  OP_ERROR cookMySop(OP_Context &context) override;
327  const char *inputLabel(unsigned idx) const override;
328  int isRefInput(unsigned int i) const;
329 };
330 
331 static int
332 sopResetTargetAttribsWrapper(
333  void *data, int index, fpreal t, const PRM_Template *)
334 {
335  UT_AutoUndoBlock u("Reset Target Point Attributes", ANYLEVEL);
336  SOP_CopyToPointsHDK *me = static_cast<SOP_CopyToPointsHDK *>(data);
337 
338  if (!me)
339  return 0;
340 
341  me->resetTargetAttribs();
342  return 1;
343 }
344 void
345 SOP_CopyToPointsHDK::resetTargetAttribs()
346 {
347  setInt("targetattribs", 0, fpreal(0.0), 3);
348  int multiparm_index = 1;
349  setIntInst(1, "useapply#", &multiparm_index, 0, fpreal(0.0));
350  setIntInst(0, "applyto#", &multiparm_index, 0, fpreal(0.0));
351  setIntInst(int(sop_AttribCombineMethod::COPY), "applymethod#", &multiparm_index, 0, fpreal(0.0));
352  setStringInst("*,^v,^Alpha,^N,^up,^pscale,^scale,^orient,^rot,^pivot,^trans,^transform", CH_StringMeaning::CH_STRING_LITERAL, "applyattribs#", &multiparm_index, 0, fpreal(0.0));
353  ++multiparm_index;
354  setIntInst(1, "useapply#", &multiparm_index, 0, fpreal(0.0));
355  setIntInst(0, "applyto#", &multiparm_index, 0, fpreal(0.0));
356  setIntInst(int(sop_AttribCombineMethod::MULTIPLY), "applymethod#", &multiparm_index, 0, fpreal(0.0));
357  setStringInst("Alpha", CH_StringMeaning::CH_STRING_LITERAL, "applyattribs#", &multiparm_index, 0, fpreal(0.0));
358  ++multiparm_index;
359  setIntInst(1, "useapply#", &multiparm_index, 0, fpreal(0.0));
360  setIntInst(0, "applyto#", &multiparm_index, 0, fpreal(0.0));
361  setIntInst(int(sop_AttribCombineMethod::ADD), "applymethod#", &multiparm_index, 0, fpreal(0.0));
362  setStringInst("v", CH_StringMeaning::CH_STRING_LITERAL, "applyattribs#", &multiparm_index, 0, fpreal(0.0));
363 }
364 
365 static bool
366 sopApproveStringIntAttribs(const GA_Attribute *attrib, void*)
367 {
368  if (GA_ATIString::isType(attrib))
369  return true;
370 
371  const GA_ATINumeric *numeric = GA_ATINumeric::cast(attrib);
372  if (!numeric)
373  return false;
374  return (numeric->getStorageClass() == GA_STORECLASS_INT);
375 }
376 
377 static void
378 sopBuildStringIntPointAttribMenu(
379  void *data, PRM_Name *menu_entries, int menu_size,
380  const PRM_SpareData *, const PRM_Parm *)
381 {
382  SOP_CopyToPointsHDK *sop = (SOP_CopyToPointsHDK *)data;
383  if (!sop || !sop->getInput(1))
384  return;
385 
386  sop->fillAttribNameMenu(menu_entries, menu_size,
387  GA_ATTRIB_POINT, 1, sopApproveStringIntAttribs);
388 }
389 
390 static void
391 sopBuildTargetPointAttribMenu(
392  void *data, PRM_Name *menu_entries, int menu_size,
393  const PRM_SpareData *, const PRM_Parm *)
394 {
395  SOP_CopyToPointsHDK *sop = (SOP_CopyToPointsHDK *)data;
396  if (!sop || !sop->getInput(1))
397  return;
398 
399  sop->fillAttribNameMenu(menu_entries, menu_size,
400  GA_ATTRIB_POINT, 1);
401 }
402 
403 static PRM_ChoiceList sopStringIntPointAttribReplaceMenu(
405  sopBuildStringIntPointAttribMenu);
406 
407 static PRM_ChoiceList sopTargetPointAttribMenu(
409  sopBuildTargetPointAttribMenu);
410 
412 SOP_CopyToPointsHDK::buildTemplates()
413 {
414  static PRM_TemplateBuilder templ("SOP_CopyToPointsHDK.C"_sh, SOP_CopyToPointsHDKVerb::theDsFile);
415  if (templ.justBuilt())
416  {
417  templ.setChoiceListPtr("sourcegroup", &SOP_Node::primGroupMenu);
418  templ.setChoiceListPtr("targetgroup", &SOP_Node::pointGroupMenu);
419  templ.setChoiceListPtr("idattrib", &sopStringIntPointAttribReplaceMenu);
420  templ.setChoiceListPtr("applyattribs#",&sopTargetPointAttribMenu);
421  templ.setCallback("resettargetattribs", &sopResetTargetAttribsWrapper);
422  }
423  return templ.templates();
424 }
425 
426 OP_Node *
427 SOP_CopyToPointsHDK::myConstructor(OP_Network *net, const char *name, OP_Operator *entry)
428 {
429  return new SOP_CopyToPointsHDK(net, name, entry);
430 }
431 
432 SOP_CopyToPointsHDK::SOP_CopyToPointsHDK(OP_Network *net, const char *name, OP_Operator *entry)
433  : SOP_Node(net, name, entry)
434 {
436 }
438 
439 OP_ERROR
441 {
442  UT_ASSERT(alone);
443  OP_AutoLockInputs inputs(this);
444  if (inputs.lock(context) >= UT_ERROR_ABORT)
445  return error();
446 
447  const GU_Detail *detail = inputGeo(0);
448 
451  evalInt(1, 0, CHgetEvalTime()));
452  GA_GroupType group_type = GA_GROUP_INVALID;
453  if (param_group_type == SOP_CopyToPointsHDKEnums::SourceGroupType::POINTS)
454  group_type = GA_GROUP_POINT;
455  else if (param_group_type == SOP_CopyToPointsHDKEnums::SourceGroupType::PRIMS)
456  group_type = GA_GROUP_PRIMITIVE;
457 
458  const GA_Group *source_group;
459  OP_ERROR ret = cookInputAllGroups(context, source_group, alone, true, 0, 1, group_type, true, true, false, GroupCreator(detail));
460  if (ret >= UT_ERROR_ABORT)
461  return ret;
462 
463  detail = inputGeo(1);
464  const GA_PointGroup *ptgroup;
465  ret = cookInputPointGroups(context, ptgroup, alone, true, 2, -1, true, true, GroupCreator(detail));
466  return ret;
467 }
468 
469 OP_ERROR
471 {
472  return cookMyselfAsVerb(context);
473 }
474 
475 const SOP_NodeVerb *
477 {
479 }
480 
481 } // End of HDK_Sample namespace
482 
483 /// newSopOperator is the hook that Houdini grabs from this dll
484 /// and invokes to register the SOP. In this case, we add ourselves
485 /// to the specified operator table.
487 {
488  table->addOperator(new OP_Operator(
490  "HDK Copy to Points", // UI name
491  HDK_Sample::SOP_CopyToPointsHDK::myConstructor, // How to build the SOP
493  2, // Min # of sources
494  2, // Max # of sources
495  nullptr,// Custom local variables (none)
496  0)); // No flags: not a generator, no merge input, not an output
497 }
498 
499 namespace HDK_Sample {
500 
501 /// NOTE: sopcache must already have myTargetOffsetList initialized for
502 /// this cook, since it's used for passing the target point list,
503 /// and it may be modified in here.
504 static bool
505 sopCopyByIDAttrib(
506  const SOP_NodeVerb::CookParms &cookparms,
507  const SOP_CopyToPointsHDKParms &sopparms,
508  SOP_CopyToPointsHDKCache *sopcache,
509  GU_Detail *output_geo,
510  const GU_Detail *source,
511  const GA_PrimitiveGroup *source_primgroup,
512  const GA_PointGroup *source_pointgroup,
513  const bool source_topology_changed,
514  const GU_Detail *target,
515  GA_OffsetList &input_target_point_list,
516  const GA_PointGroup *target_group,
519 {
520  // *** ID Attribute Setup ***
521 
522  const UT_StringHolder &idattribname = sopparms.getIDAttrib();
523  const GA_Attribute *idattrib = target->findPointAttribute(idattribname);
524  const GA_ATINumeric *idnumeric = GA_ATINumeric::cast(idattrib);
525  const GA_ATIString *idstring = GA_ATIString::cast(idattrib);
526 
527  GA_ROHandleID target_int_idattrib;
528  GA_ROHandleID source_int_idattrib;
529  GA_ROHandleS target_str_idattrib;
530  GA_ROHandleS source_str_idattrib;
531  GA_AttributeOwner source_id_owner;
532  if (idnumeric)
533  {
534  if (idnumeric->getStorageClass() != GA_STORECLASS_INT)
535  {
537  buf.appendSprintf("Target Piece Attribute (%s) is a floating-point "
538  "attribute; it must be an integer or string attribute, "
539  "so ignoring Piece Attribute.",
540  idattribname.c_str());
541  cookparms.sopAddWarning(SOP_MESSAGE, buf.buffer());
542  return false;
543  }
544 
545  source_int_idattrib.bind(source->findPrimitiveAttribute(idattribname));
546  if (!source_int_idattrib.isValid())
547  source_int_idattrib.bind(source->findPointAttribute(idattribname));
548 
549  // NOTE: We don't require a source id attribute, because it
550  // defaults to using the source primitive number if there
551  // isn't a source attribute and the target attribute is
552  // an integer attribute.
553 
554  target_int_idattrib.bind(idnumeric);
555  UT_ASSERT(target_int_idattrib.isValid());
556  source_id_owner = !source_int_idattrib.isValid() ? GA_ATTRIB_PRIMITIVE : source_int_idattrib->getOwner();
557  }
558  else if (idstring)
559  {
560  source_str_idattrib.bind(source->findPrimitiveAttribute(idattribname));
561  if (!source_str_idattrib.isValid())
562  source_str_idattrib.bind(source->findPointAttribute(idattribname));
563 
564  // String case requires a source attribute
565  if (!source_str_idattrib.isValid())
566  {
568  buf.appendSprintf("Target Piece Attribute (%s) is a string "
569  "attribute; source has no corresponding string point or "
570  "primitive attribute, so ignoring Piece Attribute.",
571  idattribname.c_str());
572  cookparms.sopAddWarning(SOP_MESSAGE, buf.buffer());
573  return false;
574  }
575 
576  target_str_idattrib.bind(idstring);
577  source_id_owner = source_str_idattrib->getOwner();
578  }
579  else
580  {
582  buf.appendSprintf("Piece Attribute (%s) is not an integer "
583  "or string attribute in the target input, so ignoring Piece Attribute.",
584  idattribname.c_str());
585  cookparms.sopAddWarning(SOP_MESSAGE, buf.buffer());
586  return false;
587  }
588 
589  bool target_group_changed =
590  (target_group != nullptr) != (sopcache->myPrevHadTargetGroup);
591  bool target_group_maybe_changed = target_group_changed;
592  if (!target_group_changed && target_group)
593  {
594  // We don't check the contents until after handling
595  // unmatched target points below.
596  bool different_group = target_group->isDetached() ||
597  (target_group->getDataId() != sopcache->myPrevTargetGroupDataID);
598  target_group_maybe_changed = different_group;
599  }
600 
601  // NOTE: If we're using an ID attribute and the target group has changed,
602  // the output topology changes, even if the number of target points
603  // is the same as before, since the selection could be different.
604  bool topology_changed =
605  (output_geo->getUniqueId() != sopcache->myPrevOutputDetailID) ||
606  source_topology_changed ||
607  target_group_changed ||
608  (sopparms.getPack() != sopcache->myPrevPack);
609 
610  GA_DataId target_idattrib_dataid = idattrib->getDataId();
611  topology_changed |= (target_idattrib_dataid != sopcache->myTargetIDAttribDataID);
612  sopcache->myTargetIDAttribDataID = target_idattrib_dataid;
613 
614  topology_changed |= (source_id_owner != sopcache->mySourceIDAttribOwner);
615  sopcache->mySourceIDAttribOwner = source_id_owner;
616 
617  GA_DataId source_idattrib_dataid;
618  if (source_int_idattrib.isValid())
619  {
620  source_idattrib_dataid = source_int_idattrib->getDataId();
621  topology_changed |= (source_idattrib_dataid != sopcache->mySourceIDAttribDataID);
622  }
623  else if (source_str_idattrib.isValid())
624  {
625  source_idattrib_dataid = source_str_idattrib->getDataId();
626  topology_changed |= (source_idattrib_dataid != sopcache->mySourceIDAttribDataID);
627  }
628  else
629  {
630  // Using source primitive number as id attribute,
631  // and source topology didn't change here, so
632  // the primitive numbers haven't changed.
633  source_idattrib_dataid = GA_INVALID_DATAID;
634  }
635  sopcache->mySourceIDAttribDataID = source_idattrib_dataid;
636 
637  using PieceData = SOP_CopyToPointsHDKCache::PieceData;
638  UT_Array<PieceData> &piece_data = sopcache->myPieceData;
639  UT_Array<exint> &target_to_piecei = sopcache->myTargetToPiece;
640 
641  bool recompute_mapping = topology_changed || target_group_maybe_changed;
642  if (recompute_mapping)
643  {
644  // *** Value to Source Mapping ***
645 
646  // These arrays map an attribute value to a pair consisting of
647  // the number of target points with that value, and
648  // the list of source offsets with that value.
651  GA_Range target_point_range(target->getPointMap(), input_target_point_list);
652  GA_OffsetList limited_target_point_list_nc;
653  if (target_int_idattrib.isValid())
654  {
655  // First, find all (integer) target values.
657  GA_Offset end;
658  for (GA_Iterator it(target_point_range); it.blockAdvance(start, end); )
659  {
660  GA_PageNum pagenum = GAgetPageNum(start);
661  if (target_int_idattrib->isPageConstant(pagenum))
662  {
663  // Only try to insert once for constant page
664  exint value = target_int_idattrib.get(start);
665  // UT_ArrayMap excludes one key value, which we must skip.
667  continue;
668 
669  bool new_value = !target_int_to_source.contains(value);
670 
671  // NOTE: This relies on operator[] to do the insertion.
672  std::pair<exint,GA_OffsetList> &pair = target_int_to_source[value];
673  if (new_value)
674  pair.first = GA_Size(end-start);
675  else
676  {
677  pair.first += GA_Size(end-start);
678  continue;
679  }
680 
681  if (!source_int_idattrib.isValid() &&
682  GAisValid(value) &&
683  value < source->getNumPrimitives())
684  {
685  // Add primitive offset with value as index,
686  // if it's a valid index and in source_primgroup.
687  GA_Offset source_primoff = source->primitiveOffset(GA_Index(value));
688  if (!source_primgroup || source_primgroup->contains(source_primoff))
689  pair.second.append(source_primoff);
690  }
691  }
692  else
693  {
694  for (GA_Offset target_ptoff = start; target_ptoff < end; ++target_ptoff)
695  {
696  exint value = target_int_idattrib.get(target_ptoff);
697  // UT_ArrayMap excludes one key value, which we must skip.
699  continue;
700 
701  bool new_value = !target_int_to_source.contains(value);
702 
703  // NOTE: This relies on operator[] to do the insertion.
704  std::pair<exint,GA_OffsetList> &pair = target_int_to_source[value];
705  if (new_value)
706  pair.first = 1;
707  else
708  {
709  pair.first += 1;
710  continue;
711  }
712 
713  if (!source_int_idattrib.isValid() &&
714  GAisValid(value) &&
715  value < source->getNumPrimitives())
716  {
717  // Add primitive offset with value as index,
718  // if it's a valid index and in source_primgroup.
719  GA_Offset source_primoff = source->primitiveOffset(GA_Index(value));
720  if (!source_primgroup || source_primgroup->contains(source_primoff))
721  pair.second.append(source_primoff);
722  }
723  }
724  }
725  }
726 
727  if (source_int_idattrib.isValid())
728  {
729  // Fill in all source offsets corresponding with each target value.
730  GA_Range source_id_range;
731  if (source_id_owner == GA_ATTRIB_PRIMITIVE)
732  {
733  source_id_range = source->getPrimitiveRange(source_primgroup);
734  }
735  else
736  {
737  UT_ASSERT(source_id_owner == GA_ATTRIB_POINT);
738  source_id_range = source->getPointRange(source_pointgroup);
739  }
741  GA_Offset end;
742  for (GA_Iterator it(source_id_range); it.blockAdvance(start, end); )
743  {
744  GA_PageNum pagenum = GAgetPageNum(start);
745  if (source_int_idattrib->isPageConstant(pagenum))
746  {
747  exint value = source_int_idattrib.get(start);
748  // UT_ArrayMap excludes one key value, which we must skip explicitly.
750  continue;
751  if (!target_int_to_source.contains(value))
752  continue;
753 
754  // Append the whole block at once.
755  GA_OffsetList &list = target_int_to_source[value].second;
756  list.setTrivialRange(list.size(), start, GA_Size(end-start));
757  }
758  else
759  {
760  for (GA_Offset source_offset = start; source_offset < end; ++source_offset)
761  {
762  exint value = source_int_idattrib.get(source_offset);
763  // UT_ArrayMap excludes one key value, which we must skip explicitly.
765  continue;
766  if (!target_int_to_source.contains(value))
767  continue;
768 
769  GA_OffsetList &list = target_int_to_source[value].second;
770  list.append(source_offset);
771  }
772  }
773  }
774  }
775 
776  // Remove map entries with no source offsets, since we
777  // don't do anything with them, even if we're in the packing case.
778  exint original_num_integers = target_int_to_source.size();
779  for (auto it = target_int_to_source.begin(); !it.atEnd(); )
780  {
781  if (it->second.second.size() == 0)
782  it = target_int_to_source.erase(it);
783  else
784  ++it;
785  }
786  if (target_int_to_source.size() != original_num_integers)
787  {
788  for (exint i = 0, n = input_target_point_list.size(); i < n; ++i)
789  {
790  GA_Offset ptoff = input_target_point_list[i];
791  exint value = target_int_idattrib.get(ptoff);
792  if (target_int_to_source.contains(value))
793  limited_target_point_list_nc.append(ptoff);
794  }
795  input_target_point_list = std::move(limited_target_point_list_nc);
796  }
797  }
798  else
799  {
800  // First, find all (string) target values.
801  UT_ASSERT(target_str_idattrib.isValid() && source_str_idattrib.isValid());
803  GA_Offset end;
804  for (GA_Iterator it(target_point_range); it.blockAdvance(start, end); )
805  {
806  GA_PageNum pagenum = GAgetPageNum(start);
807  if (target_str_idattrib->isPageConstant(pagenum))
808  {
809  // Only try to insert once for constant page
810  const UT_StringHolder &value = target_str_idattrib.get(start);
812 
813  bool new_value = !target_str_to_source.contains(value);
814 
815  // NOTE: This relies on operator[] to do the insertion.
816  std::pair<exint,GA_OffsetList> &pair = target_str_to_source[value];
817  if (new_value)
818  pair.first = GA_Size(end-start);
819  else
820  pair.first += GA_Size(end-start);
821  }
822  else
823  {
824  for (GA_Offset target_ptoff = start; target_ptoff < end; ++target_ptoff)
825  {
826  const UT_StringHolder &value = target_str_idattrib.get(target_ptoff);
828 
829  bool new_value = !target_str_to_source.contains(value);
830 
831  // NOTE: This relies on operator[] to do the insertion.
832  std::pair<exint,GA_OffsetList> &pair = target_str_to_source[value];
833  if (new_value)
834  pair.first = 1;
835  else
836  pair.first += 1;
837  }
838  }
839  }
840 
841  // Fill in all source offsets corresponding with each target value.
842  GA_Range source_id_range;
843  if (source_id_owner == GA_ATTRIB_PRIMITIVE)
844  {
845  source_id_range = source->getPrimitiveRange(source_primgroup);
846  }
847  else
848  {
849  UT_ASSERT(source_id_owner == GA_ATTRIB_POINT);
850  source_id_range = source->getPointRange(source_pointgroup);
851  }
852  for (GA_Iterator it(source_id_range); it.blockAdvance(start, end); )
853  {
854  GA_PageNum pagenum = GAgetPageNum(start);
855  if (source_str_idattrib->isPageConstant(pagenum))
856  {
857  const UT_StringHolder &value = source_str_idattrib.get(start);
859  if (!target_str_to_source.contains(value))
860  continue;
861 
862  // Append the whole block at once.
863  GA_OffsetList &list = target_str_to_source[value].second;
864  list.setTrivialRange(list.size(), start, GA_Size(end-start));
865  }
866  else
867  {
868  for (GA_Offset source_offset = start; source_offset < end; ++source_offset)
869  {
870  const UT_StringHolder &value = source_str_idattrib.get(source_offset);
872  if (!target_str_to_source.contains(value))
873  continue;
874 
875  GA_OffsetList &list = target_str_to_source[value].second;
876  list.append(source_offset);
877  }
878  }
879  }
880 
881  // Remove map entries with no source offsets, since we
882  // don't do anything with them, even if we're in the packing case.
883  exint original_num_strings = target_str_to_source.size();
884  for (auto it = target_str_to_source.begin(); !it.atEnd(); )
885  {
886  if (it->second.second.size() == 0)
887  it = target_str_to_source.erase(it);
888  else
889  ++it;
890  }
891  if (target_str_to_source.size() != original_num_strings)
892  {
893  for (exint i = 0, n = input_target_point_list.size(); i < n; ++i)
894  {
895  GA_Offset ptoff = input_target_point_list[i];
896  const UT_StringHolder &value = target_str_idattrib.get(ptoff);
897  if (target_str_to_source.contains(value))
898  limited_target_point_list_nc.append(ptoff);
899  }
900  input_target_point_list = std::move(limited_target_point_list_nc);
901  }
902  }
903 
904  // We've now limited the input_target_point_list, so we can check whether
905  // it's different from the previous cook.
906  target_group_changed |=
907  (!target_group && input_target_point_list.size() != sopcache->myPrevTargetPtCount);
908  if (!target_group_changed && target_group)
909  {
910  // For named groups, we don't need to the contents if the data ID is the same.
911  bool different_group = target_group->isDetached() ||
912  (target_group->getDataId() != sopcache->myPrevTargetGroupDataID);
913  if (different_group)
914  {
915  UT_ASSERT(sopcache->myTargetOffsetList.size() == input_target_point_list.size());
916  bool equal_group = sopcache->myTargetOffsetList.isEqual(input_target_point_list, 0, input_target_point_list.size());
917  if (!equal_group)
918  {
919  target_group_changed = true;
920  }
921  }
922  }
923  sopcache->myTargetOffsetList = input_target_point_list;
924  sopcache->myPrevHadTargetGroup = (target_group != nullptr);
925  sopcache->myPrevTargetGroupDataID = ((!target_group || target_group->isDetached()) ? GA_INVALID_DATAID : target_group->getDataId());
926  topology_changed |= target_group_changed;
927 
928  // Don't recompute piece data or target to piece mapping if
929  // it turned out that target group didn't change and
930  // topology didn't change for some other reason.
931  if (topology_changed)
932  {
933  // *** Sort out information about each piece ***
934 
935  piece_data.clear();
936 
937  // First, the integer vs. string specific data.
938  if (!target_int_to_source.empty())
939  {
940  piece_data.setSize(target_int_to_source.size());
941  exint piecei = 0;
942  for (auto it = target_int_to_source.ordered_begin(std::less<exint>()); !it.atEnd(); ++it, ++piecei)
943  {
944  PieceData &current_piece = piece_data[piecei];
945  current_piece.myRefCount = it->second.first;
946  // Record the piece index so that it's fast to look it up below.
947  it->second.first = piecei;
948 
949  current_piece.mySourceOffsetLists[source_id_owner] = std::move(it->second.second);
950  }
951  }
952  else if (!target_str_to_source.empty())
953  {
954  piece_data.setSize(target_str_to_source.size());
955  exint piecei = 0;
956  for (auto it = target_str_to_source.ordered_begin(std::less<UT_StringHolder>()); !it.atEnd(); ++it, ++piecei)
957  {
958  PieceData &current_piece = piece_data[piecei];
959  current_piece.myRefCount = it->second.first;
960  // Record the piece index so that it's fast to look it up below.
961  it->second.first = piecei;
962 
963  current_piece.mySourceOffsetLists[source_id_owner] = std::move(it->second.second);
964  }
965  }
966 
967  exint num_target_points = input_target_point_list.size();
968 
969  // Make a target point number to piece number mapping, so that we don't
970  // have to keep checking the attributes.
971  target_to_piecei.setSizeNoInit(num_target_points);
972  if (target_int_idattrib.isValid())
973  {
974  for (exint target_pointi = 0; target_pointi < num_target_points; ++target_pointi)
975  {
976  GA_Offset target_ptoff = input_target_point_list[target_pointi];
977  exint target_value = target_int_idattrib.get(target_ptoff);
978  exint piecei = target_int_to_source[target_value].first;
979  target_to_piecei[target_pointi] = piecei;
980  }
981  }
982  else
983  {
984  UT_ASSERT(target_str_idattrib.isValid());
985  for (exint target_pointi = 0; target_pointi < num_target_points; ++target_pointi)
986  {
987  GA_Offset target_ptoff = input_target_point_list[target_pointi];
988  const UT_StringHolder &target_value = target_str_idattrib.get(target_ptoff);
989  exint piecei = target_str_to_source[target_value].first;
990  target_to_piecei[target_pointi] = piecei;
991  }
992  }
993  }
994  }
995 
996  // *** Transform Setup ***
997 
998  bool had_transform_matrices = (sopcache->myTransformMatrices3D.get() != nullptr);
999 
1000  // Use the target point list from the cache, so that it's always the limited list,
1001  // even if we didn't recompute the mapping on this cook.
1002  const GA_OffsetListRef &target_point_list = sopcache->myTargetOffsetList;
1003 
1004  // NOTE: Transforms have changed if the target group has changed,
1005  // even if the number of points is the same.
1006  bool transforms_changed = target_group_changed;
1007  GUsetupPointTransforms(sopcache, target_point_list, target, sopparms.getTransform(), sopparms.getUseImplicitN(), transforms_changed);
1008 
1009  const bool has_transform_matrices = (sopcache->myTransformMatrices3D.get() != nullptr);
1010 
1011  if (topology_changed)
1012  {
1013  // Next, the data that's independent of integer vs. string.
1014  const exint npieces = piece_data.size();
1015  for (exint piecei = 0; piecei < npieces; ++piecei)
1016  {
1017  PieceData &current_piece = piece_data[piecei];
1018  GA_OffsetList &source_point_list = current_piece.mySourceOffsetLists[GA_ATTRIB_POINT];
1019  GA_OffsetList &source_prim_list = current_piece.mySourceOffsetLists[GA_ATTRIB_PRIMITIVE];
1020 
1021  if (source_id_owner == GA_ATTRIB_POINT)
1022  {
1023  if (source->getNumPrimitives() > 0)
1024  {
1025  // Compute source prims list from point list
1026  bool done = false;
1027  if (source_point_list.size() == 1)
1028  {
1029  // One point, possibly a simple case.
1030  GA_Offset ptoff = source_point_list[0];
1031  GA_Offset vtxoff = source->pointVertex(ptoff);
1032  if (!GAisValid(vtxoff))
1033  {
1034  // No vertices on the point, so no primitives.
1035  done = true;
1036  }
1037  else if (!GAisValid(source->pointVertex(vtxoff)))
1038  {
1039  // One vertex, so one primitive... if it only has one vertex.
1040  GA_Offset primoff = source->vertexPrimitive(vtxoff);
1041  if (source->getPrimitiveVertexCount(primoff) == 1)
1042  source_prim_list.append(primoff);
1043  done = true;
1044  }
1045  }
1046  if (!done)
1047  {
1048  GA_DataBitArray points_in_piece(source->getNumPointOffsets(), false);
1049  GA_DataBitArray prims_in_piece(source->getNumPrimitiveOffsets(), false);
1050  for (exint i = 0, n = source_point_list.size(); i < n; ++i)
1051  {
1052  GA_Offset ptoff = source_point_list[i];
1053  points_in_piece.set<true>(ptoff);
1054  for (GA_Offset vtxoff = source->pointVertex(ptoff); GAisValid(vtxoff); vtxoff = source->vertexToNextVertex(vtxoff))
1055  {
1056  GA_Offset primoff = source->vertexPrimitive(vtxoff);
1057  prims_in_piece.set<true>(primoff);
1058  }
1059  }
1060 
1061  // Iterate through all of the primitives.
1062  // Add only primitives where all vertices have their points present in the piece.
1063  GA_Offset start = GA_Offset(0);
1064  GA_Offset end = source->getNumPrimitiveOffsets();
1065  while (start < end)
1066  {
1067  GA_Size size; bool value;
1068  prims_in_piece.getConstantSpan(start, end, size, value);
1069  if (!value)
1070  {
1071  start += size;
1072  continue;
1073  }
1074  GA_Offset local_end = start + size;
1075  for (GA_Offset primoff = start; primoff < local_end; ++primoff)
1076  {
1077  bool all_points_present = true;
1078  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1079  for (exint i = 0, n = vertices.size(); i < n; ++i)
1080  {
1081  GA_Offset ptoff = source->vertexPoint(vertices[i]);
1082  all_points_present &= points_in_piece.get(ptoff);
1083  }
1084  if (all_points_present)
1085  source_prim_list.append(primoff);
1086  }
1087  start = local_end;
1088  }
1089  }
1090  }
1091  }
1092  else
1093  {
1094  // Compute source points list from prim list
1095  if (source_prim_list.size() == 1 && source->getPrimitiveVertexCount(source_prim_list[0]) == 1)
1096  {
1097  // Common case of a single vertex, e.g. a single packed primitive.
1098  source_point_list.append(source->vertexPoint(source->getPrimitiveVertexOffset(source_prim_list[0],0)));
1099  }
1100  else
1101  {
1102  GA_DataBitArray points_in_piece(source->getNumPointOffsets(), false);
1103  for (exint i = 0, n = source_prim_list.size(); i < n; ++i)
1104  {
1105  GA_Offset primoff = source_prim_list[i];
1106  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1107  for (exint i = 0, n = vertices.size(); i < n; ++i)
1108  {
1109  GA_Offset ptoff = source->vertexPoint(vertices[i]);
1110  points_in_piece.set<true>(ptoff);
1111  }
1112  }
1113  // Iterate through all of the points, adding them to the list.
1114  GA_Offset start = GA_Offset(0);
1115  GA_Offset end = source->getNumPointOffsets();
1116  while (start < end)
1117  {
1118  GA_Size size; bool value;
1119  points_in_piece.getConstantSpan(start, end, size, value);
1120  if (value)
1121  {
1122  source_point_list.setTrivialRange(
1123  source_point_list.size(), start, size);
1124  }
1125  start += size;
1126  }
1127  }
1128  }
1129 
1130  // Fill in the source vertices list
1131  GA_OffsetList &source_vert_list = current_piece.mySourceOffsetLists[GA_ATTRIB_VERTEX];
1132  const GA_PrimitiveList &source_primlist = source->getPrimitiveList();
1133  for (exint i = 0, n = source_prim_list.size(); i < n; ++i)
1134  {
1135  GA_Offset primoff = source_prim_list[i];
1136  const GA_OffsetListRef vertices = source_primlist.getVertexList(primoff);
1137  source_vert_list.append(vertices);
1138  }
1139 
1140  // Fill in the vertex to point mapping
1141  exint nvertices = source_vert_list.size();
1142  if (nvertices < 16 || source_point_list.isTrivial())
1143  {
1144  // Common special case for few vertices, (e.g. 1 packed prim or
1145  // 1 simple curve), or trivial point list, so we don't need
1146  // a map to quickly find the point offset.
1147  for (exint i = 0; i < nvertices; ++i)
1148  {
1149  GA_Offset vtxoff = source_vert_list[i];
1150  GA_Offset ptoff = source->vertexPoint(vtxoff);
1151  exint rel_pointi = source_point_list.find(ptoff);
1152  current_piece.myRelVtxToPt.append(rel_pointi);
1153  }
1154  }
1155  else
1156  {
1157  // Many vertices and non-trivial point list,
1158  // so we use a reverse point map.
1159  exint npoints = source_point_list.size();
1160 
1161  UT_ArrayMap<GA_Offset,exint> ptoff_to_piecei;
1162  ptoff_to_piecei.reserve(npoints);
1163 
1164  for (exint pointi = 0; pointi < npoints; ++pointi)
1165  {
1166  GA_Offset ptoff = source_point_list[pointi];
1167  ptoff_to_piecei[ptoff] = pointi;
1168  }
1169 
1170  for (exint i = 0; i < nvertices; ++i)
1171  {
1172  GA_Offset vtxoff = source_vert_list[i];
1173  GA_Offset ptoff = source->vertexPoint(vtxoff);
1174  exint rel_pointi = ptoff_to_piecei[ptoff];
1175  current_piece.myRelVtxToPt.append(rel_pointi);
1176  }
1177  }
1178  }
1179  }
1180 
1181  const exint npieces = piece_data.size();
1182  const exint num_target_points = target_to_piecei.size();
1183 
1184  if (sopparms.getPack())
1185  {
1186  const GEO_ViewportLOD lod = sopViewportLODFromParam(sopparms.getViewportLOD());
1187  const bool source_changed =
1188  source->getUniqueId() != sopcache->myPrevSourceUniqueID ||
1189  source->getMetaCacheCount() != sopcache->myPrevSourceMetaCacheCount ||
1190  source_topology_changed;
1191  const bool lod_changed = (lod != sopcache->myPrevViewportLOD);
1192  const bool source_intrinsic_changed =
1193  source_changed ||
1194  sopCachePivotType(sopparms.getPivot()) != sopcache->myPrevPivotEnum ||
1195  lod_changed;
1196 
1197  UT_Array<GU_PackedGeometry *> packed_geos;
1198 
1199  // We need to recreate the packed primitive implementations even if
1200  // just attributes on the source have changed, since the impls need
1201  // to pick up the source attribute changes.
1202  if (topology_changed || source_changed)
1203  {
1204  // Packed primitive for each target point whose value has at least one source offset.
1205  output_geo->clearAndDestroy();
1206  if (num_target_points > 0)
1207  GUcreateEmptyPackedGeometryPrims(output_geo, num_target_points);
1208 
1209  // Create a GU_PackedGeometry for each piece, (in parallel).
1210  packed_geos.setSizeNoInit(npieces);
1211 
1212  UTparallelForEachNumber(npieces, [&piece_data,source,&packed_geos](const UT_BlockedRange<exint> &r)
1213  {
1214  for (exint piecei = r.begin(), end = r.end(); piecei < end; ++piecei)
1215  {
1216  PieceData &current_piece = piece_data[piecei];
1217 
1218  GU_DetailHandle detail_handle;
1219  GU_Detail *packed_detail = new GU_Detail();
1220  detail_handle.allocateAndSet(packed_detail);
1221 
1222  // FIXME: This should be able to use current_piece.myRelVtxToPt!!! or is that not the right way to do it???
1224  packed_detail,
1225  source,
1226  current_piece.mySourceOffsetLists[GA_ATTRIB_POINT],
1227  current_piece.mySourceOffsetLists[GA_ATTRIB_VERTEX],
1228  current_piece.mySourceOffsetLists[GA_ATTRIB_PRIMITIVE],
1229  1);
1230 
1231  exint num_source_attribs[3] = {0,0,0};
1233  "Array above depends on owners other than detail being less than 3");
1235  packed_detail,
1236  source,
1237  num_source_attribs);
1238 
1239  GA_SplittableRange output_splittable_ranges[3] =
1240  {
1241  GA_SplittableRange(packed_detail->getVertexRange()),
1242  GA_SplittableRange(packed_detail->getPointRange()),
1243  GA_SplittableRange(packed_detail->getPrimitiveRange())
1244  };
1246  "Arrays above and loop below are assuming the order of GA_AttributeOwner enum");
1247 
1248  // NOTE: We're never using the cache for this, so we pass nullptr for sopcache.
1250  packed_detail,
1251  output_splittable_ranges,
1252  source,
1253  1,
1254  nullptr,
1255  current_piece.mySourceOffsetLists,
1256  num_source_attribs,
1257  true,
1258  false,
1259  false,
1260  true,
1261  true);
1262 
1263  GU_PackedGeometry *packed_geo = new GU_PackedGeometry();
1264  packed_geo->setDetailPtr(detail_handle);
1265 
1266  // Cache the bounds in advance, even if we don't need the box center for the pivot,
1267  // to avoid thread contention downstream when anything requests it.
1268  UT_BoundingBox bbox;
1269  packed_geo->getBoundsCached(bbox);
1270 
1271  // Add the reference counter refs in bulk
1272  intrusive_ptr_add_ref(packed_geo, current_piece.myRefCount);
1273 
1274  packed_geos[piecei] = packed_geo;
1275  }
1276  });
1277  }
1278 
1279  bool centroid_pivot = (sopparms.getPivot() == SOP_CopyToPointsHDKEnums::Pivot::CENTROID);
1280 
1281  if (topology_changed || source_intrinsic_changed)
1282  {
1283  GA_Offset start_primoff = num_target_points ? output_geo->primitiveOffset(GA_Index(0)) : GA_INVALID_OFFSET;
1284 
1285  // Set the implementations of all of the primitives at once.
1286  auto &&functor = [output_geo,&packed_geos,start_primoff,
1287  &target_to_piecei,centroid_pivot,lod,
1288  topology_changed,source_changed](const UT_BlockedRange<GA_Offset> &r)
1289  {
1290  exint target_pointi = GA_Size(r.begin()-start_primoff);
1291  for (GA_Offset primoff = r.begin(), end = r.end(); primoff < end; ++primoff, ++target_pointi)
1292  {
1293  GA_Primitive *prim = output_geo->getPrimitive(primoff);
1294  GU_PrimPacked *packed_prim = UTverify_cast<GU_PrimPacked *>(prim);
1295  exint piecei = target_to_piecei[target_pointi];
1296 
1297  if (topology_changed || source_changed)
1298  {
1299  GU_PackedGeometry *packed_geo_nc = packed_geos[piecei];
1300 
1301  // References were already added above, so we don't need to add them here.
1302  packed_prim->setImplementation(packed_geo_nc, false);
1303  }
1304 
1305  UT_Vector3 pivot;
1306  if (centroid_pivot)
1307  {
1308  UT_BoundingBox bbox;
1309  const GU_PackedImpl *packed_geo = packed_prim->sharedImplementation();
1310  packed_geo->getBoundsCached(bbox);
1311  if (bbox.isValid())
1312  pivot = bbox.center();
1313  else
1314  pivot.assign(0,0,0);
1315  }
1316  else
1317  pivot.assign(0,0,0);
1318  packed_prim->setPivot(pivot);
1319 
1320  packed_prim->setViewportLOD(lod);
1321  }
1322  };
1323 
1324  const UT_BlockedRange<GA_Offset> prim_range(start_primoff, start_primoff+num_target_points);
1325  constexpr exint PARALLEL_THRESHOLD = 256;
1326  if (num_target_points >= PARALLEL_THRESHOLD)
1327  UTparallelForLightItems(prim_range, functor);
1328  else
1329  functor(prim_range);
1330  }
1331 
1332  const bool pivots_changed =
1333  sopCachePivotType(sopparms.getPivot()) != sopcache->myPrevPivotEnum ||
1334  (centroid_pivot && source_changed);
1335 
1336  // Pivots all zero if pivots not at centroid.
1337  UT_Vector3 pivot(0,0,0);
1338 
1340  output_geo,
1341  sopcache,
1342  topology_changed,
1343  had_transform_matrices,
1344  target,
1345  target_point_list,
1346  target_attrib_info,
1347  target_group_info,
1348  !centroid_pivot ? &pivot : nullptr);
1349 
1350  if (topology_changed)
1351  {
1352  output_geo->bumpDataIdsForAddOrRemove(true, true, true);
1353  }
1354  if (source_changed)
1355  {
1356  output_geo->getPrimitiveList().bumpDataId();
1357  }
1358  if (transforms_changed || pivots_changed)
1359  {
1360  output_geo->getP()->bumpDataId();
1361  // If there are no transform matrices, the primitives weren't transformed.
1362  // If source_changed, we already bumped the primitive list data ID.
1363  bool has_transform_matrices = (sopcache->myTransformMatrices3D.get() != nullptr);
1364  if ((has_transform_matrices || had_transform_matrices) && !source_changed)
1365  output_geo->getPrimitiveList().bumpDataId();
1366  }
1367  if (lod_changed)
1368  {
1369  output_geo->getPrimitiveList().bumpDataId();
1370  }
1371 
1372  sopcache->myPrevPack = true;
1373  sopcache->myPrevTargetPtCount = num_target_points;
1374  sopcache->myPrevSourceGroupDataID = source->getUniqueId();
1375  sopcache->myPrevSourceMetaCacheCount = source->getMetaCacheCount();
1376  sopcache->myPrevPivotEnum = sopCachePivotType(sopparms.getPivot());
1377  sopcache->myPrevViewportLOD = lod;
1378  sopcache->myPrevOutputDetailID = output_geo->getUniqueId();
1379  sopcache->myPrevSourceUniqueID = source->getUniqueId();
1380  sopcache->myPrevSourceMetaCacheCount = source->getMetaCacheCount();
1381 
1382  // No attributes from source in output_geo, so we can clear the source data ID maps.
1383  sopcache->mySourceAttribDataIDs[GA_ATTRIB_VERTEX].clear();
1384  sopcache->mySourceAttribDataIDs[GA_ATTRIB_POINT].clear();
1385  sopcache->mySourceAttribDataIDs[GA_ATTRIB_PRIMITIVE].clear();
1386  sopcache->mySourceGroupDataIDs[GA_ATTRIB_VERTEX].clear();
1387  sopcache->mySourceGroupDataIDs[GA_ATTRIB_POINT].clear();
1388  sopcache->mySourceGroupDataIDs[GA_ATTRIB_PRIMITIVE].clear();
1389  sopcache->mySourceEdgeGroupDataIDs.clear();
1390  sopcache->myPieceOffsetStarts[GA_ATTRIB_VERTEX].setCapacity(0);
1391  sopcache->myPieceOffsetStarts[GA_ATTRIB_POINT].setCapacity(0);
1392  sopcache->myPieceOffsetStarts[GA_ATTRIB_PRIMITIVE].setCapacity(0);
1393  }
1394  else
1395  {
1396  // Full copying (not packing)
1397  if (topology_changed)
1398  {
1399  output_geo->clearAndDestroy();
1400 
1401  UTparallelForEachNumber(npieces, [&piece_data,source](const UT_BlockedRange<exint> &r)
1402  {
1403  for (exint piecei = r.begin(), end = r.end(); piecei < end; ++piecei)
1404  {
1405  PieceData &current_piece = piece_data[piecei];
1406 
1407  // For this piece, compute:
1408  // whether there are shared points,
1409  // whether the points are contiguous,
1410  // the vertex list size list,
1411  // the primtype count pairs, and
1412  // the closed span lengths.
1413 
1414  // It's safe for hassharedpoints to be true when there are no shared points,
1415  // but not vice versa.
1416  bool hassharedpoints = true;
1417  // In this case, "contiguous" means "points of the vertices
1418  // start from startpt and are contiguous in vertex order."
1419  bool hascontiguouspoints = false;
1420  const GA_OffsetList &piece_point_list = current_piece.mySourceOffsetLists[GA_ATTRIB_POINT];
1421  const GA_OffsetList &piece_vertex_list = current_piece.mySourceOffsetLists[GA_ATTRIB_VERTEX];
1422  const GA_OffsetList &piece_prim_list = current_piece.mySourceOffsetLists[GA_ATTRIB_PRIMITIVE];
1423  exint piece_point_count = piece_point_list.size();
1424  exint piece_vertex_count = piece_vertex_list.size();
1425  exint piece_prim_count = piece_prim_list.size();
1426  UT_ASSERT_P(piece_vertex_count == current_piece.myRelVtxToPt.size());
1427  if (piece_point_count >= piece_vertex_count)
1428  {
1429  // If there is at least one point per vertex, there's a
1430  // decent chance that none of the source points are shared,
1431  // which can make building the primitives much faster,
1432  // so we check.
1433 
1434  hascontiguouspoints = current_piece.myRelVtxToPt.isTrivial();
1435  hassharedpoints = false;
1436 
1437  if (!hascontiguouspoints && piece_vertex_count > 0)
1438  {
1439  // TODO: Parallelize this.
1440  // Unlike in GUcreateGeometryFromSource,
1441  // we've already computed current_piece.myRelVtxToPt,
1442  // so we can just iterate through it here.
1443  exint last_point = current_piece.myRelVtxToPt[0];
1444  for (exint vtxi = 1; vtxi < piece_vertex_count; ++vtxi)
1445  {
1446  exint current_point = current_piece.myRelVtxToPt[vtxi];
1447 
1448  // This isn't a perfect check for sharing, but since
1449  // we don't want to do a full sort, we just check whether
1450  // the point offsets are strictly increasing.
1451  // If points are shared, this check will make hassharedpoints true.
1452  // If no points are shared, this can also end up being true sometimes.
1453  hassharedpoints |= (current_point <= last_point);
1454  if (hassharedpoints)
1455  break;
1456  last_point = current_point;
1457  }
1458  }
1459  }
1460  current_piece.myHasSharedPoints = hassharedpoints;
1461  current_piece.myHasContiguousPoints = hascontiguouspoints;
1462 
1463  GA_PolyCounts &vertexlistsizelist = current_piece.myVertexListSizeList;
1464  vertexlistsizelist.clear();
1465  auto &prim_type_count_pairs = current_piece.myPrimTypeCountPairs;
1466  prim_type_count_pairs.clear();
1467  UT_SmallArray<exint, 2*sizeof(exint)> &closed_span_lengths = current_piece.myClosedSpanLengths;
1468  closed_span_lengths.clear();
1469 
1470  if (piece_prim_count > 0)
1471  {
1472  GA_Offset primoff = piece_prim_list[0];
1473  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1474  exint vertex_count = vertices.size();
1475  vertexlistsizelist.append(vertex_count);
1476 
1477  int primtype = source->getPrimitiveTypeId(primoff);
1478  prim_type_count_pairs.append(std::make_pair(primtype,1));
1479 
1480  bool closed = vertices.getExtraFlag();
1481  if (closed)
1482  {
1483  // Index 0 (size 1) always represents open,
1484  // so we need an extra zero.
1485  closed_span_lengths.append(0);
1486  }
1487  closed_span_lengths.append(1);
1488 
1489  for (exint primi = 1; primi < piece_prim_count; ++primi)
1490  {
1491  GA_Offset primoff = piece_prim_list[primi];
1492  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1493  exint vertex_count = vertices.size();
1494  vertexlistsizelist.append(vertex_count);
1495 
1496  int primtype = source->getPrimitiveTypeId(primoff);
1497  if (prim_type_count_pairs.last().first == primtype)
1498  ++(prim_type_count_pairs.last().second);
1499  else
1500  prim_type_count_pairs.append(std::make_pair(primtype,1));
1501 
1502  bool closed = vertices.getExtraFlag();
1503  // Index 0 (size 1) always represents open, and so does every even index (odd size).
1504  // Every odd index (even size) always represents closed.
1505  // This condition checks if we're switching between open and closed.
1506  if ((closed_span_lengths.size()&1) == exint(closed))
1507  closed_span_lengths.append(1);
1508  else
1509  ++(closed_span_lengths.last());
1510  }
1511  }
1512  }
1513  });
1514 
1515  // Has shared points if any piece has shared points.
1516  // Has contiguous points only if all pieces have contiguous points.
1517  bool hassharedpoints = false;
1518  bool hascontiguouspoints = true;
1519  exint total_nvertices = 0;
1520  exint total_nprims = 0;
1521  for (exint piecei = 0; piecei < npieces; ++piecei)
1522  {
1523  PieceData &current_piece = piece_data[piecei];
1524  hassharedpoints |= current_piece.myHasSharedPoints;
1525  hascontiguouspoints &= current_piece.myHasContiguousPoints;
1526  exint piece_nvertices = current_piece.mySourceOffsetLists[GA_ATTRIB_VERTEX].size();
1527  exint piece_nprims = current_piece.mySourceOffsetLists[GA_ATTRIB_PRIMITIVE].size();
1528  total_nvertices += current_piece.myRefCount * piece_nvertices;
1529  total_nprims += current_piece.myRefCount * piece_nprims;
1530  }
1531 
1532  // Combine everything needed for GEObuildPrimitives
1533  // from each of the pieces.
1534  UT_SmallArray<exint> closed_span_lengths;
1535  if (total_nprims > 0)
1536  closed_span_lengths.append(0);
1537  UT_SmallArray<std::pair<int,exint>> prim_type_count_pairs;
1538  GA_PolyCounts vertexlistsizelist;
1539  UT_Array<exint> vertexpointnumbers;
1540  vertexpointnumbers.setSizeNoInit(hascontiguouspoints ? 0 : total_nvertices);
1541  UT_Array<exint> &piece_point_starts = sopcache->myPieceOffsetStarts[GA_ATTRIB_POINT];
1542  UT_Array<exint> &piece_vertex_starts = sopcache->myPieceOffsetStarts[GA_ATTRIB_VERTEX];
1543  UT_Array<exint> &piece_prim_starts = sopcache->myPieceOffsetStarts[GA_ATTRIB_PRIMITIVE];
1544  piece_point_starts.setSizeNoInit(num_target_points);
1545  piece_vertex_starts.setSizeNoInit(num_target_points);
1546  piece_prim_starts.setSizeNoInit(num_target_points);
1547  exint piece_point_start = 0;
1548  exint piece_vertex_start = 0;
1549  exint piece_prim_start = 0;
1550  for (exint targeti = 0; targeti < num_target_points; ++targeti)
1551  {
1552  exint piecei = target_to_piecei[targeti];
1553  PieceData &current_piece = piece_data[piecei];
1554 
1555  piece_point_starts[targeti] = piece_point_start;
1556  piece_vertex_starts[targeti] = piece_vertex_start;
1557  piece_prim_starts[targeti] = piece_prim_start;
1558 
1559  exint local_npoints = current_piece.mySourceOffsetLists[GA_ATTRIB_POINT].size();
1560  exint local_nvertices = current_piece.mySourceOffsetLists[GA_ATTRIB_VERTEX].size();
1561  exint local_nprims = current_piece.mySourceOffsetLists[GA_ATTRIB_PRIMITIVE].size();
1562 
1563  exint num_prim_type_count_pairs = current_piece.myPrimTypeCountPairs.size();
1564  if (num_prim_type_count_pairs > 0)
1565  {
1566  exint pairi = 0;
1567  if (prim_type_count_pairs.size() > 0 &&
1568  prim_type_count_pairs.last().first == current_piece.myPrimTypeCountPairs[0].first)
1569  {
1570  prim_type_count_pairs.last().second += current_piece.myPrimTypeCountPairs[0].second;
1571  ++pairi;
1572  }
1573  for (; pairi < num_prim_type_count_pairs; ++pairi)
1574  {
1575  prim_type_count_pairs.append(current_piece.myPrimTypeCountPairs[pairi]);
1576  }
1577 
1578  vertexlistsizelist.append(current_piece.myVertexListSizeList);
1579 
1580  if (!vertexpointnumbers.isEmpty())
1581  {
1582  for (exint i = 0; i < local_nvertices; ++i)
1583  {
1584  vertexpointnumbers[piece_vertex_start+i] = current_piece.myRelVtxToPt[i] + piece_point_start;
1585  }
1586  }
1587 
1588  auto &local_closed_span_lengths = current_piece.myClosedSpanLengths;
1589  exint spani = (local_closed_span_lengths[0] == 0);
1590  if (((closed_span_lengths.size()-1)&1) == (spani&1))
1591  {
1592  closed_span_lengths.last() += local_closed_span_lengths[spani];
1593  ++spani;
1594  }
1595  for (exint nspans = local_closed_span_lengths.size(); spani < nspans; ++spani)
1596  {
1597  closed_span_lengths.append(local_closed_span_lengths[spani]);
1598  }
1599  }
1600 
1601  piece_point_start += local_npoints;
1602  piece_vertex_start += local_nvertices;
1603  piece_prim_start += local_nprims;
1604  }
1605 
1606  exint total_npoints = piece_point_start;
1607  GA_Offset start_ptoff = output_geo->appendPointBlock(total_npoints);
1608 
1609  GA_Offset start_primoff = GEObuildPrimitives(
1610  output_geo,
1611  prim_type_count_pairs.getArray(),
1612  start_ptoff,
1613  total_npoints,
1614  vertexlistsizelist,
1615  vertexpointnumbers.getArray(),
1616  hassharedpoints,
1617  closed_span_lengths.getArray(),
1618  1);
1619 
1620  // No primitive data to copy if only polygons and tetrahedra,
1621  // since they have no member data outside of GA_Primitive,
1622  // and might be stored compressed in GA_PrimitiveList.
1623  exint num_polys_and_tets =
1624  output_geo->countPrimitiveType(GA_PRIMPOLY) +
1626  if (output_geo->getNumPrimitives() != num_polys_and_tets)
1627  {
1628  // Copy primitive subclass data for types other than polygons and tetrahedra.
1629  UT_BlockedRange<GA_Offset> primrange(start_primoff, start_primoff+output_geo->getNumPrimitives());
1630  auto &&functor = [output_geo,source,&piece_prim_starts,start_primoff,
1631  &target_to_piecei,&piece_data,num_target_points](const UT_BlockedRange<GA_Offset> &r)
1632  {
1633  exint output_primi = r.begin() - start_primoff;
1634  // Find the first entry in piece_prim_starts where the next entry is greater than output_primi,
1635  // (in other words, the last entry whose value is less than or equal to output_primi,
1636  // so output_primi is in that piece.) The -1 is to go back one.
1637  exint targeti = (std::upper_bound(
1638  piece_prim_starts.getArray(),
1639  piece_prim_starts.getArray()+piece_prim_starts.size(),
1640  output_primi) - piece_prim_starts.getArray()) - 1;
1641  UT_ASSERT_P(targeti >= 0 && targeti < num_target_points);
1642 
1643  exint piece_prim_start = piece_prim_starts[targeti];
1644  exint piece_primi = output_primi - piece_prim_start;
1645  exint piecei = target_to_piecei[targeti];
1646  PieceData *current_piece = &piece_data[piecei];
1647  const GA_OffsetList *piece_prim_list = &(current_piece->mySourceOffsetLists[GA_ATTRIB_PRIMITIVE]);
1648 
1649  for (GA_Offset dest_off = r.begin(), end = r.end(); dest_off < end; ++dest_off)
1650  {
1651  GA_Offset source_off = (*piece_prim_list)[piece_primi];
1652  const GA_Primitive *source_prim = source->getPrimitive(source_off);
1653  GA_Primitive *output_prim = output_geo->getPrimitive(dest_off);
1654  output_prim->copySubclassData(source_prim);
1655 
1656  ++piece_primi;
1657  // NOTE: This must be while instead of if, because there can be zero primitives in a piece.
1658  while (piece_primi >= piece_prim_list->size())
1659  {
1660  piece_primi = 0;
1661  ++targeti;
1662  if (targeti >= num_target_points)
1663  break;
1664  piecei = target_to_piecei[targeti];
1665  current_piece = &piece_data[piecei];
1666  piece_prim_list = &(current_piece->mySourceOffsetLists[GA_ATTRIB_PRIMITIVE]);
1667  }
1668  }
1669  };
1670  if (output_geo->getNumPrimitives() >= 1024)
1671  {
1672  UTparallelForLightItems(primrange, functor);
1673  }
1674  else
1675  {
1676  functor(primrange);
1677  }
1678  }
1679 
1680  sopcache->myPrevOutputDetailID = output_geo->getUniqueId();
1681  sopcache->myPrevTargetPtCount = num_target_points;
1682  sopcache->myPrevPack = false;
1683 
1684  sopcache->mySourceAttribDataIDs[GA_ATTRIB_VERTEX].clear();
1685  sopcache->mySourceAttribDataIDs[GA_ATTRIB_POINT].clear();
1686  sopcache->mySourceAttribDataIDs[GA_ATTRIB_PRIMITIVE].clear();
1687  sopcache->mySourceGroupDataIDs[GA_ATTRIB_VERTEX].clear();
1688  sopcache->mySourceGroupDataIDs[GA_ATTRIB_POINT].clear();
1689  sopcache->mySourceGroupDataIDs[GA_ATTRIB_PRIMITIVE].clear();
1690  sopcache->mySourceEdgeGroupDataIDs.clear();
1691  sopcache->myTargetAttribInfo.clear();
1692  sopcache->myTargetGroupInfo.clear();
1693  }
1694 
1695  // *** Attribute Setup ***
1696 
1697  if (!topology_changed)
1698  {
1700  output_geo,
1701  source,
1702  target,
1703  sopcache,
1704  &target_attrib_info,
1705  &target_group_info);
1706  }
1707 
1708  bool needed_transforms[NeededTransforms::num_needed_transforms];
1709  for (exint i = 0; i < NeededTransforms::num_needed_transforms; ++i)
1710  needed_transforms[i] = false;
1711 
1712  // Add attributes from source and target that are not in output_geo.
1713  exint num_source_attribs[3] = {0,0,0};
1714  exint num_target_attribs[3] = {0,0,0};
1716  "Arrays above are assuming the order of GA_AttributeOwner enum");
1717 
1719  output_geo,
1720  source,
1721  num_source_attribs,
1722  has_transform_matrices,
1723  needed_transforms,
1724  target,
1725  &target_attrib_info,
1726  &target_group_info,
1727  num_target_attribs);
1728 
1729  // *** Specific Transform Caches ***
1730 
1732  sopcache,
1733  num_target_points,
1734  transforms_changed,
1735  needed_transforms);
1736 
1737  // *** Source Attribute Copying ***
1738 
1739  GA_SplittableRange output_splittable_ranges[3] =
1740  {
1741  GA_SplittableRange(output_geo->getVertexRange()),
1742  GA_SplittableRange(output_geo->getPointRange()),
1743  GA_SplittableRange(output_geo->getPrimitiveRange())
1744  };
1746  "Arrays above and loop below are assuming the order of GA_AttributeOwner enum");
1747 
1748  if (!source->edgeGroups().isEmpty() && output_geo->getNumPrimitives() > 0)
1749  {
1750  cookparms.sopAddWarning(SOP_MESSAGE, "Edge groups aren't yet supported when Piece Attribute is enabled and Pack and Instance is disabled.");
1751  }
1752 
1754  output_geo,
1755  output_splittable_ranges,
1756  source,
1757  num_target_points,
1758  sopcache,
1759  sopcache->mySourceOffsetLists,
1760  num_source_attribs,
1761  false,
1762  had_transform_matrices,
1763  has_transform_matrices,
1764  topology_changed,
1765  transforms_changed,
1766  target,
1767  &target_attrib_info,
1768  &target_group_info,
1769  target_to_piecei.getArray(),
1770  sopcache->myPieceOffsetStarts,
1771  piece_data.getArray());
1772 
1773  // *** Target Attribute Copying ***
1774 
1776  output_geo,
1777  output_splittable_ranges,
1778  num_target_points,
1779  sopcache,
1780  0, // source_point_count
1781  0, // source_vertex_count
1782  0, // source_prim_count
1783  num_target_attribs,
1784  target_point_list,
1785  target,
1786  target_attrib_info,
1787  target_group_info,
1788  topology_changed,
1789  target_to_piecei.getArray(),
1790  sopcache->myPieceOffsetStarts,
1791  piece_data.getArray());
1792 
1793  if (topology_changed)
1794  {
1795  output_geo->bumpDataIdsForAddOrRemove(true, true, true);
1796  }
1797  }
1798 
1799  // Returning true means that we used the ID attribute.
1800  return true;
1801 }
1802 
1803 void
1805 {
1806  using namespace SOP_CopyToPointsHDKEnums;
1807  auto &&sopparms = cookparms.parms<SOP_CopyToPointsHDKParms>();
1808  auto sopcache = (SOP_CopyToPointsHDKCache *)cookparms.cache();
1809  GU_Detail *output_geo = cookparms.gdh().gdpNC();
1810  const GU_Detail *source = cookparms.inputGeo(0);
1811  const GU_Detail *target = cookparms.inputGeo(1);
1812 
1813  // *** Parse input groups ***
1814 
1815  GOP_Manager group_parser;
1816 
1817  const UT_StringHolder &source_groupname = sopparms.getSourceGroup();
1818  const GA_ElementGroup *source_group = nullptr;
1819  const GA_PrimitiveGroup *source_primgroup = nullptr;
1820  GA_PrimitiveGroupUPtr source_primgroup_deleter;
1821  const GA_PointGroup *source_pointgroup = nullptr;
1822  GA_PointGroupUPtr source_pointgroup_deleter;
1823  if (source_groupname.isstring())
1824  {
1825  SourceGroupType sourcegrouptype = sopparms.getSourceGroupType();
1826 
1827  bool ok = true;
1828  GA_GroupType gagrouptype = (sourcegrouptype == SourceGroupType::GUESS) ? GA_GROUP_INVALID :
1829  ((sourcegrouptype == SourceGroupType::PRIMS) ? GA_GROUP_PRIMITIVE : GA_GROUP_POINT);
1830  const GA_Group *temp_source_group =
1831  group_parser.parseGroupDetached(source_groupname, gagrouptype, source, true, true, ok);
1832 
1833  if (!ok)
1834  cookparms.sopAddWarning(SOP_ERR_BADGROUP, source_groupname);
1835  if (temp_source_group != nullptr)
1836  {
1837  gagrouptype = temp_source_group->classType();
1838  if (gagrouptype == GA_GROUP_PRIMITIVE)
1839  {
1840  source_primgroup = UTverify_cast<const GA_PrimitiveGroup*>(temp_source_group);
1841  source_group = source_primgroup;
1842 
1843  // Make sure the primitive group is unordered
1844  if (source_primgroup->isOrdered())
1845  {
1846  source_primgroup_deleter = UTmakeUnique<GA_PrimitiveGroup>(*source);
1847  source_primgroup_deleter->copyMembership(*source_primgroup, false);
1848  source_primgroup = source_primgroup_deleter.get();
1849  }
1850 
1851  // Get all points that are used by the primitives
1852  source_pointgroup_deleter = UTmakeUnique<GA_PointGroup>(*source);
1853  source_pointgroup_deleter->combine(source_primgroup);
1854  source_pointgroup_deleter->makeUnordered();
1855  source_pointgroup = source_pointgroup_deleter.get();
1856  }
1857  else if (gagrouptype == GA_GROUP_POINT)
1858  {
1859  source_pointgroup = UTverify_cast<const GA_PointGroup*>(temp_source_group);
1860  source_group = source_pointgroup;
1861 
1862  // We don't want to add other points, so we only
1863  // want primitives for which all points they use
1864  // are present in source_pointgroup.
1865  // First, get all primitives that are used by the points
1866  source_primgroup_deleter = UTmakeUnique<GA_PrimitiveGroup>(*source);
1867  source_primgroup_deleter->combine(source_group);
1868  source_primgroup_deleter->makeUnordered();
1869  source_primgroup = source_primgroup_deleter.get();
1870 
1871  if (!source_primgroup->isEmpty())
1872  {
1873  // Remove primitives for which not all points they use
1874  // are in source_group.
1875  GA_Offset start;
1876  GA_Offset end;
1877  for (GA_Iterator it(source->getPrimitiveRange(source_primgroup)); it.blockAdvance(start, end); )
1878  {
1879  for (GA_Offset primoff = start; primoff < end; ++primoff)
1880  {
1881  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1882  for (exint i = 0, n = vertices.size(); i < n; ++i)
1883  {
1884  GA_Offset ptoff = source->vertexPoint(vertices[i]);
1885  if (!source_pointgroup->contains(ptoff))
1886  {
1887  source_primgroup_deleter->removeOffset(primoff);
1888  break;
1889  }
1890  }
1891  }
1892  }
1893  }
1894  }
1895  }
1896  }
1897  notifyGroupParmListeners(cookparms.getNode(), 0, 1, source, source_group);
1898 
1899  GA_OffsetList source_prim_list;
1900  GA_OffsetList source_point_list;
1901  GUcreatePointOrPrimList(source_prim_list, source, source_primgroup, GA_ATTRIB_PRIMITIVE);
1902  GUcreatePointOrPrimList(source_point_list, source, source_pointgroup, GA_ATTRIB_POINT);
1903 
1904  const UT_StringHolder &target_groupname = sopparms.getTargetGroup();
1905  const GA_PointGroup *target_group = nullptr;
1906  if (target_groupname.isstring())
1907  {
1908  bool ok = true;
1909  // target_group can be ordered, (2nd true value).
1910  target_group = group_parser.parsePointDetached(target_groupname, target, true, true, ok);
1911 
1912  if (!ok)
1913  cookparms.sopAddWarning(SOP_ERR_BADGROUP, target_groupname);
1914  }
1915  notifyGroupParmListeners(cookparms.getNode(), 2, -1, target, target_group);
1916 
1917  GA_OffsetList target_point_list;
1918  if (!target_group)
1919  target_point_list = target->getPointMap().getOffsetFromIndexList();
1920  else
1921  {
1922  // Create list of points in target_group
1923  // TODO: Parallelize this if it's worthwhile.
1924  GA_Offset start;
1925  GA_Offset end;
1926  for (GA_Iterator it(target->getPointRange(target_group)); it.fullBlockAdvance(start, end); )
1927  {
1928  target_point_list.setTrivialRange(target_point_list.size(), start, end-start);
1929  }
1930  }
1931 
1932  // *** Primitives ***
1933 
1934  GA_DataId source_primlist_data_id = source->getPrimitiveList().getDataId();
1935  GA_DataId source_topology_data_id = source->getTopology().getDataId();
1936  bool source_topology_changed =
1937  source_primlist_data_id != sopcache->myPrevSourcePrimListDataID ||
1938  source_topology_data_id != sopcache->myPrevSourceTopologyDataID;
1939  if (!source_topology_changed)
1940  {
1941  source_topology_changed |= (source_group != nullptr) != (sopcache->myPrevHadSourceGroup);
1942  if (!source_topology_changed && (source_group != nullptr))
1943  {
1944  bool different_group = source_group->isDetached() ||
1945  (source_group->getDataId() != sopcache->myPrevSourceGroupDataID);
1946  if (different_group)
1947  {
1948  // Compare source group contents
1949  bool equal_group =
1950  (source_point_list.size() == sopcache->mySourceOffsetLists[GA_ATTRIB_POINT].size()) &&
1951  sopcache->mySourceOffsetLists[GA_ATTRIB_POINT].isEqual(source_point_list, 0, source_point_list.size());
1952  if (!equal_group)
1953  {
1954  source_topology_changed = true;
1955  }
1956  else
1957  {
1958  equal_group =
1959  (source_prim_list.size() == sopcache->mySourceOffsetLists[GA_ATTRIB_PRIMITIVE].size()) &&
1960  sopcache->mySourceOffsetLists[GA_ATTRIB_PRIMITIVE].isEqual(source_prim_list, 0, source_prim_list.size());
1961  if (!equal_group)
1962  {
1963  source_topology_changed = true;
1964  }
1965  }
1966  }
1967  }
1968  }
1969  sopcache->myPrevHadSourceGroup = (source_group != nullptr);
1970  sopcache->myPrevSourceGroupDataID = ((!source_group || source_group->isDetached()) ? GA_INVALID_DATAID : source_group->getDataId());
1971  sopcache->myPrevSourcePrimListDataID = source_primlist_data_id;
1972  sopcache->myPrevSourceTopologyDataID = source_topology_data_id;
1973  sopcache->mySourceOffsetLists[GA_ATTRIB_POINT] = std::move(source_point_list);
1974  sopcache->mySourceOffsetLists[GA_ATTRIB_PRIMITIVE] = std::move(source_prim_list);
1975 
1976  exint source_point_count = source_pointgroup ? source_pointgroup->entries() : source->getNumPoints();
1977  exint source_prim_count = source_primgroup ? source_primgroup->entries() : source->getNumPrimitives();
1978  exint source_vertex_count = 0;
1979  if (!source_primgroup)
1980  {
1981  source_vertex_count = source->getNumVertices();
1982  sopcache->mySourceVertexCount = source_vertex_count;
1983  }
1984  else if (source_primgroup->entries() > 0)
1985  {
1986  struct CountVerticesFunctor
1987  {
1988  CountVerticesFunctor(const GA_Detail &detail)
1989  : myDetail(detail)
1990  , myVertexCount(0)
1991  {}
1992  CountVerticesFunctor(const CountVerticesFunctor &that, UT_Split)
1993  : myDetail(that.myDetail)
1994  , myVertexCount(0)
1995  {}
1996  void operator()(const GA_SplittableRange &range)
1997  {
1998  GA_Size nvertices = 0;
1999  auto &primlist = myDetail.getPrimitiveList();
2000  GA_Offset start;
2001  GA_Offset end;
2002  for (GA_Iterator it(range); it.blockAdvance(start, end);)
2003  {
2004  GA_PageNum pagenum = GAgetPageNum(start);
2005  bool constant_page = primlist.isVertexListPageConstant(pagenum);
2006  if (constant_page)
2007  {
2008  nvertices += primlist.getConstantVertexListPage(pagenum).size() * (end-start);
2009  continue;
2010  }
2011  for (GA_Offset off = start; off < end; ++off)
2012  {
2013  nvertices += myDetail.getPrimitiveVertexCount(off);
2014  }
2015  }
2016  myVertexCount += nvertices;
2017  }
2018  void join(const CountVerticesFunctor &that)
2019  {
2020  myVertexCount += that.myVertexCount;
2021  }
2022  GA_Size getVertexCount() const
2023  {
2024  return myVertexCount;
2025  }
2026  const GA_Detail &myDetail;
2027  exint myVertexCount;
2028  };
2029  if (source_topology_changed)
2030  {
2031  CountVerticesFunctor functor(*source);
2032  UTparallelReduceLightItems(GA_SplittableRange(source->getPrimitiveRange(source_primgroup)), functor);
2033  source_vertex_count = functor.getVertexCount();
2034  sopcache->mySourceVertexCount = source_vertex_count;
2035  }
2036  else
2037  {
2038  source_vertex_count = sopcache->mySourceVertexCount;
2039  }
2040  }
2041 
2042  // *** Target Attribute Pattern Handling ***
2043 
2046  const UT_Array<SOP_CopyToPointsHDKParms::TargetAttribs> &target_attribs = sopparms.getTargetAttribs();
2047  target->pointAttribs().forEachAttribute([&target_attrib_info,&target_group_info,&target_attribs](const GA_Attribute *attrib)
2048  {
2049  GA_AttributeScope scope = attrib->getScope();
2050  // Never copy private attributes or internal groups.
2051  if (scope == GA_SCOPE_PRIVATE || (scope == GA_SCOPE_GROUP && UTverify_cast<const GA_ElementGroup *>(attrib)->isInternal()))
2052  return;
2053 
2054  const UT_StringHolder &attrib_name = attrib->getName();
2055  // We intentionally avoid copying P, since it's always part of the transform.
2056  if (scope == GA_SCOPE_PUBLIC && attrib_name == GA_Names::P)
2057  return;
2058 
2059  // This wrapper is just because the multiMatch function isn't available
2060  // without a UT_String. The .c_str() is so that it doesn't do a deep copy.
2061  const UT_String attrib_name_wrap(attrib_name.c_str());
2062 
2063  for (exint target_attribsi = 0, ntarget_attribs = target_attribs.size(); target_attribsi < ntarget_attribs; ++target_attribsi)
2064  {
2065  const SOP_CopyToPointsHDKParms::TargetAttribs &target_attrib_pattern = target_attribs[target_attribsi];
2066  if (!target_attrib_pattern.useapply)
2067  continue;
2068 
2069  const UT_StringHolder &attrib_pattern = target_attrib_pattern.applyattribs;
2070  if (!attrib_name_wrap.multiMatch(attrib_pattern))
2071  continue;
2072 
2073  // Keep whichever pattern matches last.
2074 
2075  GA_AttributeOwner output_owner;
2076  if (target_attrib_pattern.applyto == 0)
2077  output_owner = GA_ATTRIB_POINT;
2078  else if (target_attrib_pattern.applyto == 1)
2079  output_owner = GA_ATTRIB_VERTEX;
2080  else // target_attrib_pattern.applyto == 2
2081  output_owner = GA_ATTRIB_PRIMITIVE;
2082 
2083  sop_AttribCombineMethod method = sop_AttribCombineMethod(target_attrib_pattern.applymethod);
2084 
2085  if (method == sop_AttribCombineMethod::NONE)
2086  {
2087  // Remove any existing.
2088  if (scope == GA_SCOPE_GROUP)
2089  target_group_info.erase(attrib_name);
2090  else
2091  target_attrib_info.erase(attrib_name);
2092  continue;
2093  }
2094 
2096  (scope == GA_SCOPE_GROUP) ?
2097  target_group_info[attrib_name] :
2098  target_attrib_info[attrib_name];
2099 
2100  info.myDataID = attrib->getDataId();
2101  info.myCopyTo = output_owner;
2102  info.myCombineMethod = method;
2103 
2104  // Only numeric or group types can multiply (intersect), add (union), or subtract
2105  if (!GA_ATINumeric::isType(attrib) && !GA_ElementGroup::isType(attrib) &&
2106  method != sop_AttribCombineMethod::COPY)
2107  {
2108  method = sop_AttribCombineMethod::COPY;
2109  }
2110  }
2111  });
2112 
2113  // Check for ID attribute case
2114  if (sopparms.getUseIDAttrib() && sopparms.getIDAttrib().isstring())
2115  {
2116  bool used_idattrib = sopCopyByIDAttrib(
2117  cookparms,
2118  sopparms,
2119  sopcache,
2120  output_geo,
2121  source,
2122  source_primgroup,
2123  source_pointgroup,
2124  source_topology_changed,
2125  target,
2126  target_point_list,
2127  target_group,
2128  target_attrib_info,
2129  target_group_info);
2130 
2131  if (used_idattrib)
2132  return;
2133 
2134  // ID attribute wasn't valid, so fall through.
2135  }
2136 
2137  // Not using an ID attribute, so if we previously used an ID attribute,
2138  // the topology must be marked as changed.
2139  if (GAisValid(sopcache->myTargetIDAttribDataID))
2140  {
2141  // These settings should be sufficient to make everything below
2142  // recook enough from scratch. Transforms from target points
2143  // may still be the same.
2144  source_topology_changed = true;
2145  sopcache->myPrevOutputDetailID = -1;
2146  output_geo->clearAndDestroy();
2147 
2148  // Clear the ID attribute related caches.
2149  sopcache->myPieceData.clear();
2150  sopcache->myTargetToPiece.clear();
2151  sopcache->myTargetIDAttribDataID = GA_INVALID_DATAID;
2152  sopcache->mySourceIDAttribOwner = GA_ATTRIB_INVALID;
2153  sopcache->mySourceIDAttribDataID = GA_INVALID_DATAID;
2154  sopcache->myPieceOffsetStarts[GA_ATTRIB_VERTEX].setCapacity(0);
2155  sopcache->myPieceOffsetStarts[GA_ATTRIB_POINT].setCapacity(0);
2156  sopcache->myPieceOffsetStarts[GA_ATTRIB_PRIMITIVE].setCapacity(0);
2157  }
2158 
2159  // We must check for the target group changing *after* handling the ID
2160  // attribute case, since the ID attribute case needs to take into account
2161  // target points that don't match anything in the source.
2162  bool target_group_changed =
2163  (target_group != nullptr) != (sopcache->myPrevHadTargetGroup) ||
2164  (target_point_list.size() != sopcache->myPrevTargetPtCount);
2165  if (!target_group_changed && (target_group!= nullptr))
2166  {
2167  // For named groups, we don't need to the contents if the data ID is the same.
2168  bool different_group = target_group->isDetached() ||
2169  (target_group->getDataId() != sopcache->myPrevTargetGroupDataID);
2170  if (different_group)
2171  {
2172  UT_ASSERT(sopcache->myTargetOffsetList.size() == target_point_list.size());
2173  bool equal_group = sopcache->myTargetOffsetList.isEqual(target_point_list, 0, target_point_list.size());
2174  if (!equal_group)
2175  {
2176  target_group_changed = true;
2177  }
2178  }
2179  }
2180  sopcache->myTargetOffsetList = target_point_list;
2181  sopcache->myPrevHadTargetGroup = (target_group != nullptr);
2182  sopcache->myPrevTargetGroupDataID = ((!target_group || target_group->isDetached()) ? GA_INVALID_DATAID : target_group->getDataId());
2183 
2184  // *** Transform Setup ***
2185 
2186  const bool had_transform_matrices = (sopcache->myTransformMatrices3D.get() != nullptr);
2187 
2188  // NOTE: Transforms have changed if the target group has changed,
2189  // even if the number of points is the same.
2190  bool transforms_changed = target_group_changed;
2191  GUsetupPointTransforms(sopcache, target_point_list, target, sopparms.getTransform(), sopparms.getUseImplicitN(), transforms_changed);
2192 
2193  const bool has_transform_matrices = (sopcache->myTransformMatrices3D.get() != nullptr);
2194 
2195  GA_Size num_target_points = target_point_list.size();
2196 
2197  if (sopparms.getPack())
2198  {
2199  const GEO_ViewportLOD lod = sopViewportLODFromParam(sopparms.getViewportLOD());
2200  const GU_CopyToPointsCache::PackedPivot pivot_type = sopCachePivotType(sopparms.getPivot());
2201 
2203  output_geo,
2204  lod,
2205  pivot_type,
2206  sopcache,
2207  cookparms.inputGeoHandle(0),
2208  source,
2209  source_pointgroup,
2210  source_primgroup,
2211  source_topology_changed,
2212  had_transform_matrices,
2213  transforms_changed,
2214  num_target_points,
2215  target,
2216  &target_point_list,
2217  &target_attrib_info,
2218  &target_group_info);
2219 
2220  // No attributes from source in output_geo, so we can clear the source data ID maps.
2221  sopcache->mySourceAttribDataIDs[GA_ATTRIB_VERTEX].clear();
2222  sopcache->mySourceAttribDataIDs[GA_ATTRIB_POINT].clear();
2223  sopcache->mySourceAttribDataIDs[GA_ATTRIB_PRIMITIVE].clear();
2224  sopcache->mySourceGroupDataIDs[GA_ATTRIB_VERTEX].clear();
2225  sopcache->mySourceGroupDataIDs[GA_ATTRIB_POINT].clear();
2226  sopcache->mySourceGroupDataIDs[GA_ATTRIB_PRIMITIVE].clear();
2227  sopcache->mySourceEdgeGroupDataIDs.clear();
2228 
2229  return;
2230  }
2231 
2232  // If anything that could result in different topology has changed,
2233  // clear the output detail, determine what to copy, and copy it.
2234  exint output_detail_id = output_geo->getUniqueId();
2235  bool topology_changed =
2236  (output_detail_id != sopcache->myPrevOutputDetailID) ||
2237  source_topology_changed ||
2238  (num_target_points != sopcache->myPrevTargetPtCount) ||
2239  sopcache->myPrevPack;
2240  if (topology_changed)
2241  {
2242  output_geo->clearAndDestroy();
2243 
2245  output_geo,
2246  source,
2247  source_point_count,
2248  source_vertex_count,
2249  source_prim_count,
2250  sopcache->mySourceOffsetLists[GA_ATTRIB_POINT],
2251  sopcache->mySourceOffsetLists[GA_ATTRIB_VERTEX],
2252  sopcache->mySourceOffsetLists[GA_ATTRIB_PRIMITIVE],
2253  source_pointgroup,
2254  source_primgroup,
2255  num_target_points);
2256 
2257  sopcache->myPrevOutputDetailID = output_detail_id;
2258  sopcache->myPrevTargetPtCount = num_target_points;
2259  sopcache->myPrevPack = false;
2260 
2261  sopcache->mySourceAttribDataIDs[GA_ATTRIB_VERTEX].clear();
2262  sopcache->mySourceAttribDataIDs[GA_ATTRIB_POINT].clear();
2263  sopcache->mySourceAttribDataIDs[GA_ATTRIB_PRIMITIVE].clear();
2264  sopcache->mySourceGroupDataIDs[GA_ATTRIB_VERTEX].clear();
2265  sopcache->mySourceGroupDataIDs[GA_ATTRIB_POINT].clear();
2266  sopcache->mySourceGroupDataIDs[GA_ATTRIB_PRIMITIVE].clear();
2267  sopcache->mySourceEdgeGroupDataIDs.clear();
2268  sopcache->myTargetAttribInfo.clear();
2269  sopcache->myTargetGroupInfo.clear();
2270  } // topology_changed
2271 
2272  // *** Attribute Setup ***
2273 
2274  // This gets complicated, with attributes from both source and target.
2275  // For attributes in output,
2276  // if not being applied from target,
2277  // if not in source or mismatches storage from source,
2278  // delete it.
2279  // else if being copied from target,
2280  // if mismatches storage from target,
2281  // delete it.
2282  // else // add/sub/mul,
2283  // if not in source, (copy/negate from target)
2284  // if mismatches storage from target,
2285  // delete it.
2286  // else if mismatches storage from source,
2287  // delete it.
2288  // For attributes in source,
2289  // if not being applied from target, or add/sub/mul,
2290  // if not in output,
2291  // clone from source.
2292  // else,
2293  // copy non-storage metadata from source.
2294  // For attributes being applied from target,
2295  // if not in source, or being copied from target
2296  // if not in output,
2297  // clone from target.
2298  // else,
2299  // copy non-storage metadata from target.
2300  // For attributes in source,
2301  // if not being applied from target,
2302  // if source data ID changed or transforming changed,
2303  // recopy.
2304  // if add/sub/mul with target,
2305  // if source data ID changed or transforming changed or target data ID changed or target apply method changed or target owner changed,
2306  // recopy.
2307  // Invalidate any cached target data ID for the attribute, so that we don't need to check source data IDs below.
2308  // For attributes being applied from target,
2309  // if target data ID changed or target apply method changed or target owner changed,
2310  // apply operation.
2311 
2312  if (!topology_changed)
2313  {
2315  output_geo,
2316  source,
2317  target,
2318  sopcache,
2319  &target_attrib_info,
2320  &target_group_info);
2321  }
2322 
2323  bool needed_transforms[NeededTransforms::num_needed_transforms];
2324  for (exint i = 0; i < NeededTransforms::num_needed_transforms; ++i)
2325  needed_transforms[i] = false;
2326 
2327  // Add attributes from source and target that are not in output_geo.
2328  exint num_source_attribs[3] = {0,0,0};
2329  exint num_target_attribs[3] = {0,0,0};
2331  "Arrays above are assuming the order of GA_AttributeOwner enum");
2332 
2334  output_geo,
2335  source,
2336  num_source_attribs,
2337  has_transform_matrices,
2338  needed_transforms,
2339  target,
2340  &target_attrib_info,
2341  &target_group_info,
2342  num_target_attribs);
2343 
2344  // *** Specific Transform Caches ***
2345 
2347  sopcache,
2348  num_target_points,
2349  transforms_changed,
2350  needed_transforms);
2351 
2352  // *** Source Attribute Copying ***
2353 
2354  GA_SplittableRange output_splittable_ranges[3] =
2355  {
2356  GA_SplittableRange(output_geo->getVertexRange()),
2357  GA_SplittableRange(output_geo->getPointRange()),
2358  GA_SplittableRange(output_geo->getPrimitiveRange())
2359  };
2361  "Arrays above and loop below are assuming the order of GA_AttributeOwner enum");
2362 
2364  output_geo,
2365  output_splittable_ranges,
2366  source,
2367  num_target_points,
2368  sopcache,
2369  sopcache->mySourceOffsetLists,
2370  num_source_attribs,
2371  false,
2372  had_transform_matrices,
2373  has_transform_matrices,
2374  topology_changed,
2375  transforms_changed,
2376  target,
2377  &target_attrib_info,
2378  &target_group_info);
2379 
2380  // *** Target Attribute Copying ***
2381 
2383  output_geo,
2384  output_splittable_ranges,
2385  num_target_points,
2386  sopcache,
2387  source_point_count,
2388  source_vertex_count,
2389  source_prim_count,
2390  num_target_attribs,
2391  target_point_list,
2392  target,
2393  target_attrib_info,
2394  target_group_info,
2395  topology_changed);
2396 
2397  if (topology_changed)
2398  {
2399  output_geo->bumpDataIdsForAddOrRemove(true, true, true);
2400  }
2401 }
2402 
2403 const char *
2405 {
2406  switch (idx)
2407  {
2408  case 0: return "Geometry to Copy";
2409  case 1: return "Target Points to Copy to";
2410  default: break;
2411  }
2412  return "Invalid Source";
2413 }
2414 
2415 int
2417 {
2418  return (i == 1); // second input
2419 }
2420 
2421 } // End of HDK_Sample namespace
static PRM_ChoiceList primGroupMenu
Definition: SOP_Node.h:1189
SYS_FORCE_INLINE const GU_PackedImpl * sharedImplementation() const
static SYS_FORCE_INLINE bool isType(const GA_Attribute *attrib)
T & last()
Definition: UT_Array.h:796
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
SYS_FORCE_INLINE void forEachAttribute(FUNCTOR &&functor) const
Definition of a geometry attribute.
Definition: GA_Attribute.h:198
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:2540
SYS_FORCE_INLINE GA_Offset getPrimitiveVertexOffset(GA_Offset primoff, GA_Size i) const
Definition: GA_Primitive.h:900
SYS_FORCE_INLINE GA_Primitive * getPrimitive(GA_Offset prim_off)
Definition: GA_Detail.h:429
GU_API void GUcreatePointOrPrimList(GA_OffsetList &offset_list, const GU_Detail *const detail, const GA_ElementGroup *const group, const GA_AttributeOwner owner)
Definition: GU_Copy2.C:1897
GLenum GLint * range
Definition: glcorearb.h:1925
SYS_FORCE_INLINE const GA_AttributeDict & pointAttribs() const
Definition: GEO_Detail.h:1939
SOP_Node * getNode() const
Definition: SOP_NodeVerb.h:347
bool copyMembership(const GA_ATIGroupBool &src, bool copy_ordering=true)
int isRefInput(unsigned int i) const
FromType append(ToType value)
Add a single entry (may grow array)
GA_Range getVertexRange(const GA_VertexGroup *group=0) const
Get a range of all vertices in the detail.
Definition: GA_Detail.h:1760
virtual OP_ERROR error()
tbb::split UT_Split
Definition: GA_PolyCounts.h:24
OP_ERROR cookInputGroups(OP_Context &context, int alone=0) override
Iteration over a range of elements.
Definition: GA_Iterator.h:29
FromType find(ToType value, FromType s=FromType(0)) const
static SYS_FORCE_INLINE bool isType(const GA_Attribute *attrib)
Definition: GA_ATINumeric.h:60
void setChoiceListPtr(const UT_StringRef &name, PRM_ChoiceList *list)
UT_ErrorSeverity sopAddWarning(int code, const char *msg=0, const UT_SourceLocation *loc=0) const
Definition: SOP_NodeVerb.h:498
bool contains(const Key &key) const
Definition: UT_ArrayMap.h:334
static const SOP_NodeVerb::Register< SOP_CopyToPointsHDKVerb > theVerb
SYS_FORCE_INLINE int getPrimitiveTypeId(GA_Offset primoff) const
Definition: GA_Primitive.h:907
#define SYS_STATIC_ASSERT_MSG(expr, msg)
OP_ERROR lock(OP_Context &context)
Locks all inputs.
GLboolean * data
Definition: glcorearb.h:131
int64 GA_DataId
Definition: GA_Types.h:687
void UTparallelForEachNumber(IntType nitems, const Body &body, const bool force_use_task_scope=true)
void bind(const GA_Detail *gdp, GA_AttributeOwner owner, const UT_StringRef &name, int minsize=1)
virtual void copySubclassData(const GA_Primitive *source)
Definition: GA_Primitive.h:508
bool blockAdvance(GA_Offset &start, GA_Offset &end)
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
bool GAisValid(GA_Size v)
Definition: GA_Types.h:649
GA_Size entries() const overridefinal
Will return the number of primary elements.
void setSizeNoInit(exint newsize)
Definition: UT_Array.h:695
void clearAndDestroy()
Clear all the points/primitives out of this detail.
Definition: GEO_Detail.h:267
static const char *const theDsFile
This is the parameter interface string, below.
**And then you can **find out if it s done
Definition: thread.h:622
SYS_FORCE_INLINE bool getExtraFlag() const
Synonym for isClosed()
int64 exint
Definition: SYS_Types.h:125
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:906
GA_Attribute * getP()
Convenience method to access the P attribute.
Definition: GA_Detail.h:164
SYS_FORCE_INLINE const HOLDER & get(GA_Offset off, int comp=0) const
Get the string at the given offset.
Definition: GA_Handle.h:911
SYS_FORCE_INLINE const char * buffer() const
UT_ErrorSeverity
Definition: UT_Error.h:25
SYS_FORCE_INLINE GA_AttributeScope getScope() const
Definition: GA_Attribute.h:212
void setTrivialRange(FromType startindex, ToType startvalue, GA_Size nelements)
SOP_NodeCache * allocCache() const override
PRM_ChoiceListType
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
GA_API const UT_StringHolder P
#define UT_ASSERT_MSG_P(ZZ,...)
Definition: UT_Assert.h:158
bool addOperator(OP_Operator *op, std::ostream *err=nullptr)
GU_API void GUcreateVertexListAndGeometryFromSource(GU_Detail *output_geo, const GU_Detail *const source, const exint source_point_count, const exint source_vertex_count, const exint source_prim_count, const GA_OffsetList &source_point_list_cache, GA_OffsetList &source_vertex_list_cache, const GA_OffsetList &source_prim_list_cache, const GA_PointGroup *const source_pointgroup, const GA_PrimitiveGroup *const source_primgroup, const exint ncopies)
Definition: GU_Copy2.C:1921
Standard user attribute level.
Definition: GA_Types.h:148
#define GA_INVALID_DATAID
Definition: GA_Types.h:688
ordered_iterator_t< true > ordered_begin(const COMPARATOR &comparator) const
Definition: UT_ArrayMap.h:660
bool getBoundsCached(UT_BoundingBox &box) const
static PRM_ChoiceList pointGroupMenu
Definition: SOP_Node.h:1190
GU_API void GUsetupPointTransforms(GU_PointTransformCache *cache, const GA_OffsetListRef &target_point_list, const GU_Detail *target, const bool transform_using_more_than_P, const bool allow_implicit_N, bool &transforms_changed)
exint size() const
Definition: UT_Array.h:646
void setSize(exint newsize)
Definition: UT_Array.h:666
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:235
#define GA_INVALID_OFFSET
Definition: GA_Types.h:678
GA_Size countPrimitiveType(const GA_PrimitiveTypeId &type) const
Definition: GA_Detail.h:2291
Geometry Embedded procedural.
A range of elements in an index-map.
Definition: GA_Range.h:42
SYS_FORCE_INLINE const UT_StringHolder & getName() const
Definition: GA_Attribute.h:283
void setViewportLOD(GEO_ViewportLOD vlod)
bool combine(const GA_Group *src) overridefinal
void set(FromType index, ToType value)
Set the index to the value.
void allocateAndSet(GU_Detail *gdp, bool own=true)
GA_Size GA_Offset
Definition: GA_Types.h:641
GA_Offset GEObuildPrimitives(GEO_Detail *detail, const std::pair< int, exint > *primtype_count_pairs, const GA_Offset init_startpt, const GA_Size npoints_per_copy, const GA_PolyCounts &vertexlistsizelist, const INT_T *vertexpointnumbers, const bool hassharedpoints, const exint *closed_span_lengths, const exint ncopies)
GU_API void GUcreateEmptyPackedGeometryPrims(GU_Detail *const output_geo, const exint num_packed_prims)
Definition: GU_Copy2.C:1978
GA_AttributeScope
Definition: GA_Types.h:142
GU_ConstDetailHandle inputGeoHandle(exint idx) const
Definition: SOP_NodeVerb.h:381
SYS_FORCE_INLINE bool isTrivial() const
const T & parms() const
Definition: SOP_NodeVerb.h:412
GLdouble n
Definition: glcorearb.h:2008
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
GU_API void GUcreateGeometryFromSource(GU_Detail *output_geo, const GU_Detail *const source, const GA_OffsetList &source_point_list_cache, const GA_OffsetList &source_vertex_list_cache, const GA_OffsetList &source_prim_list_cache, const exint ncopies)
NOTE: This does not clear output_geo.
Definition: GU_Copy2.C:1689
bool isEqual(const GA_ListTypeRef &other, FromType start, FromType end) const
Test a sub-block for equality with another list.
Constructs a PRM_Template list from an embedded .ds file or an istream.
void bind(const GA_Detail *gdp, GA_AttributeOwner owner, const UT_StringRef &name, int minsize=1)
UT_Vector3T< T > center() const
SYS_FORCE_INLINE GA_OffsetListRef getVertexList(GA_Offset primoff) const
Definition: GA_Primitive.h:928
SYS_FORCE_INLINE GA_Offset appendPointBlock(GA_Size npoints)
Append new points, returning the first offset of the contiguous block.
Definition: GA_Detail.h:330
const GU_Detail * inputGeo(int index, OP_Context &)
Definition: SOP_Node.h:1147
GOP_Manager::GroupCreator GroupCreator
Typedef to help make use of GroupCreator less verbose.
Definition: SOP_Node.h:564
void bumpDataId()
Use this to mark primitives or their intrinsic data as dirty.
GA_StorageClass getStorageClass() const
Returns the approximate type of the attribute.
PRM_Template * templates() const
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:155
CookMode cookMode(const SOP_NodeParms *parms) const override
static SYS_FORCE_INLINE GA_ATINumeric * cast(GA_Attribute *attrib)
Definition: GA_ATINumeric.h:65
SYS_FORCE_INLINE GA_OffsetListRef getPrimitiveVertexList(GA_Offset primoff) const
Definition: GA_Primitive.h:886
GLuint GLuint end
Definition: glcorearb.h:475
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
OP_ERROR cookMyselfAsVerb(OP_Context &context)
GU_API void GUaddAttributesFromSourceOrTarget(GU_Detail *output_geo, const GU_Detail *source, exint *num_source_attribs=nullptr, bool has_transform_matrices=false, bool *needed_transforms=nullptr, const GU_Detail *target=nullptr, GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info=nullptr, GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info=nullptr, exint *num_target_attribs=nullptr)
Definition: GU_Copy2.C:457
SYS_FORCE_INLINE GA_Offset pointVertex(GA_Offset point) const
Definition: GA_Detail.h:559
UT_UniquePtr< GA_PrimitiveGroup > GA_PrimitiveGroupUPtr
SYS_FORCE_INLINE GA_Offset getNumPointOffsets() const
Definition: GA_Detail.h:341
SOP_NodeFlags mySopFlags
Definition: SOP_Node.h:1625
OP_ERROR cookInputPointGroups(OP_Context &context, const GA_PointGroup *&group, bool alone=false, bool do_selection=true, int parm_index=0, int group_type_index=-1, bool allow_reference=true, bool ordered=false, bool detached=true, int input_index=0)
See cookInputPrimitiveGroups.
GLenum target
Definition: glcorearb.h:1667
size_type erase(const Key &key)
Definition: UT_ArrayMap.h:550
SOP_NodeParms * allocParms() const override
SYS_FORCE_INLINE const char * c_str() const
static SYS_FORCE_INLINE GA_ATIString * cast(GA_Attribute *attrib)
Definition: GA_ATIString.h:57
void setCallback(const UT_StringRef &name, PRM_Callback callback)
virtual void setPivot(const UT_Vector3 &pos)
SYS_FORCE_INLINE T get(GA_Offset off, int comp=0) const
Definition: GA_Handle.h:203
SYS_FORCE_INLINE GA_Offset vertexPoint(GA_Offset vertex) const
Given a vertex, return the point it references.
Definition: GA_Detail.h:529
OP_ERROR cookMySop(OP_Context &context) override
bool isOrdered() const overridefinal
Returns true if the group is currently ordered.
fpreal CHgetEvalTime()
Definition: CH_Manager.h:2097
GLuint const GLchar * name
Definition: glcorearb.h:786
SYS_FORCE_INLINE GA_DataId getDataId() const
Definition: GA_Attribute.h:299
static PRM_Template * buildTemplates()
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:635
bool contains(const GA_Primitive *prim) const
GU_Detail * gdpNC()
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void setManagesDataIDs(bool onOff)
Definition: SOP_NodeFlags.h:36
exint append()
Definition: UT_Array.h:142
SYS_FORCE_INLINE GA_Offset vertexToNextVertex(GA_Offset vtx) const
Definition: GA_Detail.h:571
SYS_FORCE_INLINE bool isPageConstant(GA_PageNum pagenum) const
GA_GroupType classType() const
Definition: GA_Group.h:54
void newSopOperator(OP_OperatorTable *table)
GLdouble t
Definition: glad.h:2397
exint getUniqueId() const
Definition: GA_Detail.h:117
A list of primitives.
SYS_FORCE_INLINE const GA_Attribute * findPrimitiveAttribute(GA_AttributeScope s, const UT_StringRef &name) const
Definition: GA_Detail.h:1048
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:187
void notifyGroupParmListeners(SOP_Node *oldsop, int groupparm_idx, int grouptype_idx, const GU_Detail *gdp, const GA_Group *group) const
GU_API void GUcomputeTransformTypeCaches(GU_PointTransformCache *cache, exint num_target_points, bool transforms_changed, const bool needed_transforms[NeededTransforms::num_needed_transforms])
void assign(T xx=0.0f, T yy=0.0f, T zz=0.0f)
Set the values of the vector components.
Definition: UT_Vector3.h:694
GU_API void GUremoveUnnecessaryAttribs(GU_Detail *output_geo, const GU_Detail *source, const GU_Detail *target, GU_CopyToPointsCache *cache, const GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info, const GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info)
GLsizeiptr size
Definition: glcorearb.h:664
SYS_FORCE_INLINE GA_Offset vertexPrimitive(GA_Offset vertex) const
Definition: GA_Detail.h:545
GA_AttributeOwner
Definition: GA_Types.h:34
int int appendSprintf(const char *fmt,...) SYS_PRINTF_CHECK_ATTRIBUTE(2
OP_ERROR cookInputAllGroups(OP_Context &context, const GA_Group *&group, bool alone=false, bool do_selection=true, int parm_index=0, int group_type_index=-1, GA_GroupType grouptype=GA_GROUP_INVALID, bool allow_reference=true, bool is_default_prim=true, bool ordered=false, bool detached=true, int input_index=0)
void intrusive_ptr_add_ref(T *x)
Definition: refcnt.h:208
GU_API void GUcopyAttributesFromSource(GU_Detail *const output_geo, const GA_SplittableRange *const output_splittable_ranges, const GU_Detail *const source, const exint num_target_points, GU_CopyToPointsCache *const cache, const GA_OffsetList *const source_offset_lists, const exint *const num_source_attribs, const bool no_transforms, const bool had_transform_matrices, const bool has_transform_matrices, const bool topology_changed, const bool transforms_changed, const GU_Detail *const target=nullptr, const GU_CopyToPointsCache::TargetAttribInfoMap *const target_attrib_info=nullptr, const GU_CopyToPointsCache::TargetAttribInfoMap *const target_group_info=nullptr, const exint *const target_to_piecei=nullptr, const UT_Array< exint > *const owner_piece_offset_starts=nullptr, const GU_CopyToPointsCache::PieceData *const piece_data=nullptr, UT_Interrupt *boss=nullptr)
SYS_FORCE_INLINE GA_Offset getNumPrimitiveOffsets() const
Definition: GA_Detail.h:415
GEO_ViewportLOD
SYS_FORCE_INLINE bool isValid() const
Check whether the bounding box contains at least one point.
UT_StringHolder name() const override
fpreal64 fpreal
Definition: SYS_Types.h:277
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
static OP_Node * myConstructor(OP_Network *net, const char *name, OP_Operator *entry)
GU_API void GUcopyPackAllSame(GU_Detail *output_geo, const GEO_ViewportLOD lod, const GU_CopyToPointsCache::PackedPivot pivot_type, GU_CopyToPointsCache *cache, const GU_ConstDetailHandle source_handle, const GU_Detail *source, const GA_PointGroup *source_pointgroup, const GA_PrimitiveGroup *source_primgroup, bool source_topology_changed, bool had_transform_matrices, bool transforms_changed, const exint num_packed_prims, const GU_Detail *target=nullptr, const GA_OffsetListRef *target_point_list=nullptr, GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info=nullptr, GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info=nullptr)
GA_Size GA_PageNum
Definition: GA_Types.h:644
void append(GA_Size size, GA_Size count=1)
GA_GroupType
An ordinal enum for the different types of groups in GA.
Definition: GA_Types.h:160
GLuint index
Definition: glcorearb.h:786
SYS_FORCE_INLINE GA_Size getNumPrimitives() const
Return the number of primitives.
Definition: GA_Detail.h:408
iterator erase(iterator pos)
GEO_API const char * GEOviewportLOD(GEO_ViewportLOD type, bool label=false)
int64 getMetaCacheCount() const
Definition: GA_Detail.h:2370
GA_API const UT_StringHolder pivot
if(num_boxed_items<=0)
Definition: UT_RTreeImpl.h:697
const GU_Detail * inputGeo(exint idx) const
Definition: SOP_NodeVerb.h:376
const GA_PrimitiveList & getPrimitiveList() const
Definition: GA_Detail.h:792
SOP_NodeCache * cache() const
Definition: SOP_NodeVerb.h:417
void setDetailPtr(const GU_DetailHandle &d)
bool isDetached() const
Definition: GA_Attribute.h:445
SYS_FORCE_INLINE GA_AttributeOwner getOwner() const
Definition: GA_Attribute.h:210
Container class for all geometry.
Definition: GA_Detail.h:96
GU_API void GUhandleTargetAttribsForPackedPrims(GU_Detail *output_geo, GU_CopyToPointsCache *cache, const bool topology_changed, const bool had_transform_matrices, const GU_Detail *const target, const GA_OffsetListRef &target_point_list, GU_CopyToPointsCache::TargetAttribInfoMap &target_attrib_info, GU_CopyToPointsCache::TargetAttribInfoMap &target_group_info, const UT_Vector3 *const constant_pivot)
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
Definition: core.h:1131
SYS_FORCE_INLINE GA_Size getPrimitiveVertexCount(GA_Offset primoff) const
Definition: GA_Primitive.h:893
exint evalInt(int pi, int vi, fpreal t) const
GLboolean r
Definition: glcorearb.h:1222
UT_UniquePtr< GA_PointGroup > GA_PointGroupUPtr
SYS_FORCE_INLINE bool isEmpty() const
Query whether the group is empty of primary elements.
void cook(const CookParms &cookparms) const override
Compute the output geometry.
void clear()
Resets list to an empty list.
Definition: UT_Array.h:716
SYS_FORCE_INLINE void setImplementation(const GU_PackedImpl *impl, bool add_ref=true, bool remove_ref=true)
SYS_FORCE_INLINE bool isPageConstant(GA_PageNum pagenum) const
Definition: GA_ATIString.h:397
An array of bits.
SYS_FORCE_INLINE const GA_Attribute * findPointAttribute(GA_AttributeScope s, const UT_StringRef &name) const
Definition: GA_Detail.h:1034
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1733
UT_ArrayStringMap< TargetAttribInfo > TargetAttribInfoMap
Definition: GU_Copy2.h:179
const SOP_NodeVerb * cookVerb() const override
void bumpDataIdsForAddOrRemove(bool added_or_removed_points, bool added_or_removed_vertices, bool added_or_removed_primitives)
Declare prior to use.
T * getArray() const
Definition: UT_Array.h:816
GLint lod
Definition: glcorearb.h:2765
GU_API void GUcopyAttributesFromTarget(GU_Detail *const output_geo, const GA_SplittableRange *const output_splittable_ranges, const exint ncopies, GU_CopyToPointsCache *const cache, const exint source_point_count, const exint source_vertex_count, const exint source_prim_count, const exint *const num_target_attribs, const GA_OffsetListRef &target_point_list, const GU_Detail *const target, GU_CopyToPointsCache::TargetAttribInfoMap &target_attrib_info, GU_CopyToPointsCache::TargetAttribInfoMap &target_group_info, const bool topology_changed, const exint *const target_to_piecei=nullptr, const UT_Array< exint > *const owner_piece_offset_starts=nullptr, const GU_CopyToPointsCache::PieceData *const piece_data=nullptr, UT_Interrupt *boss=nullptr)
static SYS_FORCE_INLINE bool isType(const GA_Attribute *attrib)
Definition: GA_ATIString.h:52
GU_DetailHandle & gdh() const
The initial state of gdh depends on the cookMode()
Definition: SOP_NodeVerb.h:341
GA_OffsetList getOffsetFromIndexList() const
Definition: GA_IndexMap.h:244
bool fullBlockAdvance(GA_Offset &start, GA_Offset &end)
Definition: format.h:895
auto join(It begin, Sentinel end, string_view sep) -> join_view< It, Sentinel >
Definition: format.h:2559
GA_PageNum GAgetPageNum(GA_Offset v)
Definition: GA_Types.h:656
SYS_FORCE_INLINE FromType size() const
Returns the number of used elements in the list (always <= capacity())
const char * inputLabel(unsigned idx) const override
void UTparallelReduceLightItems(const Range &range, Body &body)
SYS_FORCE_INLINE bool contains(GA_Offset offset) const
static const UT_StringHolder theSOPTypeName
bool isEmpty() const
Returns true iff there are no occupied elements in the array.
Definition: UT_Array.h:650