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