HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GU_Copy2.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  * Definitions of functions and structures for copying geometry.
27  */
28 
29 #include "GU_Copy2.h"
30 
31 #include "GEO_BuildPrimitives.h"
32 
33 #include <GU/GU_Detail.h>
34 #include <GU/GU_DetailHandle.h>
35 #include <GU/GU_PackedGeometry.h>
36 #include <GU/GU_PrimPacked.h>
37 #include <GEO/GEO_Normal.h>
38 #include <GEO/GEO_PackedTypes.h>
40 #include <GA/GA_ATINumeric.h>
41 #include <GA/GA_ATITopology.h>
42 #include <GA/GA_Attribute.h>
43 #include <GA/GA_AttributeDict.h>
44 #include <GA/GA_AttributeSet.h>
46 #include <GA/GA_Edge.h>
47 #include <GA/GA_EdgeGroup.h>
48 #include <GA/GA_ElementGroup.h>
50 #include <GA/GA_Handle.h>
51 #include <GA/GA_Iterator.h>
52 #include <GA/GA_OffsetList.h>
53 #include <GA/GA_PageArray.h>
54 #include <GA/GA_PolyCounts.h>
55 #include <GA/GA_Primitive.h>
56 #include <GA/GA_PrimitiveTypes.h>
57 #include <GA/GA_Range.h>
58 #include <GA/GA_RTIOffsetList.h>
59 #include <GA/GA_SplittableRange.h>
60 #include <GA/GA_Types.h>
61 #include <UT/UT_Array.h>
62 #include <UT/UT_ArrayStringMap.h>
63 #include <UT/UT_Assert.h>
64 #include <UT/UT_Matrix3.h>
65 #include <UT/UT_Matrix4.h>
66 #include <UT/UT_PageArray.h>
67 #include <UT/UT_PageArrayImpl.h>
68 #include <UT/UT_Quaternion.h>
69 #include <UT/UT_Vector3.h>
70 #include <UT/UT_ParallelUtil.h>
71 #include <UT/UT_SmallArray.h>
72 #include <UT/UT_StringHolder.h>
73 #include <UT/UT_TaskGroup.h>
74 #include <UT/UT_UniquePtr.h>
75 #include <UT/UT_VectorTypes.h>
76 #include <SYS/SYS_StaticAssert.h>
77 #include <SYS/SYS_Types.h>
78 
79 #include <algorithm> // For std::upper_bound
80 #include <utility> // For std::pair
81 
82 using namespace UT::Literal;
83 
84 namespace HDK_Sample {
85 
86 namespace GU_Copy {
87 
89 static GA_AttributeOwner
90 guConflictAttribOwner(GA_AttributeOwner owner)
91 {
92  if (owner == GA_ATTRIB_POINT)
93  return GA_ATTRIB_VERTEX;
94  if (owner == GA_ATTRIB_VERTEX)
95  return GA_ATTRIB_POINT;
96  return GA_ATTRIB_INVALID;
97 }
98 
99 static GA_TypeInfo
100 guGetTransformTypeInfo(const GA_ATINumeric *attrib, const bool has_transform_matrices)
101 {
102  int tuple_size = attrib->getTupleSize();
103  if (tuple_size < 3 || !attrib->needsTransform())
104  return GA_TYPE_VOID;
105 
106  GA_TypeInfo attrib_type_info = attrib->getTypeInfo();
107  if (tuple_size == 3)
108  {
109  // Vectors and normals don't react to translations.
110  if (attrib_type_info == GA_TYPE_POINT ||
111  (has_transform_matrices && (attrib_type_info == GA_TYPE_VECTOR || attrib_type_info == GA_TYPE_NORMAL)))
112  {
113  return attrib_type_info;
114  }
115  }
116  else if (tuple_size == 4)
117  {
118  // Quaternions don't react to translations.
119  if ((has_transform_matrices && attrib_type_info == GA_TYPE_QUATERNION) || attrib_type_info == GA_TYPE_HPOINT)
120  return attrib_type_info;
121  }
122  else if (tuple_size == 9)
123  {
124  // 3x3 matrices don't react to translations.
125  if (has_transform_matrices && attrib_type_info == GA_TYPE_TRANSFORM)
126  return attrib_type_info;
127  }
128  else if (tuple_size == 16)
129  {
130  if (attrib_type_info == GA_TYPE_TRANSFORM)
131  return attrib_type_info;
132  }
133  return GA_TYPE_VOID;
134 }
135 
136 void
138  GU_Detail *output_geo,
139  const GU_Detail *source,
140  const GU_Detail *target,
141  GU_CopyToPointsCache *cache,
142  const GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info,
143  const GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info)
144 {
145  // Remove attributes from previous cook that are not present in source
146  // or that mismatch the type in source.
147  UT_SmallArray<GA_Attribute*> attribs_to_delete;
148  for (int owneri = 0; owneri < GA_ATTRIB_OWNER_N; ++owneri)
149  {
150  attribs_to_delete.clear();
151  bool pos_storage_mismatch = false;
152  GA_AttributeOwner owner = GA_AttributeOwner(owneri);
153  output_geo->getAttributeDict(owner).forEachAttribute([source,owner,
154  &attribs_to_delete,output_geo,&pos_storage_mismatch,
155  target_group_info,target_attrib_info,target,cache](GA_Attribute *attrib)
156  {
157  const GA_AttributeScope scope = attrib->getScope();
158  const bool is_group = (scope == GA_SCOPE_GROUP);
159  if (scope == GA_SCOPE_PRIVATE ||
160  (is_group && UTverify_cast<GA_ElementGroup*>(attrib)->isInternal()))
161  {
162  // Don't delete topology attributes.
163  if (!GA_ATITopology::isType(attrib))
164  attribs_to_delete.append(attrib);
165  return;
166  }
167  const UT_StringHolder &name = attrib->getName();
168  const GA_Attribute *source_attrib = source ? source->findAttribute(owner, scope, name) : nullptr;
169  const GU_CopyToPointsCache::TargetAttribInfoMap *target_info = is_group ? target_group_info : target_attrib_info;
171  if (target_info)
172  it = target_info->find(name);
173  if (!target_info || it.atEnd() || it->second.myCopyTo != owner)
174  {
175  if (!source_attrib || !source_attrib->matchesStorage(attrib))
176  {
177  // Be careful with P, since we can't delete it.
178  if (owner == GA_ATTRIB_POINT && attrib == output_geo->getP())
179  pos_storage_mismatch = (source_attrib != nullptr);
180  else
181  attribs_to_delete.append(attrib);
182  }
183  else // if (source) This is a redundant check, since if !source, source_attrib is null.
184  {
185  // If there was previously not a source attribute, this was
186  // a target attribute, so to reduce the risk of data ID
187  // havoc, since this is an uncommon case, we just delete.
188  // NOTE: The check for non-null source is because the source
189  // data IDs are irrelevant if they were just copied
190  // into a packed primitive.
191  // NOTE: Don't delete P. (It won't be in the cache on the first cook.)
192  auto *source_dataids = is_group ? cache->mySourceGroupDataIDs : cache->mySourceAttribDataIDs;
193  if (!source_dataids[owner].contains(name) && attrib != output_geo->getP())
194  {
195  attribs_to_delete.append(attrib);
196  }
197  }
198  }
199  else if (it->second.myCombineMethod == GU_CopyToPointsCache::AttribCombineMethod::COPY || !source_attrib)
200  {
201  // NOTE: P is never applied from target, so we don't need the
202  // special case here or in the next case.
203  const GA_Attribute *target_attrib = target->findAttribute(GA_ATTRIB_POINT, scope, name);
204  UT_ASSERT(target_attrib);
205  if (!target_attrib->matchesStorage(attrib))
206  attribs_to_delete.append(attrib);
207  else if (source)
208  {
209  // If we previously cloned from a source attribute,
210  // (we're now going to be cloning from a target attribute),
211  // to avoid data ID havoc, we delete.
212  // Without this case, there were problems where a target
213  // attribute being combined with a source attribute
214  // wasn't getting its data ID bumped or updating
215  // properly when the source attribute was no longer
216  // in the source on the next cook.
217  // NOTE: The check for non-null source is because the source
218  // data IDs are irrelevant if they were just copied
219  // into a packed primitive.
220  auto *source_dataids = is_group ? cache->mySourceGroupDataIDs : cache->mySourceAttribDataIDs;
221  if (source_dataids[owner].contains(name))
222  {
223  attribs_to_delete.append(attrib);
224  }
225  }
226  }
227  else if (!source_attrib->matchesStorage(attrib))
228  {
229  attribs_to_delete.append(attrib);
230  }
231  else // if (source) This is a redundant check, since if !source, source_attrib is null.
232  {
233  // If there was previously not a source attribute, this was
234  // a target attribute, so to reduce the risk of data ID
235  // havoc, since this is an uncommon case, we just delete.
236  // NOTE: The check for non-null source is because the source
237  // data IDs are irrelevant if they were just copied
238  // into a packed primitive.
239  auto *source_dataids = is_group ? cache->mySourceGroupDataIDs : cache->mySourceAttribDataIDs;
240  if (!source_dataids[owner].contains(name))
241  {
242  attribs_to_delete.append(attrib);
243  }
244  }
245  });
246 
247  for (exint i = 0, n = attribs_to_delete.size(); i < n; ++i)
248  {
249  const UT_StringHolder &name = attribs_to_delete[i]->getName();
250 
251  // Remove it from any data ID caches before deleting it,
252  // else we'll need to get a full UT_StringHolder to keep
253  // the name in scope.
254  if (attribs_to_delete[i]->getScope() == GA_SCOPE_GROUP)
255  {
256  cache->mySourceGroupDataIDs[owneri].erase(name);
257  cache->myTargetGroupInfo.erase(name);
258  output_geo->destroyElementGroup(owner, name);
259  }
260  else
261  {
262  cache->mySourceAttribDataIDs[owneri].erase(name);
263  cache->myTargetAttribInfo.erase(name);
264  output_geo->destroyAttribute(owner, name);
265  }
266 
267  }
268 
269  if (pos_storage_mismatch)
270  {
271  // Separate handling for P, since we can't delete it.
272  UTverify_cast<GA_ATINumeric*>(output_geo->getP())->setStorage(
273  UTverify_cast<const GA_ATINumeric*>(source->getP())->getStorage());
274 
276  }
277  }
278 
279  // Remove edge groups from previous cook that are not present in source.
280  UT_SmallArray<GA_EdgeGroup*> edgegroups_to_delete;
281  for (auto it = output_geo->edgeGroups().beginTraverse(); !it.atEnd(); ++it)
282  {
283  GA_EdgeGroup *edgegroup = it.group();
284  if (edgegroup->isInternal())
285  {
286  edgegroups_to_delete.append(edgegroup);
287  continue;
288  }
289  const GA_EdgeGroup *source_edgegroup = source ? source->findEdgeGroup(edgegroup->getName()) : nullptr;
290  if (!source_edgegroup || source_edgegroup->isInternal())
291  {
292  edgegroups_to_delete.append(edgegroup);
293  }
294  }
295  for (exint i = 0, n = edgegroups_to_delete.size(); i < n; ++i)
296  {
297  const UT_StringHolder &name = edgegroups_to_delete[i]->getName();
298  output_geo->destroyEdgeGroup(name);
299  cache->mySourceEdgeGroupDataIDs.erase(name);
300  }
301 }
302 
303 void
305  GU_PointTransformCache *cache,
306  const GA_OffsetListRef &target_point_list,
307  const GU_Detail *target,
308  const bool transform_using_more_than_P,
309  const bool allow_implicit_N,
310  bool &transforms_changed)
311 {
312  const exint num_target_points = target_point_list.size();
313  if (cache->myTransformCacheSize > 0 && num_target_points != cache->myTransformCacheSize)
314  {
315  UT_ASSERT(transforms_changed);
316  transforms_changed = true;
317  cache->clearTransformArrays();
318  }
319  if (num_target_points == 0)
320  {
321  UT_ASSERT(cache->myTransformCacheSize == 0);
322  return;
323  }
324 
325  GA_AttributeInstanceMatrix target_transform_attribs;
326  bool using_implicit_N = false;
327  if (transform_using_more_than_P)
328  {
329  target_transform_attribs.initialize(target->pointAttribs());
330  if (!target_transform_attribs.getN().isValid() && allow_implicit_N && target->getNumPrimitives() != 0)
331  {
332  using_implicit_N = true;
333  }
334  }
335  transforms_changed |= (using_implicit_N != cache->myTargetUsingImplicitN);
336  cache->myTargetUsingImplicitN = using_implicit_N;
337 
338  if (using_implicit_N)
339  {
340  // Implicit normals depend on the primitive list and the topology,
341  // (also depend on P, checked below), so check data IDs.
342  GA_DataId primlist_dataid = target->getPrimitiveList().getDataId();
343  GA_DataId topology_dataid = target->getTopology().getDataId();
344  transforms_changed |=
345  primlist_dataid != cache->myTargetPrimListDataID ||
346  topology_dataid != cache->myTargetTopologyDataID;
347  cache->myTargetPrimListDataID = primlist_dataid;
348  cache->myTargetTopologyDataID = topology_dataid;
349  }
350  else
351  {
354  }
355 
357  target_transform_attribs.getDataIds(new_transform_data_ids);
358  transforms_changed |=
359  target->getP()->getDataId() == GA_INVALID_DATAID ||
361  target->getP()->getDataId() != cache->myTargetPDataID;
362  if (!transforms_changed)
363  {
365  {
366  // NOTE: GA_AttributeInstanceMatrix uses a different value to
367  // indicate that an attribute is not present, so this
368  // check still supports missing attributes.
369  if (new_transform_data_ids[i] == GA_INVALID_DATAID ||
371  new_transform_data_ids[i] != cache->myTargetTransformDataIDs[i])
372  {
373  transforms_changed = true;
374  break;
375  }
376  }
377  }
378  if (transforms_changed)
379  {
380  //cache->myTransforming = true;
381  memcpy(cache->myTargetTransformDataIDs, new_transform_data_ids, GA_AttributeInstanceMatrix::theNumAttribs*sizeof(GA_DataId));
382  cache->myTargetPDataID = target->getP()->getDataId();
383 
384  // We always cache the full transform in double-precision,
385  // in case on future cooks, it's needed for new source attributes,
386  // when !transforms_changed.
387  bool onlyP = !target_transform_attribs.hasAnyAttribs() && !using_implicit_N;
388  if (!onlyP && !cache->myTransformMatrices3D)
389  cache->myTransformMatrices3D.reset(new UT_Matrix3D[num_target_points]);
390  if (!cache->myTransformTranslates3D)
391  cache->myTransformTranslates3D.reset(new UT_Vector3D[num_target_points]);
392  cache->myTransformCacheSize = num_target_points;
393 
394  // Recompute and cache needed transforms
395  const GA_ROHandleV3D targetP(target->getP());
396  if (onlyP)
397  {
398  cache->myTransformMatrices3D.reset();
399  UT_Vector3D *translates = cache->myTransformTranslates3D.get();
400  UT_ASSERT(targetP.isValid());
401  auto &&functor = [&target_point_list,&targetP,translates](const UT_BlockedRange<exint> &r)
402  {
403  for (exint i = r.begin(), end = r.end(); i < end; ++i)
404  {
405  GA_Offset target_ptoff = target_point_list[i];
406  translates[i] = targetP.get(target_ptoff);
407  }
408  };
409  if (num_target_points > 1024)
410  UTparallelFor(UT_BlockedRange<exint>(0, num_target_points), functor, 2, 512);
411  else
412  functor(UT_BlockedRange<exint>(0, num_target_points));
413  }
414  else
415  {
416  // Implicit N doesn't need to be in cache, since we cache
417  // the transforms themselves.
418  // If things like pscale are changing and not P or the primitive
419  // list or topology, it might be worth caching the implicit N,
420  // but that's probably an uncommon edge case to optimize for.
421  GA_AttributeUPtr implicitN;
422  if (using_implicit_N)
423  {
425  GA_RWHandleV3 implicitN_h(implicitN.get());
426 
427  // Compute implicit normals based on P and the primitives in target.
428  GEOcomputeNormals(*target, implicitN_h);
429  target_transform_attribs.setN(implicitN.get());
430  }
431 
432  UT_Matrix3D *matrices = cache->myTransformMatrices3D.get();
433  UT_Vector3D *translates = cache->myTransformTranslates3D.get();
434  UT_ASSERT(targetP.isValid());
435  auto &&functor = [&target_transform_attribs,&target_point_list,&targetP,matrices,translates](const UT_BlockedRange<exint> &r)
436  {
437  for (exint i = r.begin(), end = r.end(); i < end; ++i)
438  {
439  GA_Offset target_ptoff = target_point_list[i];
441  target_transform_attribs.getMatrix(transform, targetP.get(target_ptoff), target_ptoff);
442 
443  // Save transform in matrices and translates
444  matrices[i] = UT_Matrix3D(transform);
445  transform.getTranslates(translates[i]);
446  }
447  };
448  if (num_target_points > 512)
449  UTparallelFor(UT_BlockedRange<exint>(0, num_target_points), functor, 2, 256);
450  else
451  functor(UT_BlockedRange<exint>(0, num_target_points));
452  }
453  }
454 }
455 
456 void
458  GU_Detail *output_geo,
459  const GU_Detail *source,
460  exint *num_source_attribs,
461  bool has_transform_matrices,
462  bool *needed_transforms,
463  const GU_Detail *target,
464  GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info,
466  exint *num_target_attribs)
467 {
468  using AttribCombineMethod = GU_CopyToPointsCache::AttribCombineMethod;
469 
470  for (int owneri = source ? 0 : GA_ATTRIB_OWNER_N; owneri < GA_ATTRIB_OWNER_N; ++owneri)
471  {
472  GA_AttributeOwner owner = GA_AttributeOwner(owneri);
474  [owner,output_geo,num_source_attribs,
475  needed_transforms,has_transform_matrices,
476  target_attrib_info](const GA_Attribute *source_attrib)
477  {
478  const UT_StringHolder &name = source_attrib->getName();
479  if (target_attrib_info)
480  {
481  // If copying from target, skip, since it'll be added from target.
482  // Target attributes take precedence over source attributes,
483  // because users can always remove the target attributes from
484  // the pattern parameters, but they don't have control over the
485  // source attributes in this node.
486  // NOTE: Point and vertex attributes of the same name are not allowed,
487  // so we check for both.
488  auto it = target_attrib_info->find(name);
489  if (!it.atEnd() && (it->second.myCopyTo == owner || it->second.myCopyTo == guConflictAttribOwner(owner)) &&
490  it->second.myCombineMethod == AttribCombineMethod::COPY)
491  return;
492  }
493 
494  GA_Attribute *dest_attrib = output_geo->findAttribute(owner, name);
495  UT_ASSERT(!dest_attrib || dest_attrib->matchesStorage(source_attrib));
496  if (!dest_attrib)
497  {
498  dest_attrib = output_geo->getAttributes().cloneAttribute(owner, name, GA_AttributeSet::namevalidcertificate(), *source_attrib,
499  true, (owner == GA_ATTRIB_DETAIL) ? GA_DATA_ID_CLONE : GA_DATA_ID_BUMP);
500  }
501 
502  // Copy detail attributes immediately
503  if (owner == GA_ATTRIB_DETAIL)
504  {
505  dest_attrib->replace(*source_attrib);
506  }
507  else
508  {
509  if (num_source_attribs)
510  ++num_source_attribs[owner];
511 
512  // Just copy non-storage metadata for the rest; (storage type already matches).
513  dest_attrib->copyNonStorageMetadata(source_attrib);
514 
515  if (!needed_transforms)
516  return;
517 
518  GA_ATINumeric *dest_numeric = GA_ATINumeric::cast(dest_attrib);
519  if (dest_numeric)
520  {
521  GA_TypeInfo transform_type = guGetTransformTypeInfo(dest_numeric, has_transform_matrices);
522  if (transform_type != GA_TYPE_VOID)
523  {
524  using namespace NeededTransforms;
525  bool double_precision = (dest_numeric->getStorage() == GA_STORE_REAL64);
526  if (transform_type == GA_TYPE_POINT || transform_type == GA_TYPE_HPOINT)
527  {
528  needed_transforms[matrix3f] |= !double_precision;
529  needed_transforms[translate3f] |= !double_precision;
530  }
531  else if (transform_type == GA_TYPE_VECTOR)
532  {
533  needed_transforms[matrix3f] |= !double_precision;
534  }
535  else if (transform_type == GA_TYPE_NORMAL)
536  {
537  needed_transforms[inverse3d] |= double_precision;
538  needed_transforms[inverse3f] |= !double_precision;
539  }
540  else if (transform_type == GA_TYPE_QUATERNION)
541  {
542  needed_transforms[quaterniond] |= double_precision;
543  needed_transforms[quaternionf] |= !double_precision;
544  }
545  else if (transform_type == GA_TYPE_TRANSFORM)
546  {
547  needed_transforms[matrix3f] |= !double_precision;
548  needed_transforms[translate3f] |= !double_precision && (dest_numeric->getTupleSize() == 16);
549  }
550  }
551  }
552  }
553  });
554 
555  if (owner != GA_ATTRIB_DETAIL)
556  {
557  // Now for the element groups
558  for (auto it = source->getElementGroupTable(owner).beginTraverse(); !it.atEnd(); ++it)
559  {
560  const GA_ElementGroup *source_group = it.group();
561  if (source_group->isInternal())
562  continue;
563 
564  if (target_group_info)
565  {
566  // If copying from target, skip, since it'll be added from target.
567  auto it = target_group_info->find(source_group->getName());
568  if (!it.atEnd() && it->second.myCopyTo == owner)
569  return;
570  }
571 
572  GA_ElementGroup *dest_group = output_geo->findElementGroup(owner, source_group->getName());
573  if (!dest_group)
574  {
575  dest_group = UTverify_cast<GA_ElementGroup *>(output_geo->getElementGroupTable(owner).newGroup(source_group->getName()));
576  UT_ASSERT_MSG(!dest_group->isOrdered(), "Writing to groups in parallel requires unordered groups, and ordering isn't as useful for copied geometry");
577  }
578  else if (dest_group->isOrdered())
579  {
580  dest_group->clearOrdered();
581  }
582  if (num_source_attribs)
583  ++num_source_attribs[owner];
584  }
585  }
586  }
587 
588  // Add edge groups from source that are not in output_geo.
589  if (source)
590  {
591  for (auto it = source->edgeGroups().beginTraverse(); !it.atEnd(); ++it)
592  {
593  const GA_EdgeGroup *source_group = it.group();
594  if (source_group->isInternal())
595  continue;
596  GA_EdgeGroup *dest_group = output_geo->findEdgeGroup(source_group->getName());
597  if (!dest_group)
598  {
599  dest_group = UTverify_cast<GA_EdgeGroup *>(output_geo->edgeGroups().newGroup(source_group->getName()));
600  }
601  }
602  }
603 
604  if (!target || !target_attrib_info || !target_group_info)
605  return;
606 
607  for (auto it = target_attrib_info->begin(); !it.atEnd(); ++it)
608  {
609  const UT_StringHolder &name = it->first;
610  GA_AttributeOwner output_owner = it->second.myCopyTo;
611  AttribCombineMethod method = it->second.myCombineMethod;
612  if (source && method != AttribCombineMethod::COPY)
613  {
614  // If source has the attribute and the method isn't copying from target,
615  // we've already cloned the attribute above, so skip.
616  const GA_Attribute *source_attrib = source->findAttribute(output_owner, name);
617  if (source_attrib)
618  {
619  if (num_target_attribs)
620  ++num_target_attribs[output_owner];
621  continue;
622  }
623  // NOTE: Point and vertex attributes of the same name are not allowed,
624  // so we check for both.
625  GA_AttributeOwner conflict_owner = guConflictAttribOwner(output_owner);
626  if (conflict_owner != GA_ATTRIB_INVALID)
627  {
628  source_attrib = source->findAttribute(conflict_owner, name);
629  if (source_attrib)
630  {
631  // For simplicity, instead of trying to promote to the specified target type,
632  // just stick with the type in source.
633  // TODO: Maybe for completeness in the future we should promote,
634  // but the partial cooking case checking gets very complicated,
635  // so I'm not adding that right now.
636  it->second.myCopyTo = conflict_owner;
637  if (num_target_attribs)
638  ++num_target_attribs[conflict_owner];
639  continue;
640  }
641  }
642  }
643 
644  const GA_Attribute *target_attrib = target->findAttribute(GA_ATTRIB_POINT, name);
645  GA_Attribute *dest_attrib = output_geo->findAttribute(output_owner, name);
646  UT_ASSERT(!dest_attrib || dest_attrib->matchesStorage(target_attrib));
647  if (!dest_attrib)
648  {
649  dest_attrib = output_geo->getAttributes().cloneAttribute(output_owner, name, GA_AttributeSet::namevalidcertificate(), *target_attrib,
650  true, GA_DATA_ID_BUMP);
651 
652  // We want multiplying with no attribute to be equivalent to copying,
653  // and adding to no attribute is automatically equivalent to copying,
654  // so it's easiest to just change it to copy here.
655  // Subtracting is different, but equivalent to subtracting from zero,
656  // so we can leave it as is.
657  if (method == AttribCombineMethod::MULTIPLY ||
658  method == AttribCombineMethod::ADD)
659  {
660  it->second.myCombineMethod = AttribCombineMethod::COPY;
661  }
662  }
663  UT_ASSERT(dest_attrib != nullptr);
664 
665  // Just copy non-storage metadata for the rest; (storage type already matches).
666  dest_attrib->copyNonStorageMetadata(target_attrib);
667 
668  if (num_target_attribs)
669  ++num_target_attribs[output_owner];
670  }
671 
672  for (auto it = target_group_info->begin(); !it.atEnd(); ++it)
673  {
674  const UT_StringHolder &name = it->first;
675  GA_AttributeOwner output_owner = it->second.myCopyTo;
676  AttribCombineMethod method = it->second.myCombineMethod;
677  if (source && method != AttribCombineMethod::COPY)
678  {
679  // If source has the group and the method isn't copying from target,
680  // we've already cloned the group above, so skip.
681  const GA_ElementGroup *source_group = source->findElementGroup(output_owner, name);
682  if (source_group)
683  {
684  if (num_target_attribs)
685  ++num_target_attribs[output_owner];
686  continue;
687  }
688  // NOTE: Point and vertex attributes of the same name are not allowed,
689  // so we check for both.
690  GA_AttributeOwner conflict_owner = guConflictAttribOwner(output_owner);
691  if (conflict_owner != GA_ATTRIB_INVALID)
692  {
693  source_group = source->findElementGroup(conflict_owner, name);
694  if (source_group)
695  {
696  // For simplicity, instead of trying to promote to the specified target type,
697  // just stick with the type in source.
698  // TODO: Maybe for completeness in the future we should promote,
699  // but the partial cooking case checking gets very complicated,
700  // so I'm not adding that right now.
701  it->second.myCopyTo = conflict_owner;
702  if (num_target_attribs)
703  ++num_target_attribs[conflict_owner];
704  continue;
705  }
706  }
707  }
708 
709  UT_ASSERT(target->findPointGroup(name) != nullptr);
710  GA_ElementGroup *dest_group = output_geo->findElementGroup(output_owner, name);
711  if (!dest_group)
712  {
713  dest_group = UTverify_cast<GA_ElementGroup *>(output_geo->getElementGroupTable(output_owner).newGroup(name));
714 
715  // We want intersecting with no group to be equivalent to copying,
716  // and unioning with no group is automatically equivalent to copying,
717  // so it's easiest to just change it to copy here.
718  if (method == AttribCombineMethod::MULTIPLY ||
719  method == AttribCombineMethod::ADD)
720  {
721  it->second.myCombineMethod = AttribCombineMethod::COPY;
722  }
723  // Subtracting from no group is equivalent to doing nothing,
724  // (apart from ensuring that the group exists).
725  if (method == AttribCombineMethod::SUBTRACT)
726  {
727  it->second.myCombineMethod = AttribCombineMethod::NONE;
728  }
729  }
730  UT_ASSERT_MSG(!dest_group->isOrdered(), "Writing to groups in parallel requires unordered groups, and ordering isn't as useful for copied geometry");
731 
732  if (num_target_attribs)
733  ++num_target_attribs[output_owner];
734  }
735 }
736 
737 void
739  GU_PointTransformCache *cache,
740  exint num_target_points,
741  bool transforms_changed,
742  const bool needed_transforms[NeededTransforms::num_needed_transforms])
743 {
744  using namespace NeededTransforms;
745 
746  bool has_transform_matrices = (cache->myTransformMatrices3D.get() != nullptr);
747  if (needed_transforms[translate3f] && num_target_points > 0)
748  {
749  bool compute = transforms_changed;
750  if (!cache->myTransformTranslates3F)
751  {
752  cache->myTransformTranslates3F.reset(new UT_Vector3F[num_target_points]);
753  compute = true;
754  }
755  if (compute)
756  {
757  const UT_Vector3D *vector3d = cache->myTransformTranslates3D.get();
758  UT_Vector3F *vector3f = cache->myTransformTranslates3F.get();
759  auto &&functor = [vector3d,vector3f](const UT_BlockedRange<exint> &r)
760  {
761  for (exint i = r.begin(), end = r.end(); i < end; ++i)
762  {
763  vector3f[i] = UT_Vector3F(vector3d[i]);
764  }
765  };
766  if (num_target_points > 1024)
767  UTparallelFor(UT_BlockedRange<exint>(0, num_target_points), functor, 2, 512);
768  else
769  functor(UT_BlockedRange<exint>(0, num_target_points));
770  }
771  }
772 
773  if (!has_transform_matrices)
774  {
775  // Only translates, so no matrices, inverses, or quaternions
776  cache->myTransformMatrices3F.reset();
777  cache->myTransformInverse3F.reset();
778  cache->myTransformInverse3D.reset();
779  cache->myTransformQuaternionsF.reset();
780  cache->myTransformQuaternionsD.reset();
781  return;
782  }
783  if (num_target_points <= 0)
784  return;
785 
786  if (needed_transforms[matrix3f])
787  {
788  bool compute = transforms_changed;
789  if (!cache->myTransformMatrices3F)
790  {
791  cache->myTransformMatrices3F.reset(new UT_Matrix3F[num_target_points]);
792  compute = true;
793  }
794  if (compute)
795  {
796  const UT_Matrix3D *matrices3d = cache->myTransformMatrices3D.get();
797  UT_Matrix3F *matrices3f = cache->myTransformMatrices3F.get();
798  auto &&functor = [matrices3d,matrices3f](const UT_BlockedRange<exint> &r)
799  {
800  for (exint i = r.begin(), end = r.end(); i < end; ++i)
801  {
802  matrices3f[i] = UT_Matrix3F(matrices3d[i]);
803  }
804  };
805  if (num_target_points > 1024)
806  UTparallelFor(UT_BlockedRange<exint>(0, num_target_points), functor, 2, 512);
807  else
808  functor(UT_BlockedRange<exint>(0, num_target_points));
809  }
810  }
811  if (needed_transforms[inverse3f] || needed_transforms[inverse3d])
812  {
813  bool compute = transforms_changed;
814  if (needed_transforms[inverse3f] && !cache->myTransformInverse3F)
815  {
816  cache->myTransformInverse3F.reset(new UT_Matrix3F[num_target_points]);
817  compute = true;
818  }
819  if (needed_transforms[inverse3d] && !cache->myTransformInverse3D)
820  {
821  cache->myTransformInverse3D.reset(new UT_Matrix3D[num_target_points]);
822  compute = true;
823  }
824  if (compute)
825  {
826  const UT_Matrix3D *matrices3d = cache->myTransformMatrices3D.get();
827  UT_Matrix3D *inverses3d = cache->myTransformInverse3D.get();
828  UT_Matrix3F *inverses3f = cache->myTransformInverse3F.get();
829  auto &&functor = [matrices3d,inverses3d,inverses3f](const UT_BlockedRange<exint> &r)
830  {
831  for (exint i = r.begin(), end = r.end(); i < end; ++i)
832  {
833  UT_Matrix3D inverse;
834  auto singular = matrices3d[i].invert(inverse);
835  if (singular)
836  {
837  // FIXME: Check if 1, 2, or 3 zero dimensions!!!
838  inverse.identity();
839  }
840 
841  // This determinant check and scale are from GA_AttributeTransformer.
842  // They're presumably so that normals get flipped when applying
843  // a negative scale. I'm not sure whether that's ideal behaviour,
844  // but it's consistent with previous behaviour, so I'm sticking with it.
845  if (matrices3d[i].determinant() < 0)
846  inverse.scale(-1, -1, -1);
847 
848  if (inverses3d)
849  inverses3d[i] = inverse;
850  if (inverses3f)
851  inverses3f[i] = UT_Matrix3F(inverse);
852  }
853  };
854  if (num_target_points > 512)
855  UTparallelFor(UT_BlockedRange<exint>(0, num_target_points), functor, 2, 256);
856  else
857  functor(UT_BlockedRange<exint>(0, num_target_points));
858  }
859  }
860  if (needed_transforms[quaternionf] || needed_transforms[quaterniond])
861  {
862  bool compute = transforms_changed;
863  if (needed_transforms[quaternionf] && !cache->myTransformQuaternionsF)
864  {
865  cache->myTransformQuaternionsF.reset(new UT_QuaternionF[num_target_points]);
866  compute = true;
867  }
868  if (needed_transforms[quaterniond] && !cache->myTransformQuaternionsD)
869  {
870  cache->myTransformQuaternionsD.reset(new UT_QuaternionD[num_target_points]);
871  compute = true;
872  }
873  if (compute)
874  {
875  const UT_Matrix3D *matrices3d = cache->myTransformMatrices3D.get();
876  UT_QuaternionD *quaternionsd = cache->myTransformQuaternionsD.get();
877  UT_QuaternionF *quaternionsf = cache->myTransformQuaternionsF.get();
878  auto &&functor = [matrices3d,quaternionsd,quaternionsf](const UT_BlockedRange<exint> &r)
879  {
880  for (exint i = r.begin(), end = r.end(); i < end; ++i)
881  {
882  UT_QuaternionD quaternion;
883  quaternion.updateFromArbitraryMatrix(matrices3d[i]);
884 
885  if (quaternionsd)
886  quaternionsd[i] = quaternion;
887  if (quaternionsf)
888  quaternionsf[i] = UT_QuaternionF(quaternion);
889  }
890  };
891  if (num_target_points > 512)
892  UTparallelFor(UT_BlockedRange<exint>(0, num_target_points), functor, 2, 256);
893  else
894  functor(UT_BlockedRange<exint>(0, num_target_points));
895  }
896  }
897 }
898 
899 static void
900 guFindStartInTarget(
901  const GA_Offset start,
902  exint &targeti,
903  exint &piece_elementi,
904  exint &piece_element_count,
905  const GA_OffsetList **piece_offset_list,
906  const GA_Offset start_offset,
907  const exint *const piece_offset_starts,
908  const exint *const piece_offset_starts_end,
909  const exint num_target_points,
910  const exint *const target_to_piecei,
911  const GU_CopyToPointsCache::PieceData *const piece_data,
912  const int owneri,
913  const GA_OffsetList *const source_offset_list,
914  const exint source_offset_list_size)
915 {
916  const exint output_primi = start - start_offset;
917  if (piece_offset_starts)
918  {
919  // Find the first entry in piece_prim_starts where the next entry is greater than output_primi,
920  // (in other words, the last entry whose value is less than or equal to output_primi,
921  // so output_primi is in that piece.) The -1 is to go back one.
922  targeti = (std::upper_bound(
923  piece_offset_starts,
924  piece_offset_starts_end,
925  output_primi) - piece_offset_starts) - 1;
926  UT_ASSERT_P(targeti >= 0 && targeti < num_target_points);
927  piece_elementi = output_primi - piece_offset_starts[targeti];
928  const exint piecei = target_to_piecei[targeti];
929  const GU_CopyToPointsCache::PieceData &current_piece = piece_data[piecei];
930  const GA_OffsetList &local_piece_offset_list = current_piece.mySourceOffsetLists[owneri];
931  piece_element_count = local_piece_offset_list.size();
932  if (piece_offset_list != nullptr)
933  *piece_offset_list = &local_piece_offset_list;
934  }
935  else
936  {
937  piece_element_count = source_offset_list_size;
938  if (piece_offset_list != nullptr)
939  {
940  UT_ASSERT_P(source_offset_list_size == source_offset_list->size());
941  *piece_offset_list = source_offset_list;
942  }
943  targeti = output_primi / piece_element_count;
944  piece_elementi = output_primi % piece_element_count;
945  }
946 };
947 
948 static inline void
949 guIteratePieceElement(
950  exint &piece_elementi,
951  exint &piece_element_count,
952  exint &targeti,
953  const exint *const piece_offset_starts,
954  const exint num_target_points,
955  const exint *const target_to_piecei,
956  const GU_CopyToPointsCache::PieceData *const piece_data,
957  const int owneri,
958  const GA_OffsetList *&piece_offset_list)
959 {
960  ++piece_elementi;
961  // NOTE: This must be while instead of if, because there can be zero primitives in a piece.
962  while (piece_elementi >= piece_element_count)
963  {
964  piece_elementi = 0;
965  ++targeti;
966 
967  if (targeti >= num_target_points)
968  break;
969 
970  if (piece_offset_starts != nullptr)
971  {
972  exint piecei = target_to_piecei[targeti];
973  const GU_CopyToPointsCache::PieceData &current_piece = piece_data[piecei];
974  piece_offset_list = &current_piece.mySourceOffsetLists[owneri];
975  piece_element_count = piece_offset_list->size();
976  }
977  }
978 }
979 
980 /// This is the same as guIteratePieceElement, but also looking up the target offset,
981 /// and not returning the piece_offset_list.
982 static inline void
983 guIteratePieceElementOff(
984  exint &piece_elementi,
985  exint &piece_element_count,
986  exint &targeti,
987  const exint *const piece_offset_starts,
988  const exint num_target_points,
989  const exint *const target_to_piecei,
990  const GU_CopyToPointsCache::PieceData *const piece_data,
991  const int owneri,
992  GA_Offset &target_off,
993  const GA_OffsetListRef &target_point_list)
994 {
995  ++piece_elementi;
996  // NOTE: This must be while instead of if, because there can be zero primitives in a piece.
997  while (piece_elementi >= piece_element_count)
998  {
999  piece_elementi = 0;
1000  ++targeti;
1001 
1002  if (targeti >= num_target_points)
1003  break;
1004 
1005  target_off = target_point_list[targeti];
1006 
1007  if (piece_offset_starts != nullptr)
1008  {
1009  exint piecei = target_to_piecei[targeti];
1010  const GU_CopyToPointsCache::PieceData &current_piece = piece_data[piecei];
1011  const GA_OffsetList &piece_offset_list = current_piece.mySourceOffsetLists[owneri];
1012  piece_element_count = piece_offset_list.size();
1013  }
1014  }
1015 }
1016 
1017 /// This is the same as guIteratePieceElement, but also looking up whether
1018 /// the target offset is in the given group, and not returning the piece_offset_list.
1019 static inline void
1020 guIteratePieceElementGroup(
1021  exint &piece_elementi,
1022  exint &piece_element_count,
1023  exint &targeti,
1024  const exint *const piece_offset_starts,
1025  const exint num_target_points,
1026  const exint *const target_to_piecei,
1027  const GU_CopyToPointsCache::PieceData *const piece_data,
1028  const int owneri,
1029  bool &target_in_group,
1030  const GA_OffsetListRef &target_point_list,
1031  const GA_PointGroup *const target_group)
1032 {
1033  ++piece_elementi;
1034  // NOTE: This must be while instead of if, because there can be zero primitives in a piece.
1035  while (piece_elementi >= piece_element_count)
1036  {
1037  piece_elementi = 0;
1038  ++targeti;
1039 
1040  if (targeti >= num_target_points)
1041  break;
1042 
1043  const GA_Offset target_off = target_point_list[targeti];
1044  target_in_group = target_group->contains(target_off);
1045 
1046  if (piece_offset_starts != nullptr)
1047  {
1048  exint piecei = target_to_piecei[targeti];
1049  const GU_CopyToPointsCache::PieceData &current_piece = piece_data[piecei];
1050  const GA_OffsetList &piece_offset_list = current_piece.mySourceOffsetLists[owneri];
1051  piece_element_count = piece_offset_list.size();
1052  }
1053  }
1054 }
1055 
1056 static void
1057 guApplyTransformToAttribute(
1058  const GU_CopyToPointsCache *const cache,
1059  GA_TypeInfo transform_type,
1060  GA_ATINumeric *output_numeric,
1061  const GA_ATINumeric *source_numeric,
1062  const GA_OffsetList &source_offset_list,
1063  const bool copy_source_attribs_in_parallel,
1064  UT_TaskGroup &task_group,
1065  const GA_SplittableRange &output_splittable_range,
1066  const GA_Offset start_offset,
1067  const exint *target_to_piecei,
1068  const exint num_target_points,
1069  const exint *piece_offset_starts,
1070  const exint *piece_offset_starts_end,
1071  const GU_CopyToPointsCache::PieceData *piece_data)
1072 {
1073  const GA_IndexMap &index_map = output_numeric->getIndexMap();
1074  if (index_map.indexSize() == 0)
1075  return;
1076 
1077  int owneri = output_numeric->getOwner();
1078 
1079  const UT_Matrix3F *const transform_matrices_3f = cache->myTransformMatrices3F.get();
1080  const UT_Matrix3D *const transform_matrices_3d = cache->myTransformMatrices3D.get();
1081  const UT_Vector3F *const transform_translates_3f = cache->myTransformTranslates3F.get();
1082  const UT_Vector3D *const transform_translates_3d = cache->myTransformTranslates3D.get();
1083  if (transform_type == GA_TYPE_POINT)
1084  {
1085  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1086  transform_matrices_3f,transform_matrices_3d,transform_translates_3f,transform_translates_3d,
1087  start_offset,piece_offset_starts,piece_offset_starts_end,
1088  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1089  {
1091  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1092  {
1093  exint targeti;
1094  exint piece_elementi;
1095  exint piece_element_count;
1096  const GA_OffsetList *piece_offset_list;
1097  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1098  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1099  num_target_points, target_to_piecei, piece_data, owneri,
1100  &source_offset_list, source_offset_list.size());
1101 
1102  if (output_numeric->getStorage() == GA_STORE_REAL32)
1103  {
1104  GA_PageArray<fpreal32,3> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<3>();
1105  const GA_PageArray<fpreal32,3> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<3>();
1106  // FIXME: Find longer contiguous spans to transform, for better performance.
1107  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1108  {
1109  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1110  auto pos = UTmakeVector3T( source_data.getVector(source_off) );
1111  if (transform_matrices_3f)
1112  pos = (pos * transform_matrices_3f[targeti]) + transform_translates_3f[targeti];
1113  else
1114  pos += transform_translates_3f[targeti];
1115  output_data.setVector(dest_off, pos);
1116 
1117  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1118  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1119  }
1120  }
1121  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1122  {
1123  GA_PageArray<fpreal64,3> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<3>();
1124  const GA_PageArray<fpreal64,3> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<3>();
1125  // FIXME: Find longer contiguous spans to transform, for better performance.
1126  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1127  {
1128  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1129  auto pos = UTmakeVector3T( source_data.getVector(source_off) );
1130  if (transform_matrices_3d)
1131  pos = (pos * transform_matrices_3d[targeti]) + transform_translates_3d[targeti];
1132  else
1133  pos += transform_translates_3d[targeti];
1134  output_data.setVector(dest_off, pos);
1135 
1136  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1137  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1138  }
1139  }
1140  else
1141  {
1142  GA_RWHandleV3 output_data(output_numeric);
1143  GA_ROHandleV3 source_data(source_numeric);
1144  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1145  // FIXME: Find longer contiguous spans to transform, for better performance.
1146  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1147  {
1148  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1149  UT_Vector3F pos = source_data.get(source_off);
1150  if (transform_matrices_3f)
1151  pos = (pos * transform_matrices_3f[targeti]) + transform_translates_3f[targeti];
1152  else
1153  pos += transform_translates_3f[targeti];
1154  output_data.set(dest_off, pos);
1155 
1156  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1157  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1158  }
1159  }
1160  }
1161  };
1162  if (copy_source_attribs_in_parallel)
1163  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1164  else
1165  functor(output_splittable_range);
1166  }
1167  else if (transform_type == GA_TYPE_VECTOR)
1168  {
1169  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1170  transform_matrices_3f,transform_matrices_3d,
1171  start_offset,piece_offset_starts,piece_offset_starts_end,
1172  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1173  {
1175  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1176  {
1177  exint targeti;
1178  exint piece_elementi;
1179  exint piece_element_count;
1180  const GA_OffsetList *piece_offset_list;
1181  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1182  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1183  num_target_points, target_to_piecei, piece_data, owneri,
1184  &source_offset_list, source_offset_list.size());
1185 
1186  if (output_numeric->getStorage() == GA_STORE_REAL32)
1187  {
1188  UT_ASSERT_P(transform_matrices_3f);
1189  GA_PageArray<fpreal32,3> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<3>();
1190  const GA_PageArray<fpreal32,3> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<3>();
1191  // FIXME: Find longer contiguous spans to transform, for better performance.
1192  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1193  {
1194  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1195  auto vec = UTmakeVector3T( source_data.getVector(source_off) );
1196  vec.rowVecMult(transform_matrices_3f[targeti]);
1197  output_data.setVector(dest_off, vec);
1198 
1199  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1200  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1201  }
1202  }
1203  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1204  {
1205  UT_ASSERT_P(transform_matrices_3d);
1206  GA_PageArray<fpreal64,3> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<3>();
1207  const GA_PageArray<fpreal64,3> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<3>();
1208  // FIXME: Find longer contiguous spans to transform, for better performance.
1209  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1210  {
1211  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1212  auto vec = UTmakeVector3T( source_data.getVector(source_off) );
1213  vec.rowVecMult(transform_matrices_3d[targeti]);
1214  output_data.setVector(dest_off, vec);
1215 
1216  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1217  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1218  }
1219  }
1220  else
1221  {
1222  UT_ASSERT_P(transform_matrices_3f);
1223  GA_RWHandleV3 output_data(output_numeric);
1224  GA_ROHandleV3 source_data(source_numeric);
1225  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1226  // FIXME: Find longer contiguous spans to transform, for better performance.
1227  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1228  {
1229  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1230  UT_Vector3F vec = source_data.get(source_off);
1231  vec.rowVecMult(transform_matrices_3f[targeti]);
1232  output_data.set(dest_off, vec);
1233 
1234  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1235  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1236  }
1237  }
1238  }
1239  };
1240  if (copy_source_attribs_in_parallel)
1241  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1242  else
1243  functor(output_splittable_range);
1244  }
1245  else if (transform_type == GA_TYPE_NORMAL)
1246  {
1247  const UT_Matrix3F *transform_inverse_3f = cache->myTransformInverse3F.get();
1248  const UT_Matrix3D *transform_inverse_3d = cache->myTransformInverse3D.get();
1249  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1250  transform_inverse_3f,transform_inverse_3d,
1251  start_offset,piece_offset_starts,piece_offset_starts_end,
1252  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1253  {
1255  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1256  {
1257  exint targeti;
1258  exint piece_elementi;
1259  exint piece_element_count;
1260  const GA_OffsetList *piece_offset_list;
1261  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1262  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1263  num_target_points, target_to_piecei, piece_data, owneri,
1264  &source_offset_list, source_offset_list.size());
1265 
1266  if (output_numeric->getStorage() == GA_STORE_REAL32)
1267  {
1268  UT_ASSERT_P(transform_inverse_3f);
1269  GA_PageArray<fpreal32,3> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<3>();
1270  const GA_PageArray<fpreal32,3> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<3>();
1271  // FIXME: Find longer contiguous spans to transform, for better performance.
1272  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1273  {
1274  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1275  auto nml = UTmakeVector3T( source_data.getVector(source_off) );
1276  float orig_length2 = nml.length2();
1277  nml.colVecMult(transform_inverse_3f[targeti]);
1278  float new_length2 = nml.length2();
1279  // Preserve normal length
1280  if (new_length2 != 0)
1281  nml *= SYSsqrt(orig_length2/new_length2);
1282  output_data.setVector(dest_off, nml);
1283 
1284  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1285  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1286  }
1287  }
1288  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1289  {
1290  UT_ASSERT_P(transform_inverse_3d);
1291  GA_PageArray<fpreal64,3> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<3>();
1292  const GA_PageArray<fpreal64,3> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<3>();
1293  // FIXME: Find longer contiguous spans to transform, for better performance.
1294  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1295  {
1296  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1297  auto nml = UTmakeVector3T( source_data.getVector(source_off) );
1298  float orig_length2 = nml.length2();
1299  nml.colVecMult(transform_inverse_3d[targeti]);
1300  float new_length2 = nml.length2();
1301  // Preserve normal length
1302  if (new_length2 != 0)
1303  nml *= SYSsqrt(orig_length2/new_length2);
1304  output_data.setVector(dest_off, nml);
1305 
1306  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1307  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1308  }
1309  }
1310  else
1311  {
1312  UT_ASSERT_P(transform_inverse_3f);
1313  GA_RWHandleV3 output_data(output_numeric);
1314  GA_ROHandleV3 source_data(source_numeric);
1315  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1316  // FIXME: Find longer contiguous spans to transform, for better performance.
1317  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1318  {
1319  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1320  UT_Vector3F nml = source_data.get(source_off);
1321  float orig_length2 = nml.length2();
1322  nml.colVecMult(transform_inverse_3f[targeti]);
1323  float new_length2 = nml.length2();
1324  // Preserve normal length
1325  if (new_length2 != 0)
1326  nml *= SYSsqrt(orig_length2/new_length2);
1327  output_data.set(dest_off, nml);
1328 
1329  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1330  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1331  }
1332  }
1333  }
1334  };
1335  if (copy_source_attribs_in_parallel)
1336  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1337  else
1338  functor(output_splittable_range);
1339  }
1340  else if (transform_type == GA_TYPE_QUATERNION)
1341  {
1342  const UT_QuaternionF *transform_quaternions_3f = cache->myTransformQuaternionsF.get();
1343  const UT_QuaternionD *transform_quaternions_3d = cache->myTransformQuaternionsD.get();
1344  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1345  transform_quaternions_3f,transform_quaternions_3d,
1346  start_offset,piece_offset_starts,piece_offset_starts_end,
1347  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1348  {
1350  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1351  {
1352  exint targeti;
1353  exint piece_elementi;
1354  exint piece_element_count;
1355  const GA_OffsetList *piece_offset_list;
1356  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1357  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1358  num_target_points, target_to_piecei, piece_data, owneri,
1359  &source_offset_list, source_offset_list.size());
1360 
1361  if (output_numeric->getStorage() == GA_STORE_REAL32)
1362  {
1363  UT_ASSERT_P(transform_quaternions_3f);
1364  GA_PageArray<fpreal32,4> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<4>();
1365  const GA_PageArray<fpreal32,4> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<4>();
1366  // FIXME: Find longer contiguous spans to transform, for better performance.
1367  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1368  {
1369  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1370  UT_QuaternionF q{UTmakeQuaternionT(source_data.getVector(source_off))};
1371  q = transform_quaternions_3f[targeti] * q;
1372  output_data.setVector(dest_off, *(const UT_FixedVector<fpreal32,4>*)&q);
1373 
1374  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1375  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1376  }
1377  }
1378  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1379  {
1380  UT_ASSERT_P(transform_quaternions_3d);
1381  GA_PageArray<fpreal64,4> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<4>();
1382  const GA_PageArray<fpreal64,4> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<4>();
1383  // FIXME: Find longer contiguous spans to transform, for better performance.
1384  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1385  {
1386  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1387  UT_QuaternionD q{UTmakeQuaternionT(source_data.getVector(source_off))};
1388  q = transform_quaternions_3d[targeti] * q;
1389  output_data.setVector(dest_off, *(const UT_FixedVector<fpreal64,4>*)&q);
1390 
1391  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1392  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1393  }
1394  }
1395  else
1396  {
1397  UT_ASSERT_P(transform_quaternions_3f);
1398  GA_RWHandleQ output_data(output_numeric);
1399  GA_ROHandleQ source_data(source_numeric);
1400  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1401  // FIXME: Find longer contiguous spans to transform, for better performance.
1402  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1403  {
1404  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1405  UT_QuaternionF q = source_data.get(source_off);
1406  q = transform_quaternions_3f[targeti] * q;
1407  output_data.set(dest_off, q);
1408 
1409  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1410  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1411  }
1412  }
1413  }
1414  };
1415  if (copy_source_attribs_in_parallel)
1416  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1417  else
1418  functor(output_splittable_range);
1419  }
1420  else if (transform_type == GA_TYPE_HPOINT)
1421  {
1422  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1423  transform_matrices_3f,transform_matrices_3d,transform_translates_3f,transform_translates_3d,
1424  start_offset,piece_offset_starts,piece_offset_starts_end,
1425  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1426  {
1427  UT_Matrix3F identity3f(1);
1428  UT_Matrix3D identity3d(1);
1430  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1431  {
1432  exint targeti;
1433  exint piece_elementi;
1434  exint piece_element_count;
1435  const GA_OffsetList *piece_offset_list;
1436  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1437  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1438  num_target_points, target_to_piecei, piece_data, owneri,
1439  &source_offset_list, source_offset_list.size());
1440 
1441  if (output_numeric->getStorage() == GA_STORE_REAL32)
1442  {
1443  GA_PageArray<fpreal32,4> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<4>();
1444  const GA_PageArray<fpreal32,4> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<4>();
1445  // FIXME: Find longer contiguous spans to transform, for better performance.
1446  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1447  {
1448  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1449  UT_Vector4F hp{UTmakeVector4T(source_data.getVector(source_off))};
1450  hp.homogenize();
1451  UT_Matrix4F transform(transform_matrices_3f ? transform_matrices_3f[targeti] : identity3f);
1452  transform.setTranslates(transform_translates_3f[targeti]);
1453  hp *= transform;
1454  hp.dehomogenize();
1455  output_data.setVector(dest_off, hp);
1456 
1457  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1458  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1459  }
1460  }
1461  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1462  {
1463  GA_PageArray<fpreal64,4> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<4>();
1464  const GA_PageArray<fpreal64,4> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<4>();
1465  // FIXME: Find longer contiguous spans to transform, for better performance.
1466  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1467  {
1468  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1469  UT_Vector4D hp{UTmakeVector4T(source_data.getVector(source_off))};
1470  hp.homogenize();
1471  UT_Matrix4D transform(transform_matrices_3d ? transform_matrices_3d[targeti] : identity3d);
1472  transform.setTranslates(transform_translates_3d[targeti]);
1473  hp *= transform;
1474  hp.dehomogenize();
1475  output_data.setVector(dest_off, hp);
1476 
1477  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1478  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1479  }
1480  }
1481  else
1482  {
1483  GA_RWHandleV4 output_data(output_numeric);
1484  GA_ROHandleV4 source_data(source_numeric);
1485  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1486  // FIXME: Find longer contiguous spans to transform, for better performance.
1487  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1488  {
1489  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1490  UT_Vector4F hp = source_data.get(source_off);
1491  hp.homogenize();
1492  UT_Matrix4F transform(transform_matrices_3f ? transform_matrices_3f[targeti] : identity3f);
1493  transform.setTranslates(transform_translates_3f[targeti]);
1494  hp *= transform;
1495  hp.dehomogenize();
1496  output_data.set(dest_off, hp);
1497 
1498  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1499  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1500  }
1501  }
1502  }
1503  };
1504  if (copy_source_attribs_in_parallel)
1505  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1506  else
1507  functor(output_splittable_range);
1508  }
1509  else if (output_numeric->getTupleSize() == 9) // transform_type == GA_TYPE_TRANSFORM
1510  {
1511  UT_ASSERT(transform_type == GA_TYPE_TRANSFORM);
1512  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1513  transform_matrices_3f,transform_matrices_3d,
1514  start_offset,piece_offset_starts,piece_offset_starts_end,
1515  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1516  {
1518  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1519  {
1520  exint targeti;
1521  exint piece_elementi;
1522  exint piece_element_count;
1523  const GA_OffsetList *piece_offset_list;
1524  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1525  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1526  num_target_points, target_to_piecei, piece_data, owneri,
1527  &source_offset_list, source_offset_list.size());
1528 
1529  if (output_numeric->getStorage() == GA_STORE_REAL32)
1530  {
1531  UT_ASSERT_P(transform_matrices_3f);
1532  GA_PageArray<fpreal32,9> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<9>();
1533  const GA_PageArray<fpreal32,9> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<9>();
1534  // FIXME: Find longer contiguous spans to transform, for better performance.
1535  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1536  {
1537  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1538  UT_Matrix3F mat{UTmakeMatrix3T(source_data.getVector(source_off))};
1539  mat *= transform_matrices_3f[targeti];
1540  output_data.setVector(dest_off, *(const UT_FixedVector<fpreal32,9>*)&mat);
1541 
1542  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1543  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1544  }
1545  }
1546  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1547  {
1548  UT_ASSERT_P(transform_matrices_3d);
1549  GA_PageArray<fpreal64,9> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<9>();
1550  const GA_PageArray<fpreal64,9> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<9>();
1551  // FIXME: Find longer contiguous spans to transform, for better performance.
1552  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1553  {
1554  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1555  UT_Matrix3D mat{UTmakeMatrix3T(source_data.getVector(source_off))};
1556  mat *= transform_matrices_3d[targeti];
1557  output_data.setVector(dest_off, *(const UT_FixedVector<fpreal64,9>*)&mat);
1558 
1559  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1560  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1561  }
1562  }
1563  else
1564  {
1565  UT_ASSERT_P(transform_matrices_3f);
1566  GA_RWHandleM3 output_data(output_numeric);
1567  GA_ROHandleM3 source_data(source_numeric);
1568  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1569  // FIXME: Find longer contiguous spans to transform, for better performance.
1570  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1571  {
1572  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1573  UT_Matrix3F mat = source_data.get(source_off);
1574  mat *= transform_matrices_3f[targeti];
1575  output_data.set(dest_off, mat);
1576 
1577  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1578  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1579  }
1580  }
1581  }
1582  };
1583  if (copy_source_attribs_in_parallel)
1584  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1585  else
1586  functor(output_splittable_range);
1587  }
1588  else
1589  {
1590  UT_ASSERT(transform_type == GA_TYPE_TRANSFORM);
1591  UT_ASSERT(output_numeric->getTupleSize() == 16);
1592  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
1593  transform_matrices_3f,transform_matrices_3d,transform_translates_3f,transform_translates_3d,
1594  start_offset,piece_offset_starts,piece_offset_starts_end,
1595  num_target_points,target_to_piecei,piece_data,owneri](const GA_SplittableRange &r)
1596  {
1598  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
1599  {
1600  exint targeti;
1601  exint piece_elementi;
1602  exint piece_element_count;
1603  const GA_OffsetList *piece_offset_list;
1604  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
1605  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
1606  num_target_points, target_to_piecei, piece_data, owneri,
1607  &source_offset_list, source_offset_list.size());
1608 
1609  if (output_numeric->getStorage() == GA_STORE_REAL32)
1610  {
1611  GA_PageArray<fpreal32,16> &output_data = output_numeric->getData().castType<fpreal32>().castTupleSize<16>();
1612  const GA_PageArray<fpreal32,16> &source_data = source_numeric->getData().castType<fpreal32>().castTupleSize<16>();
1613  // FIXME: Find longer contiguous spans to transform, for better performance.
1614  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1615  {
1616  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1617  UT_Matrix4F mat{UTmakeMatrix4T(source_data.getVector(source_off))};
1618  if (transform_matrices_3f)
1619  {
1620  UT_Matrix4F transform(transform_matrices_3f[targeti]);
1621  transform.setTranslates(transform_translates_3f[targeti]);
1622  mat *= transform;
1623  }
1624  else
1625  mat.translate(transform_translates_3f[targeti]);
1626  output_data.setVector(dest_off, *(const UT_FixedVector<fpreal32,16>*)&mat);
1627 
1628  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1629  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1630  }
1631  }
1632  else if (output_numeric->getStorage() == GA_STORE_REAL64)
1633  {
1634  GA_PageArray<fpreal64,16> &output_data = output_numeric->getData().castType<fpreal64>().castTupleSize<16>();
1635  const GA_PageArray<fpreal64,16> &source_data = source_numeric->getData().castType<fpreal64>().castTupleSize<16>();
1636  // FIXME: Find longer contiguous spans to transform, for better performance.
1637  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1638  {
1639  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1640  UT_Matrix4D mat{UTmakeMatrix4T(source_data.getVector(source_off))};
1641  if (transform_matrices_3d)
1642  {
1643  UT_Matrix4D transform(transform_matrices_3d[targeti]);
1644  transform.setTranslates(transform_translates_3d[targeti]);
1645  mat *= transform;
1646  }
1647  else
1648  mat.translate(transform_translates_3d[targeti]);
1649  output_data.setVector(dest_off, *(const UT_FixedVector<fpreal64,16>*)&mat);
1650 
1651  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1652  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1653  }
1654  }
1655  else
1656  {
1657  GA_RWHandleM4 output_data(output_numeric);
1658  GA_ROHandleM4 source_data(source_numeric);
1659  UT_ASSERT_P(output_data.isValid() && source_data.isValid());
1660  // FIXME: Find longer contiguous spans to transform, for better performance.
1661  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
1662  {
1663  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
1664  UT_Matrix4F mat = source_data.get(source_off);
1665  if (transform_matrices_3f)
1666  {
1667  UT_Matrix4F transform(transform_matrices_3f[targeti]);
1668  transform.setTranslates(transform_translates_3f[targeti]);
1669  mat *= transform;
1670  }
1671  else
1672  mat.translate(transform_translates_3f[targeti]);
1673  output_data.set(dest_off, mat);
1674 
1675  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
1676  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
1677  }
1678  }
1679  }
1680  };
1681  if (copy_source_attribs_in_parallel)
1682  UTparallelForRunInTaskGroup(task_group, output_splittable_range, functor);
1683  else
1684  functor(output_splittable_range);
1685  }
1686 }
1687 
1688 void
1690  GU_Detail *output_geo,
1691  const GU_Detail *const source,
1692  const GA_OffsetList &source_point_list_cache,
1693  const GA_OffsetList &source_vertex_list_cache,
1694  const GA_OffsetList &source_prim_list_cache,
1695  const exint ncopies)
1696 {
1697  exint source_point_count = source_point_list_cache.size();
1698  exint source_vertex_count = source_vertex_list_cache.size();
1699  exint source_prim_count = source_prim_list_cache.size();
1700 
1701  GA_Size totalnpoints = source_point_count * ncopies;
1702  GA_Offset startpt = output_geo->appendPointBlock(totalnpoints);
1703 
1704  if (source_prim_count <= 0)
1705  return;
1706 
1707  UT_SmallArray<std::pair<int,exint>, 2*sizeof(std::pair<int,exint>)> prim_type_count_pairs;
1708  bool all_source_prims = source_prim_list_cache.isSame(source->getPrimitiveMap().getOffsetFromIndexList());
1709  GA_Range source_primrange;
1710  if (!all_source_prims)
1711  source_primrange = GA_RTIOffsetList(source->getPrimitiveMap(), source_prim_list_cache);
1712  source->getPrimitiveList().getPrimitiveTypeCounts(prim_type_count_pairs, !all_source_prims ? &source_primrange : nullptr);
1713  UT_ASSERT(!prim_type_count_pairs.isEmpty());
1714 
1715  // Compute a ptoff to point-within-a-copy index structure if we're not copying all points.
1716  GA_PageArray<exint,1> source_ptoff_to_pointi;
1717  bool all_source_points = source_point_list_cache.isSame(source->getPointMap().getOffsetFromIndexList());
1718  bool have_reverse_point_map = !all_source_points && !source_point_list_cache.isTrivial();
1719  if (have_reverse_point_map)
1720  {
1721  source_ptoff_to_pointi.setSize(source->getNumPointOffsets(), exint(-1));
1722  // TODO: Parallelize this if it's worthwhile.
1723  for (exint pointi = 0; pointi < source_point_count; ++pointi)
1724  {
1725  source_ptoff_to_pointi.set(source_point_list_cache[pointi], pointi);
1726  }
1727  }
1728 
1729  // It's safe for hassharedpoints to be true when there are no shared points,
1730  // but not vice versa.
1731  bool hassharedpoints = true;
1732  // In this case, "contiguous" means "starting from startpt and contiguous"
1733  bool hascontiguouspoints = false;
1734  if (source_point_count >= source_vertex_count)
1735  {
1736  // If there is at least one point per vertex, there's a
1737  // decent chance that none of the source points are shared,
1738  // which can make building the primitives much faster,
1739  // so we check.
1740 
1741  hascontiguouspoints = true;
1742  hassharedpoints = false;
1743 
1744  // TODO: Parallelize this.
1745  GA_Offset last_point = GA_INVALID_OFFSET;
1746  for (exint primi = 0; primi < source_prim_count; ++primi)
1747  {
1748  GA_Offset primoff = source_prim_list_cache[primi];
1749  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1750  if (vertices.size() == 0)
1751  continue;
1752  if (!GAisValid(last_point))
1753  {
1754  last_point = source->vertexPoint(vertices[0]);
1755  if (last_point != source_point_list_cache[0])
1756  {
1757  hascontiguouspoints = false;
1758  }
1759  }
1760  else
1761  {
1762  GA_Offset current_point = source->vertexPoint(vertices[0]);
1763  hascontiguouspoints &= (current_point == last_point+1);
1764 
1765  // This isn't a perfect check for sharing, but since
1766  // we don't want to do a full sort, we just check whether
1767  // the point offsets are strictly increasing.
1768  // If points are shared, this check will make hassharedpoints true.
1769  // If no points are shared, this can also end up being true sometimes.
1770  hassharedpoints |= (current_point <= last_point);
1771  if (hassharedpoints)
1772  break;
1773  last_point = current_point;
1774  }
1775  for (exint i = 1, n = vertices.size(); i < n; ++i)
1776  {
1777  GA_Offset current_point = source->vertexPoint(vertices[i]);
1778  hascontiguouspoints &= (current_point == last_point+1);
1779 
1780  // See comment above about how this isn't a perfect check.
1781  hassharedpoints |= (current_point <= last_point);
1782  if (hassharedpoints)
1783  break;
1784  last_point = current_point;
1785  }
1786  if (hassharedpoints)
1787  break;
1788  }
1789  }
1790 
1791  UT_UniquePtr<exint[]> vertexpointnumbers_deleter(!hascontiguouspoints ? new exint[source_vertex_count] : nullptr);
1792  GA_PolyCounts vertexlistsizelist;
1793  UT_SmallArray<exint, 2*sizeof(exint)> closed_span_lengths;
1794  closed_span_lengths.append(0);
1795  exint *vertexpointnumbers = vertexpointnumbers_deleter.get();
1796  // TODO: Parallelize this if it's worthwhile.
1797  exint vertexi = 0;
1798  for (exint primi = 0; primi < source_prim_count; ++primi)
1799  {
1800  GA_Offset primoff = source_prim_list_cache[primi];
1801  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1802  GA_Size n = vertices.size();
1803 
1804  vertexlistsizelist.append(n);
1805 
1806  bool closed = vertices.getExtraFlag();
1807  // Index 0 (size 1) always represents open, and so does every even index (odd size).
1808  // Every odd index (even size) always represents closed.
1809  // This condition checks if we're switching between open and closed.
1810  if ((closed_span_lengths.size()&1) == exint(closed))
1811  closed_span_lengths.append(1);
1812  else
1813  ++(closed_span_lengths.last());
1814 
1815  if (!hascontiguouspoints)
1816  {
1817  if (have_reverse_point_map)
1818  {
1819  for (exint i = 0; i < n; ++i)
1820  {
1821  GA_Offset source_ptoff = source->vertexPoint(vertices(i));
1822  exint pointi = source_ptoff_to_pointi.get(source_ptoff);
1823  vertexpointnumbers[vertexi] = pointi;
1824  ++vertexi;
1825  }
1826  }
1827  else if (source_point_list_cache.isTrivial())
1828  {
1829  for (exint i = 0; i < n; ++i)
1830  {
1831  GA_Offset source_ptoff = source->vertexPoint(vertices(i));
1832  exint pointi = source_point_list_cache.find(source_ptoff);
1833  UT_ASSERT_P(pointi >= 0);
1834  vertexpointnumbers[vertexi] = pointi;
1835  ++vertexi;
1836  }
1837  }
1838  else
1839  {
1840  for (exint i = 0; i < n; ++i)
1841  {
1842  GA_Offset source_ptoff = source->vertexPoint(vertices(i));
1843  exint pointi = exint(source->pointIndex(source_ptoff));
1844  vertexpointnumbers[vertexi] = pointi;
1845  ++vertexi;
1846  }
1847  }
1848  }
1849  }
1850 
1851  GA_Offset start_primoff = GEObuildPrimitives(
1852  output_geo,
1853  prim_type_count_pairs.getArray(),
1854  startpt,
1855  source_point_count,
1856  vertexlistsizelist,
1857  vertexpointnumbers,
1858  hassharedpoints,
1859  closed_span_lengths.getArray(),
1860  ncopies);
1861 
1862 
1863  // Early exit if only polygons and tetrahedra,
1864  // since they have no member data outside of GA_Primitive,
1865  // and might be stored compressed in GA_PrimitiveList.
1866  exint num_polys_and_tets =
1867  output_geo->countPrimitiveType(GA_PRIMPOLY) +
1869  if (output_geo->getNumPrimitives() == num_polys_and_tets)
1870  return;
1871 
1872  // Copy primitive subclass data for types other than polygons and tetrahedra.
1873  UT_BlockedRange<GA_Offset> primrange(start_primoff, start_primoff+output_geo->getNumPrimitives());
1874  auto &&functor = [output_geo,source,&source_prim_list_cache,source_prim_count](const UT_BlockedRange<GA_Offset> &r)
1875  {
1876  // FIXME: Find longer contiguous spans to copy, for better performance.
1877  for (GA_Offset dest_off = r.begin(), end = r.end(); dest_off < end; ++dest_off)
1878  {
1879  exint sourcei = exint(dest_off) % source_prim_count;
1880  GA_Offset source_off = source_prim_list_cache[sourcei];
1881  const GA_Primitive *source_prim = source->getPrimitive(source_off);
1882  GA_Primitive *output_prim = output_geo->getPrimitive(dest_off);
1883  output_prim->copySubclassData(source_prim);
1884  }
1885  };
1886  if (output_geo->getNumPrimitives() >= 1024)
1887  {
1888  UTparallelForLightItems(primrange, functor);
1889  }
1890  else
1891  {
1892  functor(primrange);
1893  }
1894 }
1895 
1896 void
1898  GA_OffsetList &offset_list,
1899  const GU_Detail *const detail,
1900  const GA_ElementGroup *const group,
1901  const GA_AttributeOwner owner)
1902 {
1903  if (group == nullptr)
1904  {
1905  offset_list = detail->getIndexMap(owner).getOffsetFromIndexList();
1906  }
1907  else
1908  {
1909  offset_list.clear();
1910  GA_Offset start;
1911  GA_Offset end;
1912  GA_Range range(*group);
1913  for (GA_Iterator it(range); it.fullBlockAdvance(start, end); )
1914  {
1915  offset_list.setTrivialRange(offset_list.size(), start, end-start);
1916  }
1917  }
1918 }
1919 
1920 void
1922  GU_Detail *output_geo,
1923  const GU_Detail *const source,
1924  const exint source_point_count,
1925  const exint source_vertex_count,
1926  const exint source_prim_count,
1927  const GA_OffsetList &source_point_list_cache,
1928  GA_OffsetList &source_vertex_list_cache,
1929  const GA_OffsetList &source_prim_list_cache,
1930  const GA_PointGroup *const source_pointgroup,
1931  const GA_PrimitiveGroup *const source_primgroup,
1932  const exint ncopies)
1933 {
1934  if (ncopies <= 0)
1935  {
1936  source_vertex_list_cache.clear();
1937  return;
1938  }
1939 
1940  if (source_prim_count <= 0)
1941  {
1942  source_vertex_list_cache.clear();
1943 
1944  GA_Size totalnpoints = source_point_count * ncopies;
1945  output_geo->appendPointBlock(totalnpoints);
1946  return;
1947  }
1948 
1949  // We need to build and cache a structure for quickly looking up
1950  // vertex offsets in order to easily copy vertex attributes
1951  // in parallel.
1952  {
1953  // TODO: Parallelize this if it's worthwhile.
1954  source_vertex_list_cache.clear();
1955  GA_Offset start;
1956  GA_Offset end;
1957  for (GA_Iterator it(source->getPrimitiveRange(source_primgroup)); it.fullBlockAdvance(start, end); )
1958  {
1959  for (GA_Offset primoff = start; primoff < end; ++primoff)
1960  {
1961  const GA_OffsetListRef vertices = source->getPrimitiveVertexList(primoff);
1962  source_vertex_list_cache.append(vertices);
1963  }
1964  }
1965  }
1966  UT_ASSERT(source_vertex_list_cache.size() == source_vertex_count);
1967 
1969  output_geo,
1970  source,
1971  source_point_list_cache,
1972  source_vertex_list_cache,
1973  source_prim_list_cache,
1974  ncopies);
1975 }
1976 
1977 void
1979  GU_Detail *const output_geo,
1980  const exint num_packed_prims)
1981 {
1982  if (num_packed_prims <= 0)
1983  return;
1984 
1985  // Create points
1986  GA_Offset start_ptoff = output_geo->appendPointBlock(num_packed_prims);
1987  GA_Offset end_ptoff = start_ptoff+num_packed_prims;
1988 
1989  // Create primitives and vertices
1990  GA_Offset start_vtxoff;
1991  output_geo->appendPrimitivesAndVertices(
1992  GU_PackedGeometry::typeId(), num_packed_prims, 1, start_vtxoff);
1993  GA_Offset end_vtxoff = start_vtxoff+num_packed_prims;
1994 
1995  // Wire the vertices to the points.
1996  // There are no shared points, so it's relatively easy.
1997  GA_ATITopology *const vertexToPoint = output_geo->getTopology().getPointRef();
1998  GA_ATITopology *const pointToVertex = output_geo->getTopology().getVertexRef();
1999  if (vertexToPoint)
2000  {
2001  UTparallelForLightItems(GA_SplittableRange(GA_Range(output_geo->getVertexMap(), start_vtxoff, end_vtxoff)),
2002  geo_SetTopoMappedParallel<int>(vertexToPoint, start_ptoff, start_vtxoff, nullptr));
2003  }
2004  if (pointToVertex)
2005  {
2006  UTparallelForLightItems(GA_SplittableRange(GA_Range(output_geo->getPointMap(), start_ptoff, end_ptoff)),
2007  geo_SetTopoMappedParallel<int>(pointToVertex, start_vtxoff, start_ptoff, nullptr));
2008  }
2009 }
2010 
2011 void
2013  GU_Detail *const output_geo,
2014  const GA_SplittableRange *const output_splittable_ranges,
2015  const GU_Detail *const source,
2016  const exint num_target_points,
2017  GU_CopyToPointsCache *const cache,
2018  const GA_OffsetList *const source_offset_lists,
2019  const exint *const num_source_attribs,
2020  const bool no_transforms,
2021  const bool had_transform_matrices,
2022  const bool has_transform_matrices,
2023  const bool topology_changed,
2024  const bool transforms_changed,
2025  const GU_Detail *const target,
2026  const GU_CopyToPointsCache::TargetAttribInfoMap *const target_attrib_info,
2027  const GU_CopyToPointsCache::TargetAttribInfoMap *const target_group_info,
2028  const exint *const target_to_piecei,
2029  const UT_Array<exint> *const owner_piece_offset_starts,
2030  const GU_CopyToPointsCache::PieceData *const piece_data)
2031 {
2032  if (num_target_points <= 0)
2033  return;
2034 
2035  using AttribCombineMethod = GU_CopyToPointsCache::AttribCombineMethod;
2036 
2037  UT_ASSERT(source_offset_lists != nullptr);
2038  // If target is present or transforms are applied, cache is required.
2039  UT_ASSERT((target == nullptr && no_transforms) || cache != nullptr);
2040  const exint num_output_elements =
2041  output_geo->getNumVertices() * num_source_attribs[GA_ATTRIB_VERTEX] +
2042  output_geo->getNumPoints() * num_source_attribs[GA_ATTRIB_POINT] +
2043  output_geo->getNumPrimitives() * num_source_attribs[GA_ATTRIB_PRIMITIVE];
2044  const bool copy_source_attribs_in_parallel = num_output_elements >= 4096;
2045  UT_TaskGroup task_group;
2046  for (int owneri = 0; owneri < 3; ++owneri)
2047  {
2048  const exint *piece_offset_starts = nullptr;
2049  const exint *piece_offset_starts_end = nullptr;
2050  if (owner_piece_offset_starts)
2051  {
2052  auto &array = owner_piece_offset_starts[owneri];
2053  piece_offset_starts = array.getArray();
2054  piece_offset_starts_end = array.getArray() + array.size();
2055  }
2056 
2057  const GA_OffsetList &source_offset_list = source_offset_lists[owneri];
2058  GA_AttributeOwner owner = GA_AttributeOwner(owneri);
2059  const GA_IndexMap &index_map = output_geo->getIndexMap(owner);
2060  if (index_map.indexSize() == 0)
2061  continue;
2062 
2063  // To get the start offset of the copied geometry without the overhead of using a GA_Iterator
2064  // (which copyies the GA_Range), we just use iterateCreate and iterateRewind directly.
2065  GA_IteratorState iterator_state;
2066  output_splittable_ranges[owneri].iterateCreate(iterator_state);
2067  GA_Offset start_offset;
2068  GA_Offset first_block_end;
2069  output_splittable_ranges[owneri].iterateRewind(iterator_state, start_offset, first_block_end);
2070 
2071  for (auto it = output_geo->getAttributeDict(owner).begin(GA_SCOPE_PUBLIC); !it.atEnd(); ++it)
2072  {
2073  GA_Attribute *output_attrib = it.attrib();
2074  const UT_StringHolder &name = output_attrib->getName();
2075  const GA_Attribute *source_attrib = source->findAttribute(owner, name);
2076  if (!source_attrib)
2077  continue;
2078 
2079  // Check for interactions with target attributes.
2080  AttribCombineMethod target_method = AttribCombineMethod::NONE;
2081  if (target)
2082  {
2083  auto target_it = target_attrib_info->find(name);
2084  if (!target_it.atEnd())
2085  {
2086  if (target_it->second.myCopyTo == owner)
2087  {
2088  target_method = target_it->second.myCombineMethod;
2089 
2090  // Target attributes take precedence if copying.
2091  if (target_method == AttribCombineMethod::COPY)
2092  continue;
2093  }
2094  else if (target_it->second.myCombineMethod == AttribCombineMethod::COPY &&
2095  target_it->second.myCopyTo == guConflictAttribOwner(owner))
2096  {
2097  // Target attributes take precedence if copying,
2098  // including if destination type differs between point and vertex.
2099  // NOTE: We don't have to check point vs. vertex for
2100  // mult/add/sub, because we already forced target type
2101  // to match source type in those situations.
2102  continue;
2103  }
2104  }
2105  }
2106 
2107  AttribCombineMethod prev_target_method = AttribCombineMethod::NONE;
2109  bool target_changed = false;
2110  if (target)
2111  {
2112  prev_target_it = cache->myTargetAttribInfo.find(name);
2113  if (!prev_target_it.atEnd())
2114  {
2115  if (prev_target_it->second.myCopyTo == owner)
2116  {
2117  prev_target_method = prev_target_it->second.myCombineMethod;
2118 
2119  if (prev_target_method != AttribCombineMethod::NONE)
2120  {
2121  const GA_Attribute *target_attrib = target->findPointAttribute(name);
2122  GA_DataId prev_target_dataid = prev_target_it->second.myDataID;
2123  target_changed = !target_attrib ||
2124  !GAisValid(prev_target_dataid) ||
2125  !GAisValid(target_attrib->getDataId()) ||
2126  target_attrib->getDataId() != prev_target_dataid;
2127  }
2128  }
2129  }
2130  if (target_method != prev_target_method)
2131  {
2132  target_changed = true;
2133  }
2134  }
2135 
2136  GA_ATINumeric *output_numeric = GA_ATINumeric::cast(output_attrib);
2137  if (output_numeric)
2138  {
2139  const GA_ATINumeric *source_numeric = UTverify_cast<const GA_ATINumeric*>(source_attrib);
2140 
2141  GA_TypeInfo transform_type = no_transforms ? GA_TYPE_VOID : guGetTransformTypeInfo(output_numeric, has_transform_matrices);
2142 
2143  if (transform_type != GA_TYPE_VOID)
2144  {
2145  UT_ASSERT(cache != nullptr);
2146  // If !topology_changed, and !transforms_changed, and the
2147  // source_attrib data ID hasn't changed, don't re-transform.
2148  if (!topology_changed && !transforms_changed && !target_changed)
2149  {
2150  auto it = cache->mySourceAttribDataIDs[owneri].find(name);
2151  if (!it.atEnd() && it->second != GA_INVALID_DATAID && it->second == source_attrib->getDataId())
2152  {
2153  continue;
2154  }
2155  }
2156  cache->mySourceAttribDataIDs[owneri][name] = source_attrib->getDataId();
2157  output_attrib->bumpDataId();
2158 
2159  // Remove any entry from the target cache, so that the code for
2160  // applying target attributes is forced to re-apply the operation.
2161  if (target && !prev_target_it.atEnd())
2162  cache->myTargetAttribInfo.erase(prev_target_it);
2163 
2164  guApplyTransformToAttribute(
2165  cache,
2166  transform_type,
2167  output_numeric,
2168  source_numeric,
2169  source_offset_list,
2170  copy_source_attribs_in_parallel,
2171  task_group,
2172  output_splittable_ranges[owneri],
2173  start_offset,
2174  target_to_piecei,
2175  num_target_points,
2176  piece_offset_starts,
2177  piece_offset_starts_end,
2178  piece_data);
2179  }
2180  else
2181  {
2182  if (cache != nullptr)
2183  {
2184  // If !topology_changed and the source_attrib data ID hasn't changed since last time, don't re-copy.
2185  if (!topology_changed && !target_changed)
2186  {
2187  auto it = cache->mySourceAttribDataIDs[owneri].find(name);
2188  if (!it.atEnd() && it->second != GA_INVALID_DATAID && it->second == source_attrib->getDataId())
2189  {
2190  // NOTE: no_transforms should only be false when copying for a packed primitive,
2191  // in which case, there are never transforms, so we don't have to worry
2192  // about it changing whether an attribute is transformed or copied.
2193  if (no_transforms || has_transform_matrices || !had_transform_matrices)
2194  continue;
2195 
2196  // If this attribute was transforming last time and is no longer transforming,
2197  // it needs to be copied, even though it hasn't changed.
2198  GA_TypeInfo old_transform_type = guGetTransformTypeInfo(output_numeric, has_transform_matrices);
2199  if (old_transform_type == GA_TYPE_VOID)
2200  {
2201  // Wasn't transforming last time either,
2202  // and the attribute hasn't changed, so we can skip it.
2203  continue;
2204  }
2205  }
2206  }
2207  cache->mySourceAttribDataIDs[owneri][name] = source_attrib->getDataId();
2208 
2209  // Remove any entry from the target cache, so that the code for
2210  // applying target attributes is forced to re-apply the operation.
2211  if (target && !prev_target_it.atEnd())
2212  cache->myTargetAttribInfo.erase(prev_target_it);
2213  }
2214 
2215  output_attrib->bumpDataId();
2216 
2217  auto &&functor = [output_numeric,source_numeric,&source_offset_list,
2218  piece_offset_starts,piece_offset_starts_end,num_target_points,
2219  target_to_piecei,piece_data,owneri,start_offset](const GA_SplittableRange &r)
2220  {
2221  auto &output_data = output_numeric->getData();
2222  const auto &source_data = source_numeric->getData();
2223 
2224  GA_Offset start;
2225  GA_Offset end;
2226  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2227  {
2228  exint targeti;
2229  exint piece_elementi;
2230  exint piece_element_count;
2231  const GA_OffsetList *piece_offset_list;
2232  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2233  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
2234  num_target_points, target_to_piecei, piece_data, owneri,
2235  &source_offset_list, source_offset_list.size());
2236 
2237  // FIXME: Consider switching on type and tuple size outside the loop.
2238  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2239  {
2240  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
2241  output_data.moveRange(source_data, source_off, dest_off, GA_Offset(1));
2242 
2243  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2244  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
2245  }
2246  }
2247  };
2248  if (copy_source_attribs_in_parallel)
2249  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owneri], functor);
2250  else
2251  functor(output_splittable_ranges[owneri]);
2252  }
2253 
2254  }
2255  else
2256  {
2257  if (cache != nullptr)
2258  {
2259  // If !topology_changed and the source_attrib data ID hasn't changed since last time, don't re-copy.
2260  if (!topology_changed && !target_changed)
2261  {
2262  auto it = cache->mySourceAttribDataIDs[owneri].find(name);
2263  if (!it.atEnd() && it->second != GA_INVALID_DATAID && it->second == source_attrib->getDataId())
2264  continue;
2265  }
2266  cache->mySourceAttribDataIDs[owneri][name] = source_attrib->getDataId();
2267 
2268  // Remove any entry from the target cache, so that the code for
2269  // applying target attributes is forced to re-apply the operation.
2270  if (target && !prev_target_it.atEnd())
2271  cache->myTargetAttribInfo.erase(prev_target_it);
2272  }
2273 
2274  output_attrib->bumpDataId();
2275 
2276  GA_ATIString *output_string = GA_ATIString::cast(output_attrib);
2277  if (output_string)
2278  {
2279  // Special case for string attributes for batch adding of string references.
2280  const GA_ATIString *source_string = UTverify_cast<const GA_ATIString *>(source_attrib);
2281  auto &&functor = [output_string,source_string,&source_offset_list,
2282  piece_offset_starts,piece_offset_starts_end,num_target_points,
2283  target_to_piecei,piece_data,start_offset,owneri](const GA_SplittableRange &r)
2284  {
2285  GA_RWBatchHandleS output_string_handle(output_string);
2286  UT_ASSERT_P(output_string_handle.isValid());
2287  GA_ROHandleS source_string_handle(source_string);
2288  GA_Offset start;
2289  GA_Offset end;
2290  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2291  {
2292  exint targeti;
2293  exint piece_elementi;
2294  exint piece_element_count;
2295  const GA_OffsetList *piece_offset_list;
2296  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2297  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
2298  num_target_points, target_to_piecei, piece_data, owneri,
2299  &source_offset_list, source_offset_list.size());
2300 
2301  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2302  {
2303  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
2304  output_string_handle.set(dest_off, source_string_handle.get(source_off));
2305 
2306  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2307  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
2308  }
2309  }
2310  };
2311  if (copy_source_attribs_in_parallel)
2312  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owneri], functor);
2313  else
2314  functor(output_splittable_ranges[owneri]);
2315  }
2316  else
2317  {
2318  auto &&functor = [output_attrib,source_attrib,&source_offset_list,
2319  piece_offset_starts,piece_offset_starts_end,num_target_points,
2320  target_to_piecei,piece_data,start_offset,owneri](const GA_SplittableRange &r)
2321  {
2322  GA_Offset start;
2323  GA_Offset end;
2324  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2325  {
2326  exint targeti;
2327  exint piece_elementi;
2328  exint piece_element_count;
2329  const GA_OffsetList *piece_offset_list;
2330  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2331  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
2332  num_target_points, target_to_piecei, piece_data, owneri,
2333  &source_offset_list, source_offset_list.size());
2334 
2335  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2336  {
2337  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
2338  output_attrib->copy(dest_off, *source_attrib, source_off);
2339 
2340  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2341  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
2342  }
2343  }
2344  };
2345  if (copy_source_attribs_in_parallel)
2346  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owneri], functor);
2347  else
2348  functor(output_splittable_ranges[owneri]);
2349  }
2350  }
2351  }
2352  }
2353 
2354  // Don't forget the element groups
2355  for (int owneri = 0; owneri < 3; ++owneri)
2356  {
2357  const exint *piece_offset_starts = nullptr;
2358  const exint *piece_offset_starts_end = nullptr;
2359  if (owner_piece_offset_starts)
2360  {
2361  auto &array = owner_piece_offset_starts[owneri];
2362  piece_offset_starts = array.getArray();
2363  piece_offset_starts_end = array.getArray() + array.size();
2364  }
2365 
2366  const GA_OffsetList &source_offset_list = source_offset_lists[owneri];
2367  GA_AttributeOwner owner = GA_AttributeOwner(owneri);
2368  const GA_IndexMap &index_map = output_geo->getIndexMap(owner);
2369  if (index_map.indexSize() == 0)
2370  continue;
2371 
2372  // To get the start offset of the copied geometry without the overhead of using a GA_Iterator
2373  // (which copyies the GA_Range), we just use iterateCreate and iterateRewind directly.
2374  GA_IteratorState iterator_state;
2375  output_splittable_ranges[owner].iterateCreate(iterator_state);
2376  GA_Offset start_offset;
2377  GA_Offset first_block_end;
2378  output_splittable_ranges[owner].iterateRewind(iterator_state, start_offset, first_block_end);
2379 
2380  for (auto it = output_geo->getElementGroupTable(owner).beginTraverse(); !it.atEnd(); ++it)
2381  {
2382  GA_ElementGroup *output_group = it.group();
2383  UT_ASSERT_MSG(!output_group->isInternal(),
2384  "GUremoveUnnecessaryAttribs removes internal groups and "
2385  "GUaddAttributesFromSourceOrTarget doesn't add them");
2386 
2387  const UT_StringHolder &name = output_group->getName();
2388 
2389  // Check for interactions with target groups.
2390  AttribCombineMethod target_method = AttribCombineMethod::NONE;
2391  if (target)
2392  {
2393  auto target_it = target_group_info->find(name);
2394  if (!target_it.atEnd())
2395  {
2396  if (target_it->second.myCopyTo == owner)
2397  {
2398  target_method = target_it->second.myCombineMethod;
2399 
2400  // Target groups take precedence if copying.
2401  if (target_method == AttribCombineMethod::COPY)
2402  continue;
2403  }
2404  else if (target_it->second.myCombineMethod == AttribCombineMethod::COPY &&
2405  target_it->second.myCopyTo == guConflictAttribOwner(owner))
2406  {
2407  // Target groups take precedence if copying,
2408  // including if destination type differs between point and vertex.
2409  // NOTE: We don't have to check point vs. vertex for
2410  // mult/add/sub, because we already forced target type
2411  // to match source type in those situations.
2412  continue;
2413  }
2414  }
2415  }
2416 
2417  AttribCombineMethod prev_target_method = AttribCombineMethod::NONE;
2419  bool target_changed = false;
2420  if (target)
2421  {
2422  prev_target_it = cache->myTargetGroupInfo.find(name);
2423  if (!prev_target_it.atEnd())
2424  {
2425  if (prev_target_it->second.myCopyTo == owner)
2426  {
2427  prev_target_method = prev_target_it->second.myCombineMethod;
2428 
2429  if (prev_target_method != AttribCombineMethod::NONE)
2430  {
2431  const GA_Attribute *target_group = target->findPointGroup(name);
2432  GA_DataId prev_target_dataid = prev_target_it->second.myDataID;
2433  target_changed = !target_group ||
2434  !GAisValid(prev_target_dataid) ||
2435  !GAisValid(target_group->getDataId()) ||
2436  target_group->getDataId() != prev_target_dataid;
2437  }
2438  }
2439  }
2440  if (target_method != prev_target_method)
2441  {
2442  target_changed = true;
2443  }
2444  }
2445 
2446  const GA_ElementGroup *source_group = source->getElementGroupTable(owner).find(name);
2447  UT_ASSERT(source_group);
2448 
2449  if (cache != nullptr)
2450  {
2451  // If !topology_changed and the source_group data ID hasn't changed since last time, don't re-copy.
2452  if (!topology_changed && !target_changed)
2453  {
2454  auto it = cache->mySourceGroupDataIDs[owneri].find(name);
2455  if (!it.atEnd() && it->second != GA_INVALID_DATAID && it->second == source_group->getDataId())
2456  continue;
2457  }
2458  cache->mySourceGroupDataIDs[owneri][name] = source_group->getDataId();
2459 
2460  // Remove any entry from the target cache, so that the code for
2461  // applying target groups is forced to re-apply the operation.
2462  if (target && !prev_target_it.atEnd())
2463  cache->myTargetAttribInfo.erase(prev_target_it);
2464  }
2465 
2466  output_group->bumpDataId();
2467  output_group->invalidateGroupEntries();
2468 
2469  auto &&functor = [output_group,source_group,&source_offset_list,
2470  piece_offset_starts,piece_offset_starts_end,owneri,target_to_piecei,piece_data,
2471  num_target_points,start_offset](const GA_SplittableRange &r)
2472  {
2473  GA_Offset start;
2474  GA_Offset end;
2475  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2476  {
2477  exint targeti;
2478  exint piece_elementi;
2479  exint piece_element_count;
2480  const GA_OffsetList *piece_offset_list;
2481  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2482  &piece_offset_list, start_offset, piece_offset_starts, piece_offset_starts_end,
2483  num_target_points, target_to_piecei, piece_data, owneri,
2484  &source_offset_list, source_offset_list.size());
2485 
2486  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2487  {
2488  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
2489  output_group->setElement(dest_off, source_group->contains(source_off));
2490 
2491  guIteratePieceElement(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2492  num_target_points, target_to_piecei, piece_data, owneri, piece_offset_list);
2493  }
2494  }
2495  };
2496  if (copy_source_attribs_in_parallel)
2497  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owneri], functor);
2498  else
2499  functor(output_splittable_ranges[owneri]);
2500  }
2501  }
2502 
2503  // Don't forget the edge groups, either.
2504  // FIXME: This doesn't work for the piece attribute case!!!
2505  GA_PageArray<exint,1> source_to_sourcei;
2506  bool all_source_points;
2507  if (output_geo->edgeGroups().entries() > 0 && piece_data == nullptr)
2508  {
2509  // We need a source-point-offset-to-source-point-in-group mapping.
2510  const GA_OffsetList &source_point_list = source_offset_lists[GA_ATTRIB_POINT];
2511  all_source_points = source_point_list.isSame(source->getPointMap().getOffsetFromIndexList());
2512  if (!all_source_points)
2513  {
2514  source_to_sourcei.setSize(source->getNumPointOffsets(), exint(-1));
2515  for (exint i = 0, n = source_point_list.size(); i < n; ++i)
2516  {
2517  source_to_sourcei.set(source_point_list[i], i);
2518  }
2519  }
2520  }
2521  for (auto it = output_geo->edgeGroups().beginTraverse(); piece_data == nullptr && !it.atEnd(); ++it)
2522  {
2523  GA_EdgeGroup *output_group = it.group();
2524  UT_ASSERT_MSG(!output_group->isInternal(),
2525  "GUremoveUnnecessaryAttribs removes internal groups and "
2526  "GUaddAttributesFromSourceOrTarget doesn't add them");
2527  const GA_EdgeGroup *source_group = source->findEdgeGroup(output_group->getName());
2528  UT_ASSERT(source_group);
2529 
2530  if (cache != nullptr)
2531  {
2532  // If !topology_changed and the source_group data ID hasn't changed since last time, don't re-copy.
2533  if (!topology_changed)
2534  {
2535  auto it = cache->mySourceEdgeGroupDataIDs.find(source_group->getName());
2536  if (!it.atEnd() && it->second != GA_INVALID_DATAID && it->second == source_group->getDataId())
2537  continue;
2538  }
2539  cache->mySourceEdgeGroupDataIDs[source_group->getName()] = source_group->getDataId();
2540  }
2541  output_group->bumpDataId();
2542 
2543  // There are two main possible approaches:
2544  // A) Iterate through source_group, checking whether both points are in source_pointgroup (if source_pointgroup is non-null)
2545  // B) Iterate through all edges of one copy in output_geo, checking whether corresponding edges are in source_group
2546  // After either, the results from the first copy could be easily duplicated for other copies,
2547  // but which one is more efficient depends on the portion of source_group that's in the output
2548  // and the portion of output_geo that is not in source_group.
2549  // I've semi-arbitrarily chosen A, under the assumption that most edge groups are a small
2550  // portion of the total edges in the geometry and that the most common case is to copy
2551  // all of the input geometry.
2552 
2553  const exint source_point_count = source_offset_lists[GA_ATTRIB_POINT].size();
2554 
2555  for (auto it = source_group->begin(); !it.atEnd(); ++it)
2556  {
2557  const GA_Edge source_edge = it.getEdge();
2558  GA_Index index0;
2559  GA_Index index1;
2560  if (all_source_points)
2561  {
2562  index0 = source->pointIndex(source_edge.p0());
2563  index1 = source->pointIndex(source_edge.p1());
2564  }
2565  else
2566  {
2567  index0 = GA_Index(source_to_sourcei.get(source_edge.p0()));
2568  index1 = GA_Index(source_to_sourcei.get(source_edge.p1()));
2569 
2570  // Both of the points must be in the source point list.
2571  if (index0 == -1 || index1 == -1)
2572  continue;
2573  }
2574  GA_Edge output_edge(
2575  output_geo->pointOffset(index0),
2576  output_geo->pointOffset(index1));
2577 
2578  UT_ASSERT(num_target_points > 0);
2579  output_group->add(output_edge);
2580 
2581  for (exint copy = 1; copy < num_target_points; ++copy)
2582  {
2583  output_edge.p0() += source_point_count;
2584  output_edge.p1() += source_point_count;
2585  output_group->add(output_edge);
2586  }
2587  }
2588  }
2589 
2590  // If we're transforming, apply the transform to transforming primitives like in GEO_Detail::transform
2591  if (!no_transforms && output_geo->hasTransformingPrimitives() && (topology_changed || transforms_changed))
2592  {
2593  output_geo->getPrimitiveList().bumpDataId();
2594 
2595  UT_ASSERT(cache != nullptr);
2596 
2597  const exint *piece_offset_starts = nullptr;
2598  const exint *piece_offset_starts_end = nullptr;
2599  if (owner_piece_offset_starts)
2600  {
2601  auto &array = owner_piece_offset_starts[GA_ATTRIB_PRIMITIVE];
2602  piece_offset_starts = array.getArray();
2603  piece_offset_starts_end = array.getArray() + array.size();
2604  }
2605 
2606  const GA_OffsetList &source_prim_list = source_offset_lists[GA_ATTRIB_PRIMITIVE];
2607  const UT_Matrix3D *transform_matrices_3d = cache->myTransformMatrices3D.get();
2608  const UT_Vector3D *transform_translates_3d = cache->myTransformTranslates3D.get();
2609  auto &&functor = [transform_matrices_3d,transform_translates_3d,output_geo,
2610  piece_offset_starts,piece_offset_starts_end,target_to_piecei,piece_data,
2611  num_target_points,topology_changed,source,&source_prim_list](const GA_SplittableRange &r)
2612  {
2613  const exint source_prim_count = source_prim_list.size();
2614  GA_Offset start_primoff = output_geo->primitiveOffset(GA_Index(0));
2615  GA_Offset start;
2616  GA_Offset end;
2617  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2618  {
2619  exint targeti;
2620  exint piece_elementi;
2621  exint piece_element_count;
2622  const GA_OffsetList *piece_offset_list;
2623  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2624  &piece_offset_list, start_primoff, piece_offset_starts, piece_offset_starts_end,
2625  num_target_points, target_to_piecei, piece_data, GA_ATTRIB_PRIMITIVE,
2626  &source_prim_list, source_prim_count);
2627 
2629  if (transform_matrices_3d)
2630  transform = UT_Matrix4D(transform_matrices_3d[targeti]);
2631  else
2632  transform.identity();
2633  transform.setTranslates(transform_translates_3d[targeti]);
2634 
2635  // TODO: Remove the cast to UT_Matrix4 once
2636  // GEO_Primitive::transform accepts UT_Matrix4D.
2637  UT_Matrix4 transformf(transform);
2638 
2639  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2640  {
2641  GEO_Primitive *prim = output_geo->getGEOPrimitive(dest_off);
2642 
2643  if (!topology_changed)
2644  {
2645  // We haven't re-copied the untransformed primitive,
2646  // so we need to copy it now. Note that VDB primitives
2647  // sometimes transform actual voxel data when transformed,
2648  // so we can't just call setLocalTransform.
2649  GA_Offset source_off = (*piece_offset_list)[piece_elementi];
2650  const GA_Primitive *source_prim = source->getPrimitive(source_off);
2651  prim->copySubclassData(source_prim);
2652  }
2653 
2654  prim->transform(transformf);
2655 
2656  // The iteration below is similar to guIteratePieceElement above,
2657  // but also assembling target transforms.
2658  ++piece_elementi;
2659  // NOTE: This must be while instead of if, because there can be zero primitives in a piece.
2660  while (piece_elementi >= piece_element_count)
2661  {
2662  piece_elementi = 0;
2663  ++targeti;
2664 
2665  if (targeti >= num_target_points)
2666  break;
2667 
2668  if (transform_matrices_3d)
2669  transform = UT_Matrix4D(transform_matrices_3d[targeti]);
2670  else
2671  transform.identity();
2672  transform.setTranslates(transform_translates_3d[targeti]);
2673  // TODO: Remove the cast to UT_Matrix4 once
2674  // GEO_Primitive::transform accepts UT_Matrix4D.
2675  transformf = UT_Matrix4(transform);
2676 
2677  if (piece_offset_starts != nullptr)
2678  {
2679  exint piecei = target_to_piecei[targeti];
2680  const GU_CopyToPointsCache::PieceData &current_piece = piece_data[piecei];
2681  piece_offset_list = &current_piece.mySourceOffsetLists[GA_ATTRIB_PRIMITIVE];
2682  piece_element_count = piece_offset_list->size();
2683  }
2684  }
2685  }
2686  }
2687  };
2688  if (copy_source_attribs_in_parallel)
2689  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[GA_ATTRIB_PRIMITIVE], functor);
2690  else
2691  functor(output_splittable_ranges[GA_ATTRIB_PRIMITIVE]);
2692  }
2693 
2694  if (copy_source_attribs_in_parallel)
2695  task_group.wait();
2696 }
2697 
2698 void
2700  GU_Detail *const output_geo,
2701  const GA_SplittableRange *const output_splittable_ranges,
2702  const exint ncopies,
2703  GU_CopyToPointsCache *const cache,
2704  const exint source_point_count,
2705  const exint source_vertex_count,
2706  const exint source_prim_count,
2707  const exint *const num_target_attribs,
2708  const GA_OffsetListRef &target_point_list,
2709  const GU_Detail *const target,
2710  GU_CopyToPointsCache::TargetAttribInfoMap &target_attrib_info,
2711  GU_CopyToPointsCache::TargetAttribInfoMap &target_group_info,
2712  const bool topology_changed,
2713  const exint *const target_to_piecei,
2714  const UT_Array<exint> *const owner_piece_offset_starts,
2715  const GU_CopyToPointsCache::PieceData *const piece_data)
2716 {
2717  if (ncopies <= 0)
2718  return;
2719 
2720  using AttribCombineMethod = GU_CopyToPointsCache::AttribCombineMethod;
2721 
2722  const exint num_target_output_elements =
2723  output_geo->getNumVertices() * num_target_attribs[GA_ATTRIB_VERTEX] +
2724  output_geo->getNumPoints() * num_target_attribs[GA_ATTRIB_POINT] +
2725  output_geo->getNumPrimitives() * num_target_attribs[GA_ATTRIB_PRIMITIVE];
2726  const bool copy_target_attribs_in_parallel = num_target_output_elements >= 4096;
2727 
2728  if (num_target_output_elements <= 0)
2729  return;
2730 
2731  exint source_element_counts[3] =
2732  {
2733  source_vertex_count,
2734  source_point_count,
2735  source_prim_count
2736  };
2738  "Arrays above and loop below are assuming the order of GA_AttributeOwner enum");
2739 
2740  const exint num_target_points = target_point_list.size();
2741 
2742  UT_TaskGroup task_group;
2743  for (auto it = target_attrib_info.begin(); !it.atEnd(); ++it)
2744  {
2745  const UT_StringHolder &name = it->first;
2746  const GA_Attribute *target_attrib = target->findPointAttribute(name);
2747  UT_ASSERT(target_attrib != nullptr);
2748 
2749  const GA_AttributeOwner owner = it->second.myCopyTo;
2750  GA_Attribute *output_attrib = output_geo->findAttribute(owner, name);
2751  UT_ASSERT(output_attrib != nullptr);
2752 
2753  const GA_IndexMap &index_map = output_geo->getIndexMap(owner);
2754  if (index_map.indexSize() == 0)
2755  continue;
2756 
2757  // To get the start offset of the copied geometry without the overhead of using a GA_Iterator
2758  // (which copyies the GA_Range), we just use iterateCreate and iterateRewind directly.
2759  GA_IteratorState iterator_state;
2760  output_splittable_ranges[owner].iterateCreate(iterator_state);
2761  GA_Offset start_offset;
2762  GA_Offset first_block_end;
2763  output_splittable_ranges[owner].iterateRewind(iterator_state, start_offset, first_block_end);
2764 
2765  exint num_elements_per_copy = source_element_counts[owner];
2766  const AttribCombineMethod method = it->second.myCombineMethod;
2767  if (method == AttribCombineMethod::NONE)
2768  continue;
2769 
2770  if (!topology_changed)
2771  {
2772  // If unchanged since last cook, no need to re-apply operation.
2773  // NOTE: GUcopyAttributesFromSource removed cache entries for
2774  // attributes that were re-copied from source.
2775  auto prev_it = cache->myTargetAttribInfo.find(name);
2776  if (!prev_it.atEnd() &&
2777  prev_it->second.myDataID == it->second.myDataID &&
2778  prev_it->second.myCopyTo == it->second.myCopyTo &&
2779  prev_it->second.myCombineMethod == it->second.myCombineMethod)
2780  {
2781  continue;
2782  }
2783  }
2784 
2785  const exint *piece_offset_starts = nullptr;
2786  const exint *piece_offset_starts_end = nullptr;
2787  if (owner_piece_offset_starts)
2788  {
2789  auto &array = owner_piece_offset_starts[owner];
2790  piece_offset_starts = array.getArray();
2791  piece_offset_starts_end = array.getArray() + array.size();
2792  }
2793 
2794  output_attrib->bumpDataId();
2795 
2796  if (method == AttribCombineMethod::COPY)
2797  {
2798  // Copy attribute values from target to output_geo
2799  // FIXME: Specialize for numeric and string attributes to reduce
2800  // performance impact of the GA_Attribute::copy() virtual function calls.
2801  auto &&functor = [output_attrib,target_attrib,&target_point_list,num_elements_per_copy,
2802  start_offset,piece_offset_starts,piece_offset_starts_end,
2803  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
2804  {
2805  GA_Offset start;
2806  GA_Offset end;
2807  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2808  {
2809  exint targeti;
2810  exint piece_elementi;
2811  exint piece_element_count;
2812  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2813  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
2814  num_target_points, target_to_piecei, piece_data, owner,
2815  nullptr, num_elements_per_copy);
2816 
2817  GA_Offset target_off = target_point_list[targeti];
2818 
2819  // FIXME: Find longer contiguous spans to copy, for better performance.
2820  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2821  {
2822  output_attrib->copy(dest_off, *target_attrib, target_off);
2823 
2824  guIteratePieceElementOff(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2825  num_target_points, target_to_piecei, piece_data, owner, target_off, target_point_list);
2826  }
2827  }
2828  };
2829  if (copy_target_attribs_in_parallel)
2830  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
2831  else
2832  functor(output_splittable_ranges[owner]);
2833  }
2834  else
2835  {
2836  GA_ATINumeric *output_numeric = UTverify_cast<GA_ATINumeric *>(output_attrib);
2837  const GA_ATINumeric *target_numeric = UTverify_cast<const GA_ATINumeric *>(target_attrib);
2838  const exint min_tuple_size = SYSmin(output_numeric->getTupleSize(), target_numeric->getTupleSize());
2839  GA_PageArray<void, -1> &output_data = output_numeric->getData();
2840  const GA_PageArray<void, -1> &target_data = target_numeric->getData();
2841  if (method == AttribCombineMethod::MULTIPLY)
2842  {
2843  // Multiply by target attribute values: output_geo (i.e. source) * target.
2844  auto &&functor = [&output_data,&target_data,&target_point_list,num_elements_per_copy,min_tuple_size,
2845  start_offset,piece_offset_starts,piece_offset_starts_end,
2846  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
2847  {
2848  GA_Offset start;
2849  GA_Offset end;
2850  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2851  {
2852  exint targeti;
2853  exint piece_elementi;
2854  exint piece_element_count;
2855  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2856  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
2857  num_target_points, target_to_piecei, piece_data, owner,
2858  nullptr, num_elements_per_copy);
2859 
2860  GA_Offset target_off = target_point_list[targeti];
2861 
2862  // FIXME: Find longer contiguous spans to copy, for better performance.
2863  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2864  {
2865  for (exint component = 0; component < min_tuple_size; ++component)
2866  {
2867  // FIXME: This is just mimicking what the old code did, but
2868  // it doesn't maintain full precision for 64-bit integers,
2869  // so we can do better.
2870  // The performance probably isn't great, either.
2871  // TODO: Maybe we should handle transform matrices and quaternions
2872  // in a special way, too.
2873  fpreal64 existing_value = output_data.get<fpreal64>(dest_off, component);
2874  fpreal64 target_value = target_data.get<fpreal64>(target_off, component);
2875  fpreal64 product = existing_value*target_value;
2876  output_data.set(dest_off, component, product);
2877  }
2878 
2879  guIteratePieceElementOff(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2880  num_target_points, target_to_piecei, piece_data, owner, target_off, target_point_list);
2881  }
2882  }
2883  };
2884  if (copy_target_attribs_in_parallel)
2885  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
2886  else
2887  functor(output_splittable_ranges[owner]);
2888  }
2889  else if (method == AttribCombineMethod::ADD)
2890  {
2891  // Add target attribute values: output_geo (i.e. source) + target.
2892  auto &&functor = [&output_data,&target_data,&target_point_list,num_elements_per_copy,min_tuple_size,
2893  start_offset,piece_offset_starts,piece_offset_starts_end,
2894  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
2895  {
2896  GA_Offset start;
2897  GA_Offset end;
2898  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2899  {
2900  exint targeti;
2901  exint piece_elementi;
2902  exint piece_element_count;
2903  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2904  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
2905  num_target_points, target_to_piecei, piece_data, owner,
2906  nullptr, num_elements_per_copy);
2907 
2908  GA_Offset target_off = target_point_list[targeti];
2909 
2910  // FIXME: Find longer contiguous spans to copy, for better performance.
2911  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2912  {
2913  for (exint component = 0; component < min_tuple_size; ++component)
2914  {
2915  // FIXME: This is just mimicking what the old code did, but
2916  // it doesn't maintain full precision for 64-bit integers,
2917  // so we can do better.
2918  // The performance probably isn't great, either.
2919  fpreal64 existing_value = output_data.get<fpreal64>(dest_off, component);
2920  fpreal64 target_value = target_data.get<fpreal64>(target_off, component);
2921  fpreal64 sum = existing_value + target_value;
2922  output_data.set(dest_off, component, sum);
2923  }
2924 
2925  guIteratePieceElementOff(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2926  num_target_points, target_to_piecei, piece_data, owner, target_off, target_point_list);
2927  }
2928  }
2929  };
2930  if (copy_target_attribs_in_parallel)
2931  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
2932  else
2933  functor(output_splittable_ranges[owner]);
2934  }
2935  else // (method == AttribCombineMethod::SUBTRACT)
2936  {
2937  // Subtract target attribute values: output_geo (i.e. source or zero) - target.
2938  auto &&functor = [&output_data,&target_data,&target_point_list,num_elements_per_copy,min_tuple_size,
2939  start_offset,piece_offset_starts,piece_offset_starts_end,
2940  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
2941  {
2942  GA_Offset start;
2943  GA_Offset end;
2944  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
2945  {
2946  exint targeti;
2947  exint piece_elementi;
2948  exint piece_element_count;
2949  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
2950  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
2951  num_target_points, target_to_piecei, piece_data, owner,
2952  nullptr, num_elements_per_copy);
2953 
2954  GA_Offset target_off = target_point_list[targeti];
2955 
2956  // FIXME: Find longer contiguous spans to copy, for better performance.
2957  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
2958  {
2959  for (exint component = 0; component < min_tuple_size; ++component)
2960  {
2961  // FIXME: This is just mimicking what the old code did, but
2962  // it doesn't maintain full precision for 64-bit integers,
2963  // so we can do better.
2964  // The performance probably isn't great, either.
2965  fpreal64 existing_value = output_data.get<fpreal64>(dest_off, component);
2966  fpreal64 target_value = target_data.get<fpreal64>(target_off, component);
2967  fpreal64 difference = existing_value - target_value;
2968  output_data.set(dest_off, component, difference);
2969  }
2970 
2971  guIteratePieceElementOff(piece_elementi, piece_element_count, targeti, piece_offset_starts,
2972  num_target_points, target_to_piecei, piece_data, owner, target_off, target_point_list);
2973  }
2974  }
2975  };
2976  if (copy_target_attribs_in_parallel)
2977  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
2978  else
2979  functor(output_splittable_ranges[owner]);
2980  }
2981  }
2982  }
2983  for (auto it = target_group_info.begin(); !it.atEnd(); ++it)
2984  {
2985  const UT_StringHolder &name = it->first;
2986  const GA_PointGroup *target_group = target->findPointGroup(name);
2987  UT_ASSERT(target_group != nullptr);
2988 
2989  const GA_AttributeOwner owner = it->second.myCopyTo;
2990  GA_ElementGroup *output_group = output_geo->findElementGroup(owner, name);
2991  UT_ASSERT(output_group != nullptr);
2992 
2993  const GA_IndexMap &index_map = output_geo->getIndexMap(owner);
2994  if (index_map.indexSize() == 0)
2995  continue;
2996 
2997  // To get the start offset of the copied geometry without the overhead of using a GA_Iterator
2998  // (which copyies the GA_Range), we just use iterateCreate and iterateRewind directly.
2999  GA_IteratorState iterator_state;
3000  output_splittable_ranges[owner].iterateCreate(iterator_state);
3001  GA_Offset start_offset;
3002  GA_Offset first_block_end;
3003  output_splittable_ranges[owner].iterateRewind(iterator_state, start_offset, first_block_end);
3004 
3005  const AttribCombineMethod method = it->second.myCombineMethod;
3006  if (method == AttribCombineMethod::NONE)
3007  continue;
3008 
3009  if (!topology_changed)
3010  {
3011  // If unchanged since last cook, no need to re-apply operation.
3012  // NOTE: GUcopyAttributesFromSource removed cache entries for
3013  // attributes that were re-copied from source.
3014  auto prev_it = cache->myTargetAttribInfo.find(name);
3015  if (!prev_it.atEnd() &&
3016  prev_it->second.myDataID == it->second.myDataID &&
3017  prev_it->second.myCopyTo == it->second.myCopyTo &&
3018  prev_it->second.myCombineMethod == it->second.myCombineMethod)
3019  {
3020  continue;
3021  }
3022  }
3023 
3024  const exint *piece_offset_starts = nullptr;
3025  const exint *piece_offset_starts_end = nullptr;
3026  if (owner_piece_offset_starts)
3027  {
3028  auto &array = owner_piece_offset_starts[owner];
3029  piece_offset_starts = array.getArray();
3030  piece_offset_starts_end = array.getArray() + array.size();
3031  }
3032 
3033  output_group->bumpDataId();
3034  output_group->invalidateGroupEntries();
3035 
3036  if (method == AttribCombineMethod::COPY)
3037  {
3038  // Copy group membership from target to output_geo
3039  exint num_elements_per_copy = source_element_counts[owner];
3040  auto &&functor = [output_group,target_group,&target_point_list,num_elements_per_copy,
3041  start_offset,piece_offset_starts,piece_offset_starts_end,
3042  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
3043  {
3044  GA_Offset start;
3045  GA_Offset end;
3046  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
3047  {
3048  exint targeti;
3049  exint piece_elementi;
3050  exint piece_element_count;
3051  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
3052  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
3053  num_target_points, target_to_piecei, piece_data, owner,
3054  nullptr, num_elements_per_copy);
3055 
3056  GA_Offset target_off = target_point_list[targeti];
3057  bool target_in_group = target_group->contains(target_off);
3058 
3059  // FIXME: Find longer contiguous spans to copy, for better performance.
3060  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
3061  {
3062  output_group->setElement(dest_off, target_in_group);
3063 
3064  guIteratePieceElementGroup(piece_elementi, piece_element_count, targeti, piece_offset_starts,
3065  num_target_points, target_to_piecei, piece_data, owner, target_in_group, target_point_list, target_group);
3066  }
3067  }
3068  };
3069  if (copy_target_attribs_in_parallel)
3070  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
3071  else
3072  functor(output_splittable_ranges[owner]);
3073  }
3074  else if (method == AttribCombineMethod::MULTIPLY)
3075  {
3076  // Intersect group membership from target to output_geo
3077  exint num_elements_per_copy = source_element_counts[owner];
3078  auto &&functor = [output_group,target_group,&target_point_list,num_elements_per_copy,
3079  start_offset,piece_offset_starts,piece_offset_starts_end,
3080  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
3081  {
3082  GA_Offset start;
3083  GA_Offset end;
3084  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
3085  {
3086  exint targeti;
3087  exint piece_elementi;
3088  exint piece_element_count;
3089  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
3090  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
3091  num_target_points, target_to_piecei, piece_data, owner,
3092  nullptr, num_elements_per_copy);
3093 
3094  GA_Offset target_off = target_point_list[targeti];
3095  bool target_in_group = target_group->contains(target_off);
3096 
3097  // FIXME: Find longer contiguous spans to copy, for better performance.
3098  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
3099  {
3100  if (!target_in_group)
3101  output_group->setElement(dest_off, false);
3102 
3103  guIteratePieceElementGroup(piece_elementi, piece_element_count, targeti, piece_offset_starts,
3104  num_target_points, target_to_piecei, piece_data, owner, target_in_group, target_point_list, target_group);
3105  }
3106  }
3107  };
3108  if (copy_target_attribs_in_parallel)
3109  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
3110  else
3111  functor(output_splittable_ranges[owner]);
3112  }
3113  else if (method == AttribCombineMethod::ADD)
3114  {
3115  // Union group membership from target to output_geo
3116  exint num_elements_per_copy = source_element_counts[owner];
3117  auto &&functor = [output_group,target_group,&target_point_list,num_elements_per_copy,
3118  start_offset,piece_offset_starts,piece_offset_starts_end,
3119  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
3120  {
3121  GA_Offset start;
3122  GA_Offset end;
3123  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
3124  {
3125  exint targeti;
3126  exint piece_elementi;
3127  exint piece_element_count;
3128  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
3129  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
3130  num_target_points, target_to_piecei, piece_data, owner,
3131  nullptr, num_elements_per_copy);
3132 
3133  GA_Offset target_off = target_point_list[targeti];
3134  bool target_in_group = target_group->contains(target_off);
3135 
3136  // FIXME: Find longer contiguous spans to copy, for better performance.
3137  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
3138  {
3139  if (target_in_group)
3140  output_group->setElement(dest_off, true);
3141 
3142  guIteratePieceElementGroup(piece_elementi, piece_element_count, targeti, piece_offset_starts,
3143  num_target_points, target_to_piecei, piece_data, owner, target_in_group, target_point_list, target_group);
3144  }
3145  }
3146  };
3147  if (copy_target_attribs_in_parallel)
3148  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
3149  else
3150  functor(output_splittable_ranges[owner]);
3151  }
3152  else // if (method == AttribCombineMethod::SUBTRACT)
3153  {
3154  // Subtract group membership of target from output_geo
3155  exint num_elements_per_copy = source_element_counts[owner];
3156  auto &&functor = [output_group,target_group,&target_point_list,num_elements_per_copy,
3157  start_offset,piece_offset_starts,piece_offset_starts_end,
3158  num_target_points,target_to_piecei,piece_data,owner](const GA_SplittableRange &r)
3159  {
3160  GA_Offset start;
3161  GA_Offset end;
3162  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
3163  {
3164  exint targeti;
3165  exint piece_elementi;
3166  exint piece_element_count;
3167  guFindStartInTarget(start, targeti, piece_elementi, piece_element_count,
3168  nullptr, start_offset, piece_offset_starts, piece_offset_starts_end,
3169  num_target_points, target_to_piecei, piece_data, owner,
3170  nullptr, num_elements_per_copy);
3171 
3172  GA_Offset target_off = target_point_list[targeti];
3173  bool target_in_group = target_group->contains(target_off);
3174 
3175  // FIXME: Find longer contiguous spans to copy, for better performance.
3176  for (GA_Offset dest_off = start; dest_off < end; ++dest_off)
3177  {
3178  if (target_in_group)
3179  output_group->setElement(dest_off, false);
3180 
3181  guIteratePieceElementGroup(piece_elementi, piece_element_count, targeti, piece_offset_starts,
3182  num_target_points, target_to_piecei, piece_data, owner, target_in_group, target_point_list, target_group);
3183  }
3184  }
3185  };
3186  if (copy_target_attribs_in_parallel)
3187  UTparallelForRunInTaskGroup(task_group, output_splittable_ranges[owner], functor);
3188  else
3189  functor(output_splittable_ranges[owner]);
3190  }
3191  }
3192 
3193  if (copy_target_attribs_in_parallel)
3194  task_group.wait();
3195 
3196  cache->myTargetAttribInfo.swap(target_attrib_info);
3197  cache->myTargetGroupInfo.swap(target_group_info);
3198  target_attrib_info.clear();
3199  target_group_info.clear();
3200 }
3201 
3202 void
3204  GU_Detail *output_geo,
3205  GU_CopyToPointsCache *cache,
3206  const bool had_transform_matrices,
3207  const exint num_packed_prims,
3208  const UT_Vector3 *const constant_pivot)
3209 {
3210  UT_ASSERT(num_packed_prims == output_geo->getNumPoints());
3211  UT_ASSERT(num_packed_prims == output_geo->getNumPrimitives());
3212  if (num_packed_prims <= 0)
3213  return;
3214 
3215  const UT_Matrix3D *const transform_matrices_3d = cache->myTransformMatrices3D.get();
3216  const UT_Vector3D *const transform_translates_3d = cache->myTransformTranslates3D.get();
3217  GA_RWHandleV3 output_pos3f(output_geo->getP());
3218  GA_RWHandleV3D output_pos3d(output_geo->getP());
3219  GA_Offset start_ptoff = output_geo->pointOffset(GA_Index(0));
3220  GA_Offset start_primoff = output_geo->primitiveOffset(GA_Index(0));
3221  auto &&functor = [output_geo,
3222  start_ptoff,start_primoff,transform_translates_3d,transform_matrices_3d,
3223  output_pos3f,output_pos3d,had_transform_matrices,constant_pivot](const GA_Range &r)
3224  {
3225  GA_Offset start;
3226  GA_Offset end;
3227  for (GA_Iterator it(r); it.fullBlockAdvance(start, end); )
3228  {
3229  if (transform_matrices_3d || had_transform_matrices)
3230  {
3231  UT_Matrix3D identity;
3232  if (!transform_matrices_3d)
3233  identity.identity();
3234 
3235  exint transformi = start - start_primoff;
3236  for (GA_Offset primoff = start; primoff < end; ++primoff, ++transformi)
3237  {
3238  GA_Primitive *prim = output_geo->getPrimitive(primoff);
3239  GU_PrimPacked *packed_prim = UTverify_cast<GU_PrimPacked *>(prim);
3240  if (transform_matrices_3d)
3241  {
3242  const UT_Matrix3D &transform = transform_matrices_3d[transformi];
3243  packed_prim->setLocalTransform(transform);
3244  }
3245  else // had_transform_matrices
3246  {
3247  packed_prim->setLocalTransform(identity);
3248  }
3249  }
3250  }
3251 
3252  if (!constant_pivot)
3253  {
3254  // Apply pivots from primitives to P.
3255  exint transformi = start - start_primoff;
3256  for (GA_Offset primoff = start; primoff < end; ++primoff, ++transformi)
3257  {
3258  GA_Primitive *prim = output_geo->getPrimitive(primoff);
3259  GU_PrimPacked *packed_prim = UTverify_cast<GU_PrimPacked *>(prim);
3260  GA_Offset ptoff = start_ptoff + transformi;
3261  UT_Vector3 pivot;
3262  packed_prim->getPivot(pivot);
3263  // Need to transform pivot position before adding it to P
3264  UT_Vector3D transformed_pivot(pivot);
3265  if (transform_matrices_3d)
3266  transformed_pivot = transformed_pivot*transform_matrices_3d[transformi];
3267  if (transform_translates_3d)
3268  transformed_pivot += transform_translates_3d[transformi];
3269  output_pos3d.set(ptoff, transformed_pivot);
3270  }
3271  continue;
3272  }
3273 
3274  // NOTE: This conversion to ptoff relies on that there's one point per primitive,
3275  // that both are contiguous, and that both are in the same order.
3276  exint transformi = start - start_primoff;
3277  GA_Offset local_start_ptoff = start_ptoff + transformi;
3278  GA_Offset local_end_ptoff = start_ptoff + (end-start_primoff);
3279  if (!transform_translates_3d)
3280  {
3281  for (GA_Offset ptoff = local_start_ptoff; ptoff < local_end_ptoff; ++ptoff, ++transformi)
3282  {
3283  // Need to transform pivot position before adding it to P
3284  UT_Vector3D transformed_pivot(*constant_pivot);
3285  if (transform_matrices_3d)
3286  transformed_pivot = transformed_pivot*transform_matrices_3d[transformi];
3287 
3288  output_pos3d.set(ptoff, transformed_pivot);
3289  }
3290  }
3291  else if (constant_pivot->isZero())
3292  {
3293  if (output_pos3f->getStorage() == GA_STORE_REAL64)
3294  {
3295  for (GA_Offset ptoff = local_start_ptoff; ptoff < local_end_ptoff; ++ptoff, ++transformi)
3296  {
3297  output_pos3d.set(ptoff, transform_translates_3d[transformi]);
3298  }
3299  }
3300  else
3301  {
3302  for (GA_Offset ptoff = local_start_ptoff; ptoff < local_end_ptoff; ++ptoff, ++transformi)
3303  {
3304  output_pos3f.set(ptoff, UT_Vector3F(transform_translates_3d[transformi]));
3305  }
3306  }
3307  }
3308  else
3309  {
3310  for (GA_Offset ptoff = local_start_ptoff; ptoff < local_end_ptoff; ++ptoff, ++transformi)
3311  {
3312  // Need to transform pivot position before adding it to P
3313  UT_Vector3D transformed_pivot(*constant_pivot);
3314  if (transform_matrices_3d)
3315  transformed_pivot = transformed_pivot*transform_matrices_3d[transformi];
3316 
3317  output_pos3d.set(ptoff, transform_translates_3d[transformi] + transformed_pivot);
3318  }
3319  }
3320  }
3321  };
3322 
3323  constexpr exint PARALLEL_THRESHOLD = 2048;
3324  if (num_packed_prims >= PARALLEL_THRESHOLD)
3326  else
3327  functor(output_geo->getPrimitiveRange());
3328 }
3329 
3330 void
3332  GU_Detail *output_geo,
3333  GU_CopyToPointsCache *cache,
3334  const bool topology_changed,
3335  const bool had_transform_matrices,
3336  const GU_Detail *const target,
3337  const GA_OffsetListRef &target_point_list,
3338  GU_CopyToPointsCache::TargetAttribInfoMap &target_attrib_info,
3339  GU_CopyToPointsCache::TargetAttribInfoMap &target_group_info,
3340  const UT_Vector3 *const constant_pivot)
3341 {
3342  // *** Transform ***
3343 
3344  exint num_target_points = target_point_list.size();
3345  GUupdatePackedPrimTransforms(output_geo, cache, had_transform_matrices, num_target_points, constant_pivot);
3346 
3347  // Remove any attributes from output_geo that are not being applied from target.
3348  // (They've already all been removed if topology_changed.)
3349  if (!topology_changed)
3350  {
3352  output_geo,
3353  nullptr,
3354  target,
3355  cache,
3356  &target_attrib_info,
3357  &target_group_info);
3358  }
3359 
3360  // Add attributes from target that are not in output_geo.
3361  exint num_target_attribs[3] = {0,0,0};
3363  "Array above is assuming the order of GA_AttributeOwner enum");
3364 
3366  output_geo,
3367  nullptr,
3368  nullptr,
3369  false,
3370  nullptr,
3371  target,
3372  &target_attrib_info,
3373  &target_group_info,
3374  num_target_attribs);
3375 
3376  // Copy attributes from target points
3377  GA_SplittableRange output_splittable_ranges[3] =
3378  {
3379  GA_SplittableRange(output_geo->getVertexRange()),
3380  GA_SplittableRange(output_geo->getPointRange()),
3381  GA_SplittableRange(output_geo->getPrimitiveRange())
3382  };
3384  "Array above is assuming the order of GA_AttributeOwner enum");
3385 
3387  output_geo,
3388  output_splittable_ranges,
3389  num_target_points,
3390  cache,
3391  1, 1, 1, // 1 point, 1 vertex, 1 primitive
3392  num_target_attribs,
3393  target_point_list,
3394  target,
3395  target_attrib_info,
3396  target_group_info,
3397  topology_changed);
3398 }
3399 
3400 void
3402  GU_Detail *output_geo,
3403  const GEO_ViewportLOD lod,
3404  const GU_CopyToPointsCache::PackedPivot pivot_type,
3405  GU_CopyToPointsCache *cache,
3406  const GU_ConstDetailHandle source_handle,
3407  const GU_Detail *source,
3408  const GA_PointGroup *source_pointgroup,
3409  const GA_PrimitiveGroup *source_primgroup,
3410  bool source_topology_changed,
3411  bool had_transform_matrices,
3412  bool transforms_changed,
3413  const exint num_packed_prims,
3414  const GU_Detail *target,
3415  const GA_OffsetListRef *target_point_list,
3416  GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info,
3417  GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info)
3418 {
3419  const bool topology_changed =
3420  !cache->myPrevPack ||
3421  num_packed_prims != cache->myPrevTargetPtCount ||
3422  output_geo->getNumPoints() != num_packed_prims ||
3423  output_geo->getUniqueId() != cache->myPrevOutputDetailID;
3424  const bool source_changed =
3425  !cache->myPrevPack ||
3426  source->getUniqueId() != cache->myPrevSourceUniqueID ||
3427  source->getMetaCacheCount() != cache->myPrevSourceMetaCacheCount ||
3428  source_topology_changed;
3429  const bool lod_changed = (lod != cache->myPrevViewportLOD);
3430  const bool source_intrinsic_changed =
3431  source_changed ||
3432  pivot_type != cache->myPrevPivotEnum ||
3433  lod_changed;
3434 
3435  // *** Creating Packed Primitives ***
3436 
3437  if (topology_changed)
3438  {
3439  output_geo->clearAndDestroy();
3440 
3441  GUcreateEmptyPackedGeometryPrims(output_geo, num_packed_prims);
3442  }
3443  GA_Offset start_primoff = (output_geo->getNumPrimitives() > 0) ? output_geo->primitiveOffset(GA_Index(0)) : GA_INVALID_OFFSET;
3444 
3445  // *** Updating Content ***
3446 
3447  bool centroid_pivot = (pivot_type == GU_CopyToPointsCache::PackedPivot::CENTROID);
3448 
3449  UT_Vector3 pivot(0,0,0);
3450  if ((source_intrinsic_changed || source_changed || topology_changed) && num_packed_prims > 0)
3451  {
3452  // Create a packed geometry implementation
3453  const GU_PackedGeometry *packed_geo = nullptr;
3454  GU_PackedGeometry *packed_geo_nc = nullptr;
3455  const bool impl_changed = (source_changed || topology_changed);
3456  if (impl_changed)
3457  {
3458  packed_geo_nc = new GU_PackedGeometry();
3459  if (!source_primgroup && !source_pointgroup)
3460  {
3461  // Easy case: just instance the source input geometry.
3462  packed_geo_nc->setDetailPtr(source_handle);
3463  }
3464  else
3465  {
3466  // Hard case: copy the source geometry in the groups.
3467  GU_DetailHandle detail_handle;
3468  GU_Detail *packed_detail = new GU_Detail();
3469  detail_handle.allocateAndSet(packed_detail);
3470  exint source_point_count = source_pointgroup ? source_pointgroup->entries() : source->getNumPoints();
3471  exint source_prim_count = source_primgroup ? source_primgroup->entries() : source->getNumPrimitives();
3472 
3474  packed_detail,
3475  source,
3476  source_point_count,
3477  cache->mySourceVertexCount,
3478  source_prim_count,
3482  source_pointgroup,
3483  source_primgroup,
3484  1);
3485 
3486  exint num_source_attribs[3];
3488  "Array above depends on owners other than detail being less than 3");
3490  packed_detail,
3491  source,
3492  num_source_attribs);
3493 
3494  GA_SplittableRange output_splittable_ranges[3] =
3495  {
3496  GA_SplittableRange(packed_detail->getVertexRange()),
3497  GA_SplittableRange(packed_detail->getPointRange()),
3498  GA_SplittableRange(packed_detail->getPrimitiveRange())
3499  };
3501  "Array above is assuming the order of GA_AttributeOwner enum");
3502 
3504  packed_detail,
3505  output_splittable_ranges,
3506  source,
3507  1,
3508  cache,
3509  cache->mySourceOffsetLists,
3510  num_source_attribs,
3511  true,
3512  false,
3513  false,
3514  true,
3515  true);
3516 
3517  packed_geo_nc->setDetailPtr(detail_handle);
3518  }
3519 
3520  packed_geo = packed_geo_nc;
3521 
3522  // Add all of the reference counter refs at once
3523  intrusive_ptr_add_ref(packed_geo, num_packed_prims);
3524  }
3525  else
3526  {
3527  // source_intrinsic_changed is true, but source_changed is false and topology_changed is false.
3528  GA_Primitive *prim = output_geo->getPrimitive(start_primoff);
3529  GU_PrimPacked *packed_prim = UTverify_cast<GU_PrimPacked *>(prim);
3530  packed_geo = UTverify_cast<const GU_PackedGeometry*>(packed_prim->sharedImplementation());
3531  }
3532 
3533  // Cache the bounds in advance, even if we don't need the box center for the pivot,
3534  // to avoid thread contention downstream when anything requests it.
3535  UT_BoundingBox bbox;
3536  packed_geo->getBoundsCached(bbox);
3537  if (centroid_pivot)
3538  {
3539  if (bbox.isValid())
3540  pivot = bbox.center();
3541  }
3542 
3543  // Set the implementations of all of the primitives at once.
3544  auto &&functor = [output_geo,packed_geo_nc,
3545  &pivot,lod,impl_changed](const UT_BlockedRange<GA_Offset> &r)
3546  {
3547  for (GA_Offset primoff = r.begin(), end = r.end(); primoff < end; ++primoff)
3548  {
3549  GA_Primitive *prim = output_geo->getPrimitive(primoff);
3550  GU_PrimPacked *packed_prim = UTverify_cast<GU_PrimPacked *>(prim);
3551  if (impl_changed)
3552  {
3553  // It's okay to set the implementation if it hasn't changed,
3554  // since setImplementation checks if the pointer is equal.
3555  // It doesn't incur the previous impl's decref in that case.
3556  packed_prim->setImplementation(packed_geo_nc, false);
3557  }
3558 
3559  // NOTE: Applying pivot to position is handled below.
3560  packed_prim->setPivot(pivot);
3561 
3562  packed_prim->setViewportLOD(lod);
3563  }
3564  };
3565 
3566  const UT_BlockedRange<GA_Offset> prim_range(start_primoff, start_primoff+num_packed_prims);
3567  constexpr exint PARALLEL_THRESHOLD = 2048;
3568  if (num_packed_prims >= PARALLEL_THRESHOLD)
3569  UTparallelForLightItems(prim_range, functor);
3570  else
3571  functor(prim_range);
3572  }
3573  else if ((num_packed_prims > 0) && centroid_pivot)
3574  {
3575  GA_Primitive *prim = output_geo->getPrimitive(start_primoff);
3576  GU_PrimPacked *packed_prim = UTverify_cast<GU_PrimPacked *>(prim);
3577  const GU_PackedImpl *packed_impl = packed_prim->sharedImplementation();
3578  UT_BoundingBox bbox;
3579  packed_impl->getBoundsCached(bbox);
3580  if (bbox.isValid())
3581  pivot = bbox.center();
3582  }
3583 
3584  const bool pivots_changed =
3585  pivot_type != cache->myPrevPivotEnum ||
3586  (centroid_pivot && source_changed);
3587 
3588  if (num_packed_prims > 0)
3589  {
3590  if (target != nullptr)
3591  {
3592  UT_ASSERT(target_point_list != nullptr && num_packed_prims == target_point_list->size());
3594  output_geo,
3595  cache,
3596  topology_changed,
3597  had_transform_matrices,
3598  target,
3599  *target_point_list,
3600  *target_attrib_info,
3601  *target_group_info,
3602  &pivot);
3603  }
3604  else
3605  {
3606  // No target, so only update the primitive transforms.
3608  output_geo,
3609  cache,
3610  had_transform_matrices,
3611  num_packed_prims,
3612  &pivot);
3613  }
3614  }
3615 
3616  if (topology_changed)
3617  {
3618  output_geo->bumpDataIdsForAddOrRemove(true, true, true);
3619  }
3620  if (source_changed && num_packed_prims > 0)
3621  {
3622  output_geo->getPrimitiveList().bumpDataId();
3623  }
3624  if (transforms_changed || pivots_changed)
3625  {
3626  output_geo->getP()->bumpDataId();
3627  // If there are no transform matrices, the primitives weren't transformed.
3628  // If source_changed, we already bumped the primitive list data ID.
3629  bool has_transform_matrices = (cache->myTransformMatrices3D.get() != nullptr);
3630  if ((has_transform_matrices || had_transform_matrices) && !source_changed)
3631  output_geo->getPrimitiveList().bumpDataId();
3632  }
3633  if (lod_changed)
3634  {
3635  output_geo->getPrimitiveList().bumpDataId();
3636  }
3637 
3638  cache->myPrevPack = true;
3639  cache->myPrevTargetPtCount = num_packed_prims;
3640  cache->myPrevSourceGroupDataID = source->getUniqueId();
3641  cache->myPrevSourceMetaCacheCount = source->getMetaCacheCount();
3642  cache->myPrevPivotEnum = pivot_type;
3643  cache->myPrevViewportLOD = lod;
3644  cache->myPrevOutputDetailID = output_geo->getUniqueId();
3645  cache->myPrevSourceUniqueID = source->getUniqueId();
3646  cache->myPrevSourceMetaCacheCount = source->getMetaCacheCount();
3647 }
3648 
3649 } // namespace GU_Copy
3650 
3651 } // End of HDK_Sample namespace
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, const GA_OffsetListRef *target_point_list, GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info, GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info)
Definition: GU_Copy2.C:3401
SYS_FORCE_INLINE void clear()
clear removes all of the entries
SYS_FORCE_INLINE const GU_PackedImpl * sharedImplementation() const
A class to manage an ordered array which has fixed offset handles.
Definition: GA_IndexMap.h:63
constexpr SYS_FORCE_INLINE T length2() const noexcept
Definition: UT_Vector3.h:356
void GUcreatePointOrPrimList(GA_OffsetList &offset_list, const GU_Detail *const detail, const GA_ElementGroup *const group, const GA_AttributeOwner owner)
Definition: GU_Copy2.C:1897
T & last()
Definition: UT_Array.h:816
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, const GU_CopyToPointsCache::TargetAttribInfoMap *const target_attrib_info, const GU_CopyToPointsCache::TargetAttribInfoMap *const target_group_info, const exint *const target_to_piecei, const UT_Array< exint > *const owner_piece_offset_starts, const GU_CopyToPointsCache::PieceData *const piece_data)
Definition: GU_Copy2.C:2012
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
SYS_FORCE_INLINE void forEachAttribute(FUNCTOR &&functor) const
static SYS_FORCE_INLINE bool isType(const GA_Attribute *attrib)
UT_UniquePtr< UT_Matrix3F[]> myTransformMatrices3F
Definition: GU_Copy2.h:74
Definition of a geometry attribute.
Definition: GA_Attribute.h:198
UT_Matrix4T< fpreal64 > UT_Matrix4D
void UTparallelFor(const Range &range, const Body &body, const int subscribe_ratio=2, const int min_grain_size=1, const bool force_use_task_scope=true)
Data has no numeric representation.
Definition: GA_Types.h:104
UT_UniquePtr< UT_Vector3D[]> myTransformTranslates3D
Definition: GU_Copy2.h:95
void GUupdatePackedPrimTransforms(GU_Detail *output_geo, GU_CopyToPointsCache *cache, const bool had_transform_matrices, const exint num_packed_prims, const UT_Vector3 *const constant_pivot)
Definition: GU_Copy2.C:3203
void clear()
Definition: UT_ArraySet.h:368
UT_ArrayStringMap< GA_DataId > mySourceGroupDataIDs[3]
Definition: GU_Copy2.h:155
SYS_FORCE_INLINE GA_Primitive * getPrimitive(GA_Offset prim_off)
Definition: GA_Detail.h:429
GLenum GLint * range
Definition: glcorearb.h:1925
SYS_FORCE_INLINE const GA_AttributeDict & pointAttribs() const
Definition: GEO_Detail.h:1939
UT_UniquePtr< UT_Vector3F[]> myTransformTranslates3F
Definition: GU_Copy2.h:76
FromType append(ToType value)
Add a single entry (may grow array)
GA_Range getVertexRange(const GA_VertexGroup *group=0) const
Get a range of all vertices in the detail.
Definition: GA_Detail.h:1768
const GA_IndexMap & getPrimitiveMap() const
Definition: GA_Detail.h:746
GA_DataId getDataId() const
Iteration over a range of elements.
Definition: GA_Iterator.h:29
FromType find(ToType value, FromType s=FromType(0)) const
void scale(T sx, T sy, T sz)
Definition: UT_Matrix3.h:854
void
Definition: png.h:1083
#define SYS_STATIC_ASSERT_MSG(expr, msg)
OIIO_UTIL_API bool copy(string_view from, string_view to, std::string &err)
const DataType & getData() const
int64 GA_DataId
Definition: GA_Types.h:696
SYS_FORCE_INLINE const GA_IndexMap & getIndexMap() const
Definition: GA_Attribute.h:207
SYS_FORCE_INLINE void colVecMult(const UT_Matrix3F &m)
Definition: UT_Matrix3.h:1557
virtual void copySubclassData(const GA_Primitive *source)
Definition: GA_Primitive.h:508
GLuint start
Definition: glcorearb.h:475
SYS_FORCE_INLINE bool atEnd() const
GA_Size entries() const overridefinal
Will return the number of primary elements.
void homogenize()
Express the point in homogeneous coordinates or vice-versa.
Definition: UT_Vector4.h:539
void clearAndDestroy()
Clear all the points/primitives out of this detail.
Definition: GEO_Detail.h:267
int getTupleSize() const
GA_ElementGroup * findElementGroup(GA_AttributeOwner owner, const UT_StringRef &name)
UT_UniquePtr< UT_Matrix3D[]> myTransformInverse3D
Definition: GU_Copy2.h:79
void getDataIds(GA_DataId data_ids[theNumAttribs]) const
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:1085
void GUcreateVertexListAndGeometryFromSource(GU_Detail *output_geo, const GU_Detail *const source, const exint source_point_count, const exint source_vertex_count, const exint source_prim_count, const GA_OffsetList &source_point_list_cache, GA_OffsetList &source_vertex_list_cache, const GA_OffsetList &source_prim_list_cache, const GA_PointGroup *const source_pointgroup, const GA_PrimitiveGroup *const source_primgroup, const exint ncopies)
Definition: GU_Copy2.C:1921
SYS_FORCE_INLINE bool getExtraFlag() const
Synonym for isClosed()
GA_DataId getDataId() const
Return the data ID for the topology attributes.
int64 exint
Definition: SYS_Types.h:125
exint entries() const
Definition: GA_GroupTable.h:67
SYS_FORCE_INLINE DEST_DATA_T get(GA_Offseti, exint component=0) const
Definition: UT_PageArray.h:488
void GUcomputeTransformTypeCaches(GU_PointTransformCache *cache, exint num_target_points, bool transforms_changed, const bool needed_transforms[NeededTransforms::num_needed_transforms])
Definition: GU_Copy2.C:738
GA_Attribute * getP()
Convenience method to access the P attribute.
Definition: GA_Detail.h:164
SYS_FORCE_INLINE GA_AttributeScope getScope() const
Definition: GA_Attribute.h:212
void setTrivialRange(FromType startindex, ToType startvalue, GA_Size nelements)
void GUcreateGeometryFromSource(GU_Detail *output_geo, const GU_Detail *const source, const GA_OffsetList &source_point_list_cache, const GA_OffsetList &source_vertex_list_cache, const GA_OffsetList &source_prim_list_cache, const exint ncopies)
NOTE: This does not clear output_geo.
Definition: GU_Copy2.C:1689
void clearOrdered()
Clear all order information, including any mixed entries.
void GEO_API GEOcomputeNormals(const GEO_Detail &detail, const GA_RWHandleV3 &normalattrib, const GA_Group *group=NULL, const float cuspangledegrees=GEO_DEFAULT_ADJUSTED_CUSP_ANGLE, const GEO_NormalMethod method=GEO_NormalMethod::ANGLE_WEIGHTED, const bool copy_orig_if_zero=false)
bool hasTransformingPrimitives() const
void UTparallelForLightItems(const Range &range, const Body &body, const bool force_use_task_scope=true)
GA_EdgeGroupTable & edgeGroups()
Definition: GA_Detail.h:1191
SYS_FORCE_INLINE TO_T UTverify_cast(FROM_T from)
Definition: UT_Assert.h:229
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, const UT_Array< exint > *const owner_piece_offset_starts, const GU_CopyToPointsCache::PieceData *const piece_data)
Definition: GU_Copy2.C:2699
Standard user attribute level.
Definition: GA_Types.h:149
SYS_FORCE_INLINE UT_FixedVector< DEST_DATA_T, DEST_TSIZE > getVector(GA_Offseti) const
Definition: UT_PageArray.h:498
GLdouble GLdouble GLdouble q
Definition: glad.h:2445
#define GA_INVALID_DATAID
Definition: GA_Types.h:697
virtual bool matchesStorage(const GA_Attribute *that) const
Definition: GA_Attribute.h:751
constexpr UT_Vector4T< SYS_FixedArrayElement_t< TS > > UTmakeVector4T(const TS &as) noexcept
Definition: UT_Vector4.h:1036
bool getBoundsCached(UT_BoundingBox &box) const
GA_DataId myTargetTransformDataIDs[GA_AttributeInstanceMatrix::theNumAttribs]
This is for keeping track of whether transforms have changed since the last cook. ...
Definition: GU_Copy2.h:86
SYS_FORCE_INLINE void set(GA_Offseti, SRC_DATA_T v)
component == 0 in this version
Definition: UT_PageArray.h:624
UT_UniquePtr< UT_QuaternionF[]> myTransformQuaternionsF
Definition: GU_Copy2.h:80
float fpreal32
Definition: SYS_Types.h:200
SYS_FORCE_INLINE bool GAisValid(GA_Size v)
Definition: GA_Types.h:655
exint size() const
Definition: UT_Array.h:646
GA_OffsetList mySourceOffsetLists[3]
Definition: GU_Copy2.h:174
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:236
UT_Matrix4T< float > UT_Matrix4
SYS_FORCE_INLINE iterator begin(GA_AttributeScope scope=GA_SCOPE_INVALID) const
UT_UniquePtr< UT_Matrix3D[]> myTransformMatrices3D
Definition: GU_Copy2.h:75
#define GA_INVALID_OFFSET
Definition: GA_Types.h:687
iterator find(const Key &key)
Definition: UT_ArrayMap.h:158
GA_Size countPrimitiveType(const GA_PrimitiveTypeId &type) const
Definition: GA_Detail.h:2299
bool hasAnyAttribs() const
Returns true if there are any attributes bound.
GA_GroupTable::iterator< GA_EdgeGroup > beginTraverse() const
Geometry Embedded procedural.
A range of elements in an index-map.
Definition: GA_Range.h:42
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
SYS_FORCE_INLINE GA_ElementGroup * find(const UT_StringRef &name) const
SYS_FORCE_INLINE const UT_StringHolder & getName() const
Definition: GA_Attribute.h:283
void setViewportLOD(GEO_ViewportLOD vlod)
double fpreal64
Definition: SYS_Types.h:201
#define UT_ASSERT_MSG(ZZ,...)
Definition: UT_Assert.h:159
UT_UniquePtr< UT_Matrix3D[]> myTransformMatrices3D
Definition: GU_Copy2.h:93
void allocateAndSet(GU_Detail *gdp, bool own=true)
GA_Size GA_Offset
Definition: GA_Types.h:646
tbb::task_group_status wait()
Definition: UT_TaskGroup.h:131
void updateFromArbitraryMatrix(const UT_Matrix3 &)
GA_AttributeScope
Definition: GA_Types.h:143
SYS_FORCE_INLINE bool isTrivial() const
const GA_ElementGroupTable & getElementGroupTable(GA_AttributeOwner owner) const
GLdouble n
Definition: glcorearb.h:2008
const GA_EdgeGroup * findEdgeGroup(const UT_StringRef &name) const
UT_UniquePtr< UT_Vector3F[]> myTransformTranslates3F
Definition: GU_Copy2.h:94
const GA_ROHandleV3D & getN() const
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1738
const GA_IndexMap & getPointMap() const
Definition: GA_Detail.h:744
GA_Attribute * cloneAttribute(GA_AttributeOwner owner, const UT_StringHolder &name, namevalidcertificate, const GA_Attribute &src, bool clone_options, GA_DataIdStrategy data_id_strategy=GA_DATA_ID_BUMP, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
UT_QuaternionT< fpreal32 > UT_QuaternionF
void identity()
Set the matrix to identity.
Definition: UT_Matrix3.h:1128
constexpr UT_Matrix3T< SYS_FixedArrayElement_t< TS > > UTmakeMatrix3T(const TS &as) noexcept
Definition: UT_Matrix3.h:1717
UT_Vector3T< T > center() const
SYS_FORCE_INLINE void setElement(GA_Offset ai, bool v)
virtual void replace(const GA_Attribute &src)=0
SYS_FORCE_INLINE GA_Offset appendPointBlock(GA_Size npoints)
Append new points, returning the first offset of the contiguous block.
Definition: GA_Detail.h:330
SYS_FORCE_INLINE bool destroyElementGroup(GA_AttributeOwner owner, const UT_StringRef &name)
Definition: GA_Detail.h:1238
void bumpDataId()
Use this to mark primitives or their intrinsic data as dirty.
UT_UniquePtr< UT_QuaternionD[]> myTransformQuaternionsD
Definition: GU_Copy2.h:81
GEO_ViewportLOD myPrevViewportLOD
Definition: GU_Copy2.h:146
void GUcreateEmptyPackedGeometryPrims(GU_Detail *const output_geo, const exint num_packed_prims)
Definition: GU_Copy2.C:1978
void UTparallelForRunInTaskGroup(UT_TaskGroup &task_group, RANGE &&range, BODY &&body)
Definition: UT_TaskGroup.h:139
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:155
SYS_FORCE_INLINE const GA_PointGroup * findPointGroup(const UT_StringRef &name) const
Definition: GA_Detail.h:1250
static SYS_FORCE_INLINE GA_ATINumeric * cast(GA_Attribute *attrib)
Definition: GA_ATINumeric.h:65
SYS_FORCE_INLINE GA_OffsetListRef getPrimitiveVertexList(GA_Offset primoff) const
Definition: GA_Primitive.h:891
void getMatrix(UT_Matrix4 &xform, const UT_Vector3 &P, GA_Offset offset, float default_pscale=1) const
UT_UniquePtr< UT_Vector3D[]> myTransformTranslates3D
Definition: GU_Copy2.h:77
GLuint GLuint end
Definition: glcorearb.h:475
GEO_API 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=1)
void setLocalTransform(const UT_Matrix3D &matrix) override
iterator begin()
Returns a non-const iterator for the beginning of the set.
Definition: UT_ArraySet.h:660
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
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)
Definition: GU_Copy2.C:304
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
GA_AttributeSet & getAttributes()
Definition: GA_Detail.h:799
constexpr UT_Matrix4T< SYS_FixedArrayElement_t< TS > > UTmakeMatrix4T(const TS &as) noexcept
Definition: UT_Matrix4.h:2237
const GA_IndexMap & getIndexMap(GA_AttributeOwner owner) const
SYS_FORCE_INLINE void rowVecMult(const UT_Matrix3F &m)
Definition: UT_Matrix3.h:1545
SYS_FORCE_INLINE GA_ATINumericUPtr createDetachedTupleAttribute(GA_AttributeOwner owner, GA_Storage storage, int tuple_size, const GA_Defaults &defaults=GA_Defaults(0.0f), const GA_AttributeOptions *attribute_options=nullptr) const
Definition: GA_Detail.h:895
UT_ArrayStringMap< GA_DataId > mySourceAttribDataIDs[3]
Definition: GU_Copy2.h:154
SYS_FORCE_INLINE GA_Offset getNumPointOffsets() const
Definition: GA_Detail.h:341
GLenum target
Definition: glcorearb.h:1667
static SYS_FORCE_INLINE GA_ATIString * cast(GA_Attribute *attrib)
Definition: GA_ATIString.h:57
virtual void setPivot(const UT_Vector3 &pos)
UT_Matrix3T< fpreal32 > UT_Matrix3F
const GA_IndexMap & getVertexMap() const
Definition: GA_Detail.h:745
T p1() const
Definition: GA_Edge.h:36
bool isInternal() const
Definition: GA_Group.h:45
GA_GroupTable::iterator< GA_ElementGroup > beginTraverse() const
SYS_FORCE_INLINE GA_Offset vertexPoint(GA_Offset vertex) const
Given a vertex, return the point it references.
Definition: GA_Detail.h:529
void identity()
Set the matrix to identity.
Definition: UT_Matrix4.h:1128
bool isOrdered() const overridefinal
Returns true if the group is currently ordered.
GA_Group * newGroup(const UT_StringHolder &name)
GLuint const GLchar * name
Definition: glcorearb.h:786
GA_OffsetList mySourceOffsetLists[3]
Definition: GU_Copy2.h:149
SYS_FORCE_INLINE GA_DataId getDataId() const
Definition: GA_Attribute.h:299
SYS_FORCE_INLINE const GA_Attribute * findAttribute(GA_AttributeScope scope, const UT_StringRef &name, const GA_AttributeOwner search_order[], int search_size) const
Definition: GA_Detail.h:1010
const UT_StringHolder & getName() const
Definition: GA_Group.h:42
SYS_FORCE_INLINE bool destroyEdgeGroup(const UT_StringRef &name)
Definition: GA_Detail.h:1278
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:640
GA_API const UT_StringHolder transform
virtual void copyNonStorageMetadata(const GA_Attribute *that)
Definition: GA_Attribute.h:765
void getPrimitiveTypeCounts(UT_Array< std::pair< int, exint >> &type_count_pairs, const GA_Range *range=nullptr) const
exint append()
Definition: UT_Array.h:142
GA_TypeInfo
Definition: GA_Types.h:101
UT_UniquePtr< GA_Attribute > GA_AttributeUPtr
Definition: GA_Attribute.h:930
exint getUniqueId() const
Definition: GA_Detail.h:117
void setTranslates(const UT_Vector3T< S > &translates)
Definition: UT_Matrix4.h:1442
GA_Topology & getTopology()
Definition: GA_Detail.h:801
virtual void getPivot(UT_Vector3 &pos) const
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)
Definition: GU_Copy2.C:3331
SYS_FORCE_INLINE bool isValid() const
Definition: GA_Handle.h:187
UT_Vector3T< fpreal32 > UT_Vector3F
Define a range based on a specific offset list.
bool isSame(const GA_ListTypeRef &that) const
SYS_FORCE_INLINE GA_Index pointIndex(GA_Offset offset) const
Given a point's data offset, return its index.
Definition: GA_Detail.h:349
void moveRange(GA_Offsetsrcstart, GA_Offsetdeststart, GA_Offsetnelements)
virtual void transform(const UT_Matrix4 &)
SYS_FORCE_INLINE GA_Size getNumVertices() const
Return the number verticies in the entire detail.
Definition: GA_Detail.h:504
Data represents a quaternion. Token "quaternion".
Definition: GA_Types.h:120
GA_AttributeOwner
Definition: GA_Types.h:35
void GUaddAttributesFromSourceOrTarget(GU_Detail *output_geo, const GU_Detail *source, exint *num_source_attribs, bool has_transform_matrices, bool *needed_transforms, const GU_Detail *target, GU_CopyToPointsCache::TargetAttribInfoMap *target_attrib_info, GU_CopyToPointsCache::TargetAttribInfoMap *target_group_info, exint *num_target_attribs)
Definition: GU_Copy2.C:457
UT_UniquePtr< UT_Matrix3F[]> myTransformInverse3F
Definition: GU_Copy2.h:96
UT_UniquePtr< UT_QuaternionD[]> myTransformQuaternionsD
Definition: GU_Copy2.h:99
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)
Definition: GU_Copy2.C:137
void intrusive_ptr_add_ref(T *x)
Definition: refcnt.h:208
void translate(T dx, T dy, T dz=0)
Definition: UT_Matrix4.h:773
GEO_ViewportLOD
SYS_FORCE_INLINE GA_TypeInfo getTypeInfo() const
Definition: GA_Attribute.h:252
constexpr UT_Vector3T< SYS_FixedArrayElement_t< TS > > UTmakeVector3T(const TS &as) noexcept
Definition: UT_Vector3.h:1313
SYS_FORCE_INLINE bool isValid() const
Check whether the bounding box contains at least one point.
SYS_FORCE_INLINE GA_Index indexSize() const
Definition: GA_IndexMap.h:103
SYS_FORCE_INLINE GA_Offset primitiveOffset(GA_Index index) const
Given a primitive's index (in append order), return its data offset.
Definition: GA_Detail.h:419
virtual bool copy(GA_Offset desti, GA_Offset srci)
Definition: GA_Attribute.h:800
Data represents a normal vector. Token "normal".
Definition: GA_Types.h:114
void append(GA_Size size, GA_Size count=1)
SYS_FORCE_INLINE const GA_ATITopology * getVertexRef() const
Definition: GA_Topology.h:225
constexpr SYS_FORCE_INLINE bool isZero() const noexcept
Definition: UT_Vector3.h:393
SYS_FORCE_INLINE GA_Size getNumPrimitives() const
Return the number of primitives.
Definition: GA_Detail.h:408
constexpr UT_QuaternionT< SYS_FixedArrayElement_t< TS > > UTmakeQuaternionT(const TS &as) noexcept
iterator erase(iterator pos)
Compute an instance transform given a set of attributes.
Data represents a direction vector. Token "vector".
Definition: GA_Types.h:112
int64 getMetaCacheCount() const
Definition: GA_Detail.h:2378
UT_ArrayStringMap< GA_DataId > mySourceEdgeGroupDataIDs
Definition: GU_Copy2.h:156
void swap(set_type &that)
Swaps another set with this one.
Definition: UT_ArraySet.h:332
GA_API const UT_StringHolder pivot
const GA_PrimitiveList & getPrimitiveList() const
Definition: GA_Detail.h:795
void setDetailPtr(const GU_DetailHandle &d)
bool add(const GA_Edge &edge, GA_Offset primoff=GA_INVALID_OFFSET)
Data represents a position in space. Token "point".
Definition: GA_Types.h:106
SYS_FORCE_INLINE GA_AttributeOwner getOwner() const
Definition: GA_Attribute.h:210
void setN(const GA_Attribute *N)
Overrides the N attribute with the specified attribute.
UT_UniquePtr< UT_Matrix3F[]> myTransformInverse3F
Definition: GU_Copy2.h:78
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
SYS_FORCE_INLINE const GA_PageArray< DEST_DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED > & castType() const
Definition: GA_PageArray.h:732
T p0() const
Definition: GA_Edge.h:34
SYS_FORCE_INLINE void set(GA_Offset off, const HOLDER &str)
Store the str at the given offset.
Definition: GA_Handle.h:1107
GLboolean r
Definition: glcorearb.h:1222
void dehomogenize()
Express the point in homogeneous coordinates or vice-versa.
Definition: UT_Vector4.h:545
const GA_AttributeDict & getAttributeDict(GA_AttributeOwner owner) const
Definition: GA_Detail.h:835
TargetAttribInfoMap myTargetGroupInfo
Definition: GU_Copy2.h:184
void clear()
Resets list to an empty list.
Definition: UT_Array.h:729
SYS_FORCE_INLINE void setImplementation(const GU_PackedImpl *impl, bool add_ref=true, bool remove_ref=true)
SYS_FORCE_INLINE const GA_Attribute * findPointAttribute(GA_AttributeScope s, const UT_StringRef &name) const
Definition: GA_Detail.h:1037
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1741
#define SYSmin(a, b)
Definition: SYS_Math.h:1571
bool OIIO_UTIL_API contains(string_view a, string_view b)
Does 'a' contain the string 'b' within it?
exint myPrevSourceUniqueID
These are only used when myPrevPack is true.
Definition: GU_Copy2.h:143
void destroyAttribute(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringRef &name, const GA_AttributeFilter *filter=0)
void bumpDataIdsForAddOrRemove(bool added_or_removed_points, bool added_or_removed_vertices, bool added_or_removed_primitives)
SYS_FORCE_INLINE GA_Offset pointOffset(GA_Index index) const
Given a point's index (in append order), return its data offset.
Definition: GA_Detail.h:345
Declare prior to use.
SYS_FORCE_INLINE const GA_ATITopology * getPointRef() const
Definition: GA_Topology.h:221
Data represents a transform matrix. Token "matrix".
Definition: GA_Types.h:118
UT_UniquePtr< UT_QuaternionF[]> myTransformQuaternionsF
Definition: GU_Copy2.h:98
T * getArray() const
Definition: UT_Array.h:836
GLint lod
Definition: glcorearb.h:2765
void initialize(const GA_AttributeDict &dict, const UT_StringRef &N_name=GA_Names::N, const UT_StringRef &v_name=GA_Names::v)
void invalidateGroupEntries()
UT_UniquePtr< UT_Matrix3D[]> myTransformInverse3D
Definition: GU_Copy2.h:97
GA_OffsetList getOffsetFromIndexList() const
Definition: GA_IndexMap.h:244
bool fullBlockAdvance(GA_Offset &start, GA_Offset &end)
UT_UniquePtr< UT_Matrix3F[]> myTransformMatrices3F
Definition: GU_Copy2.h:92
TargetAttribInfoMap myTargetAttribInfo
Definition: GU_Copy2.h:183
SYS_FORCE_INLINE FromType size() const
Returns the number of used elements in the list (always <= capacity())
void bumpDataId()
void getTranslates(UT_Vector3T< S > &translates) const
Definition: UT_Matrix4.h:1432
static GA_PrimitiveTypeId typeId()
Get the type ID for the GU_PackedGeometry primitive type.
SYS_FORCE_INLINE bool contains(GA_Offset offset) const
UT_Matrix3T< fpreal64 > UT_Matrix3D
SYS_FORCE_INLINE GA_Size getNumPoints() const
Return the number of points.
Definition: GA_Detail.h:334
GA_Storage getStorage() const
GA_Offset appendPrimitivesAndVertices(const GA_PrimitiveTypeId &type, GA_Size nprimitives, GA_Size nvertices_each, GA_Offset &vertex_block_start, bool closed_flag=false)