HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GU_GroomUtils.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: GU Library (C++)
7  *
8  */
9 
10 #ifndef __GU_GroomUtils_h__
11 #define __GU_GroomUtils_h__
12 
13 #include "GU_API.h"
14 #include "GU_Detail.h"
15 #include "GU_RayIntersect.h"
16 #include "GU_PolyFrame.h"
17 #include "GU_SurfaceDeform.h"
18 
19 #include <GA/GA_Attribute.h>
20 #include <GA/GA_AttributeRefMap.h>
21 #include <GA/GA_Handle.h>
22 #include <GA/GA_SplittableRange.h>
23 #include <GA/GA_Types.h>
24 #include <GEO/GEO_Interpolate.h>
25 #include <GEO/GEO_PointTree.h>
26 #include <GEO/GEO_PrimPoly.h>
27 #include <UT/UT_ErrorLog.h>
28 #include <UT/UT_LineTree.h>
29 #include <UT/UT_Set.h>
30 #include <UT/UT_Tuple.h>
31 #include <UT/UT_Tracing.h>
32 
33 #include <functional>
34 #include <numeric>
35 #include <type_traits>
36 
38 {
39 public:
40  static GA_Attribute*
41  findOrCreateFloatAttribute(
42  GU_Detail &gdp,
43  const GA_AttributeOwner owner,
44  const UT_StringRef &name,
45  const exint tsize,
46  const GA_Defaults &defaults = GA_Defaults(0.0),
48 
49  static GA_Attribute*
50  findOrCreateIntAttribute(
51  GU_Detail &gdp,
52  const GA_AttributeOwner owner,
53  const UT_StringRef &name,
54  const exint tsize,
55  const GA_Defaults &defaults = GA_Defaults(0),
57 
58  static GA_Attribute*
59  findOrCreateStringAttribute(
60  GU_Detail &gdp,
61  const GA_AttributeOwner owner,
62  const UT_StringRef &name);
63 
64  static GA_Attribute*
66  GU_Detail &gdp,
67  GA_AttributeOwner owner,
68  const UT_StringRef &name,
69  int tsize)
70  {
71  GA_Attribute *attrib = gdp.findStringArray(owner, name);
72 
73  if (!attrib)
74  attrib = gdp.addStringArray(owner, name, tsize);
75 
76  return attrib;
77  }
78 
79  static GA_Attribute*
81  GU_Detail &gdp,
82  GA_AttributeOwner owner,
83  const UT_StringRef &name,
84  int tsize,
86  {
87  GA_Attribute *attrib = gdp.findIntArray(owner, name);
88 
89  if (!attrib)
90  attrib = gdp.addIntArray(owner, name, tsize);
91 
92  return attrib;
93  }
94 
95  static GA_Attribute*
97  GU_Detail &gdp,
98  GA_AttributeOwner owner,
99  const UT_StringRef &name,
100  int tsize,
102  {
103  GA_Attribute *attrib = gdp.findFloatArray(owner, name);
104 
105  if (!attrib)
106  attrib = gdp.addFloatArray(owner, name, tsize);
107 
108  return attrib;
109  }
110 
111  static GA_Attribute*
112  findNamedOrient(GU_Detail &gdp, const UT_StringRef &name);
113 
114  static const GA_Attribute*
115  findNamedOrient(const GU_Detail &gdp, const UT_StringRef &name);
116 
117  static GA_Attribute*
118  findOrCreateNamedOrient(GU_Detail &gdp, const UT_StringRef &name);
119 
120  static void getPrimPointPositions(
121  UT_Vector3Array &positions,
122  GA_Offset primoff,
123  const GU_Detail &gdp,
124  const GA_ROHandleV3 &pos_handle);
125 
126 
127  static void setPrimPointPositions(
128  UT_Vector3Array &positions,
129  GA_Offset primoff,
130  const GU_Detail &gdp,
131  const GA_RWHandleV3 &pos_handle);
132 
133  static fpreal calcPerimeter(UT_Vector3Array &pos);
134 
135  static float unitLengthToUnitDomain(
136  const UT_Vector3Array &pos,
137  float perimeter,
138  const UT_FloatArray &edgelengths,
139  float ulength);
140 
141  static float unitToUnitLengthDomain(
142  float perimeter,
143  const UT_FloatArray &edgelengths,
144  float uparm);
145 
146  static float unitLengthToUnitDomain(
148  const UT_FloatArray &edgelengths,
149  float ulength);
150 
151  static float
152  openCurveUnitToReal(int n, float u_unit)
153  {
154  if (n <= 0 || !u_unit) return 0.0f;
155 
156  if (u_unit < 0.0f) u_unit = 0.0f;
157  else if (u_unit > 1.0f) u_unit = 1.0f;
158 
159  return u_unit * float(n-1);
160  }
161 
164  {
165  if (n == 0)
166  {
167  return { {0.0f, 0.0f}, {-1, -1} };
168  }
169 
170  if (n == 1)
171  {
172  return { {1.0f, 0.0f}, {0, -1} };
173  }
174 
175  u = openCurveUnitToReal(n, u); // Also checks out of bounds etc.
176 
177  int vcrt, vseq;
178  float rem = GEO_Face::getIndices(u, vcrt, vseq, n-1, false /*ifclosed*/);
179 
180  if (rem == 0 || vcrt == vseq) {
181  return { {1.0f, 0.0f}, {vcrt, -1} };
182  }
183 
184  return {
185  {1.0f-rem, rem},
186  {vcrt, vseq}
187  };
188  }
189 
190  template<typename T>
191  static T
192  interpAttrib(
193  const GA_ROHandleT<T> &attrib,
194  const GA_Offset &primoff,
195  const GA_OffsetArray &vertoffsets,
196  const UT_FloatArray &vertweights,
197  const T defaultvalue=T(0.0f));
198 
199  static fpreal
200  computeLength(
201  const UT_Vector3Array &positions);
202 
203  static fpreal
204  computeEdgeLengths(
205  const UT_Vector3Array &positions,
206  UT_FloatArray &edgelengths);
207 
208  static fpreal
209  computeEdgeLengths(
210  const GU_Detail &gdp,
211  const GA_ROHandleV3 &P,
212  GA_Offset primoff,
213  UT_FloatArray &edgelengths);
214 
215  static void
218  const GU_Detail &gdp)
219  {
220  for (int i=0; i<offsets.size(); ++i)
221  offsets[i] = gdp.vertexPoint(offsets[i]);
222  }
223 
224  // stores all quats of influencing guides for each point and attribute
225  // in a linear array. Guides are the smallest dimension, so all the guides
226  // for a point and attribute are in a continuous block of memory, ready
227  // to be used with UT_Quaternion::interpolate().
229  {
230  public:
232  void setDim(int pointcount, int attribcount, int guidecount)
233  {
234  myPointCount = pointcount;
235  myAttribCount = attribcount;
236  myGuideCount = guidecount;
237  myQuats.setSize(attribcount * pointcount * guidecount);
238  };
239 
241  int getLinearIndex(int pointindex, int attribindex, int guideindex)
242  {
243  int index = pointindex * (myAttribCount * myGuideCount)
244  + attribindex * myGuideCount
245  + guideindex;
246 
247  return index;
248  }
249 
251  const UT_QuaternionF&
252  getValue(int pointindex, int attribindex, int guideindex)
253  {
254  int index = getLinearIndex(pointindex, attribindex, guideindex);
255 
256  return myQuats[index];
257  }
258 
260  void
261  setValue(int pointindex, int attribindex, int guideindex, UT_QuaternionF &quat)
262  {
263  int index = getLinearIndex(pointindex, attribindex, guideindex);
264 
265  myQuats[index] = quat;
266  }
267 
269  const UT_QuaternionFArray&
271  {
272  return myQuats;
273  }
274 
275  private:
276  int myPointCount = 0;
277  int myAttribCount = 0;
278  int myGuideCount = 0;
279 
280  UT_QuaternionFArray myQuats;
281  };
282 
283  // positions must have size == vtxcount and initialized to 0.
284  static void
285  addWeightedGuide(
286  const GU_Detail &guidegdp,
287  const GEO_PrimPoly * const guideprim,
288  const GA_Offset guideprimoff,
289  const int guideindex,
290  const float guideweight,
291  const GA_ROHandleF &guidetightnessattrib,
292  const GA_ROHandleV3 &guidetangentattrib,
293  const GA_ROHandleV3 &guidenormalattrib,
294  const GA_ROHandleV3 &guidePattrib,
295  const GA_AttributeRefMap &guidepointlinattribmap,
296  const GA_AttributeRefMap &guidepointquatattribmap,
297 
298  // settings
299  const bool extrude,
300  const bool setpos,
301  const bool uniformguidesegments,
302  const GA_Size vtxcount,
303  const UT_Vector3 rootpos,
304  const GA_OffsetListRef &vtxlist,
305  // temp storage
306  UT_FloatArray &glengths,
307  GA_OffsetArray &guidevertoffsets,
308  UT_FloatArray &guidevertweights,
309  // output
310  UT_Array<UT_Vector3> &positions,
311  UT_ValArray<GA_WeightedSum> &pointsums,
312  QuatInterpTempStorage &quattemp,
313  const UT_Matrix3 *guidetoposrot = nullptr);
314 
315  static void
316  removeNonGuidesFromGroup(
317  GA_PrimitiveGroup &guidegroup,
318  GU_Detail &gdp);
319 
320  static void
321  removeHiddenPrimitivesFromGroup(
322  GA_PrimitiveGroup &guidegroup,
323  GU_Detail &gdp);
324 
325  static void
326  setGuideGroupMembers(
327  GA_PrimitiveGroup &guidegroup,
328  const GA_ElementGroup *ingroup,
329  const GA_ElementGroup *inmirrorgroup,
330  const GA_PrimitiveGroup *templategroup,
331  GU_Detail &gdp);
332 
333  enum class SetLengthMode
334  {
335  SET = 0,
336  MULTIPLY,
337  ADD,
338  SUBTRACT,
339  SETMIN,
340  SETMAX
341  };
342 
343  static fpreal
345  fpreal prevlength,
347  fpreal value)
348  {
349  if (mode == SetLengthMode::SETMIN)
350  {
351  return SYSmin(prevlength, value);
352  }
353  else if (mode == SetLengthMode::SETMAX)
354  {
355  return SYSmax(prevlength, value);
356  }
357  else if (mode == SetLengthMode::ADD)
358  {
359  return (prevlength+value);
360  }
361  else if (mode == SetLengthMode::SUBTRACT)
362  {
363  return (prevlength-value);
364  }
365  else if (mode == SetLengthMode::SET)
366  {
367  return value;
368  }
369 
370  return value;
371  }
372 
373  static void
374  scaleCurve(
375  GU_Detail &gdp,
376  const GA_RWHandleV3 &Pattrib,
377  GA_Offset primoff,
378  GA_OffsetListRef &curvevertoffs,
379  float factor);
380 
381  static void
382  setCurveLength(
383  GU_Detail &gdp,
384  const GA_RWHandleV3 &Pattrib,
385  GA_Offset primoff,
386  fpreal prevlength,
387  fpreal newlength,
388  GA_OffsetListRef &curvevertoffs,
389  UT_FloatArray &tempu,
390  UT_Vector3Array &temppos);
391 
392  static void
393  setCurveLength(
394  UT_Vector3Array &positions,
395  fpreal prevlength,
396  fpreal newlength,
397  UT_FloatArray &tempu,
398  UT_Vector3Array &temppos);
399 
400  static float
401  circlePackRootPoints(
402  const GU_Detail &gdp,
403  const GA_ROHandleV3 &restPattrib,
405  UT_FloatArray &sqdists,
406  const GA_Offset * const offsets,
407  exint numcurves,
408  const UT_Vector3 &clumproot,
409  const UT_Matrix3 &clumprot,
410  const GA_RWHandleV2 &bundleposattrib);
411 
412  // copy of vex intrinsic in $SHS/vex/intrinsics/opmath.vfl
413  static UT_Vector3
414  slideFrame(
415  const UT_Vector3 x0,
416  const UT_Vector3 t0,
417  const UT_Vector3 n0,
418  const UT_Vector3 x1,
419  const UT_Vector3 t1);
420 
421  static UT_Matrix3
422  computeTangentFrame(
423  const UT_Vector3 &tangent,
424  const UT_Vector3 &normal);
425 
426  static void
427  computeTangentFrames(
428  UT_ValArray<UT_Matrix3> &frames,
429  const UT_Vector3Array &positions,
430  const UT_Vector3 &roottangent,
431  const UT_Vector3 &rootnormal);
432 
434  static GA_Offset
436  {
437  return gdp.getPrimitiveVertexOffset(primoff, index);
438  }
439 
441  static GA_Offset
443  {
444  GA_Offset firstvtxoff(primVertexOffset(gdp, primoff, index));
445  return gdp.vertexPoint(firstvtxoff);
446  }
447 
448  static void setPagesConstant(GA_RWHandleF attribhandle, float value)
449  {
450  GA_Offset size = attribhandle->getIndexMap().offsetSize();
451 
452  UT_PageNum numpages(attribhandle->getData().numPages(size));
453 
454  for(GA_PageNum pagenum(0); pagenum < numpages; ++pagenum)
455  attribhandle.setPageConstant(pagenum, &value);
456  }
457 
458  static bool
460  const GA_ROHandleI &skinprimattrib,
461  const GU_Detail &skingdp,
462  const GA_ElementGroup *group = nullptr)
463  {
464  UT_ASSERT(!group || skinprimattrib->getOwner() == group->getOwner());
465 
466  auto &constgdp = skinprimattrib.getAttribute()->getDetail();
467  bool skinprimfail = false;
468  GA_Range range = skinprimattrib->getOwner() == GA_ATTRIB_POINT
469  ? constgdp.getPointRange(dynamic_cast<const GA_PointGroup*>(group))
470  : constgdp.getPrimitiveRange(dynamic_cast<const GA_PrimitiveGroup*>(group));
472  [&](const GA_SplittableRange &r)
473  {
474  GA_Offset startoff;
475  GA_Offset endoff;
476 
477  for (GA_Iterator ptit(r); ptit.blockAdvance(startoff, endoff); )
478  {
479  if (skinprimfail)
480  return;
481 
482  for (GA_Offset off = startoff; off < endoff; ++off)
483  {
484  GA_Index skinprimindex = skinprimattrib.get(off);
485  if (skinprimindex < 0 ||
486  skinprimindex >= skingdp.getNumPrimitives())
487  {
488  skinprimfail = true;
489  return;
490  }
491  }
492  }
493  });
494 
495  return !skinprimfail;
496  }
497 
500  GU_Detail &gdp,
501  GA_AttributeOwner owner,
502  const UT_StringHolder &name_attrib_name,
503  const UT_StringHolder &weight_attrib_name)
504  {
505  GA_Attribute* keyattrib = findOrCreateStringArrayAttribute(
506  gdp, owner, name_attrib_name, 1);
507 
508  GA_Attribute* weightattrib = findOrCreateFloatArrayAttribute(
509  gdp, owner, weight_attrib_name, 1);
510 
511  return UTmakeTuple(keyattrib, weightattrib);
512  }
513 
516  GU_Detail &gdp,
517  GA_AttributeOwner owner,
518  const UT_StringHolder &name_attrib_name,
519  const UT_StringHolder &weight_attrib_name)
520  {
521  GA_Attribute* keyattrib = findOrCreateIntArrayAttribute(
522  gdp, owner, name_attrib_name, 1);
523 
524  GA_Attribute* weightattrib = findOrCreateFloatArrayAttribute(
525  gdp, owner, weight_attrib_name, 1);
526 
527  return UTmakeTuple(keyattrib, weightattrib);
528  }
529 
530 };
531 
533 {
534 public:
536  const GU_Detail &gdp,
537  const GA_Attribute *restattrib);
538 
539  void findAttribs(const GU_Detail &gdp);
540 
542  {
543  return myAttribMissing;
544  }
545 
547  {
548  return myAttribMissingWarning;
549  }
550 
555 
556 private:
557  bool myAttribMissing = false;
558 
559  static const UT_StringHolder myAttribMissingWarning;
560 };
561 
563 
565 {
566 public:
569 };
570 
572 {
573 public:
575  const GU_RayIntersect &rayintersect)
576  : myRayIntersect(rayintersect)
577  , mySkinGdp(*rayintersect.detail())
578  , mySkinP(mySkinGdp.getP())
579  {}
580 
582  const GU_RayIntersect &rayintersect,
583  const GA_Attribute &normal)
584  : myRayIntersect(rayintersect)
585  , mySkinGdp(*rayintersect.detail())
586  , mySkinP(mySkinGdp.getP())
587  , mySkinN(&normal)
588  {}
589 
591  const GU_Detail&
593  {
594  return mySkinGdp;
595  }
596 
598  {
599  return myRayIntersect;
600  }
601 
603  bool
605  const UT_Vector3 &pos,
606  GU_MinInfo &mininfo) const
607  {
608  int hitcount = myRayIntersect.minimumPoint(pos, mininfo);
609 
610  // If nothing hit within radius (unlikely), fall back to infinite radius
611  if (hitcount == 0)
612  {
613  mininfo = GU_MinInfo(FLT_MAX, 0, true);
614  hitcount = myRayIntersect.minimumPoint(pos, mininfo);
615  }
616 
617  return hitcount > 0;
618  }
619 
622  {
623  int hits = myRayIntersect.sendRay(pos, dir, info);
624 
625  return hits > 0;
626  }
627 
629  static void
631  const GU_MinInfo &mininfo,
632  GU_GroomInterpCoords &interpcoords)
633  {
635  interpcoords.offsets, interpcoords.weights,
636  mininfo.u1,
637  mininfo.v1,
638  0.0f);
639  }
640 
642  static void
644  const GU_RayInfo &hitinfo,
645  GU_GroomInterpCoords &interpcoords)
646  {
648  interpcoords.offsets, interpcoords.weights,
649  hitinfo.myU,
650  hitinfo.myV,
651  0.0f);
652  }
653 
654  template<typename T>
655  T
657  const GA_ROHandleT<T> &attrib,
658  const GU_GroomInterpCoords &interpcoords) const
659  {
661  attrib, GA_INVALID_OFFSET,
662  interpcoords.offsets, interpcoords.weights);
663  }
664 
666  UT_Vector3
668  const GU_GroomInterpCoords &interpcoords) const
669  {
670  return interpAttrib(mySkinP, interpcoords);
671  }
672 
674  UT_Vector3
676  const GU_GroomInterpCoords &interpcoords) const
677  {
678  UT_ASSERT(mySkinN.isValid());
679  UT_Vector3 normal = interpAttrib(mySkinN, interpcoords);
680 
681  normal.normalize();
682 
683  return normal;
684  }
685 
687  bool
689  UT_Vector3 &pos,
690  GU_MinInfo &mininfo,
691  GU_GroomInterpCoords &interpcoords)
692  {
693  if (!closestPointInfo(pos, mininfo))
694  return false;
695 
696  computeVertWeights(mininfo, interpcoords);
697 
698  pos = interpAttrib(mySkinP, interpcoords);
699 
700  return true;
701  }
702 
703 private:
704  const GU_RayIntersect &myRayIntersect;
705  const GU_Detail &mySkinGdp;
706  const GA_ROHandleV3 mySkinP;
707  const GA_ROHandleV3 mySkinN;
708 };
709 
711 {
712 public:
714  {
715  void setArraySize(unsigned int size)
716  {
717  lines.setSize(size);
718  dist2s.setSize(size);
719  }
720 
723 
725  };
726 
727  GU_GroomPartingLineLookup(unsigned int maxpartlines)
728  : myMaxPartLines(maxpartlines)
729  {}
730 
732  unsigned int maxpartlines,
733  GU_GroomSkinIntersect *skinintersect)
734  : myMaxPartLines(maxpartlines)
735  {}
736 
737  static const GA_PrimitiveGroup*
738  findGroup(const GU_Detail &gdp)
739  {
740  return gdp.findPrimitiveGroup("partinglines");
741  }
742 
743  static GA_PrimitiveGroup&
745  {
746  GA_PrimitiveGroup *group = gdp.findPrimitiveGroup("partinglines");
747 
748  if (group)
749  return *group;
750 
751  return *gdp.newPrimitiveGroup("partinglines");
752  }
753 
754  static GA_PrimitiveGroup*
756  {
757  return gdp.findPrimitiveGroup("partinglines");
758  }
759 
760  void
761  setLines(
762  const GU_Detail &gdp,
763  const GA_ROHandleV3 &restattrib,
764  const GA_PrimitiveGroup &partlinesgroup);
765 
766  float
767  computePartingWeight(
768  const UT_Vector3 &posA,
769  const UT_Vector3 &posB,
770  const GU_GroomPartingLineAttribs &attribs,
771  PerThreadData &threadvalues,
772  const GU_GroomSkinIntersect *skinintersect = nullptr) const;
773 
774  GA_Offset getLineOffset1(int id) const
775  {
776  return myLineOffsets(2 * id);
777  }
778 
779  GA_Offset getLineOffset2(int id) const
780  {
781  return myLineOffsets(2 * id + 1);
782  }
783 
784  int64 getMemoryUsage(bool inclusive) const
785  {
786  int64 size(inclusive ? sizeof(*this) : 0);
787 
788  size += myLineOffsets.getMemoryUsage(true);
789 
790  return size;
791  }
792 
793 private:
794  int addLine(const GA_Offset ptoff1, const UT_Vector3 &pt1,
795  const GA_Offset ptoff2, const UT_Vector3 &pt2);
796 
797  const unsigned int myMaxPartLines;
798  UT_LineTree myLineTree;
799  GA_OffsetArray myLineOffsets;
800 };
801 
803 {
804 public:
806  GU_Detail &dstgdp,
807  const GU_Detail *srcgdp = nullptr)
808  : myDstGdp(dstgdp)
809  , mySrcGdp(srcgdp != nullptr ? *srcgdp : dstgdp)
810  , myAttribMap(myDstGdp, &mySrcGdp)
811  , myLengthScaledAttribMap(myDstGdp, &mySrcGdp)
812  {
813  }
814 
815  void
816  addAttrib(const UT_StringRef &name)
817  {
818  addAttribToMap(myAttribMap, name);
819  }
820 
821  void
823  {
824  addAttribToMap(myLengthScaledAttribMap, name);
825  }
826 
827  void
828  sampleAttribs(
829  const GEO_PrimPoly &dstpoly,
830  const GEO_PrimPoly &srcpoly,
831  float blend);
832 
833  void
835  const GEO_PrimPoly &dstprimpoly,
836  GA_Offset sourceprimoff,
837  float blend)
838  {
839  const GEO_Primitive *srcgeoprim = mySrcGdp.getGEOPrimitive(
840  sourceprimoff);
841  const GEO_PrimPoly *srcpoly(nullptr);
842 
843  // We only do polygons
844  if (srcgeoprim->getTypeId() == GEO_PRIMPOLY)
845  {
846  srcpoly = static_cast<const GEO_PrimPoly *>(srcgeoprim);
847 
848  sampleAttribs(dstprimpoly, *srcpoly, blend);
849  }
850  }
851 
852 private:
853  void
854  addAttribToMap(GA_AttributeRefMap &map, const char *attribname);
855 
856  GU_Detail &myDstGdp;
857  const GU_Detail &mySrcGdp;
858 
859  GA_AttributeRefMap myAttribMap;
860  GA_AttributeRefMap myLengthScaledAttribMap;
861 };
862 
863 // A tree that can be built using an arbitrary prim or point attribute as the
864 // point position. When using a prim attribute, this allows direct lookup of
865 // curve primitives.
867 {
868 public:
869  /// Rebuilds the tree with the given detail and primitive group,
870  /// This creates a point per primitive using the primitive attribute of the
871  /// specified name as the point position.
872  /// if primgroup is NULL then all primitives are used.
873  void
874  build(
875  const GEO_Detail *gdp,
876  const GA_PrimitiveGroup *primgroup,
877  const char *attrib,
878  bool enable_multithreading=true);
879 
880  // Methods that must delegate to base class due to overloads
881  void build(
882  const GEO_Detail *gdp,
883  const GA_PointGroup *ptgroup,
884  bool enable_multithreading=true)
885  {
886  GEO_PointTreeGAOffset::build(gdp, ptgroup, enable_multithreading);
887  }
888 
889  void build(
890  const GEO_Detail *gdp,
891  const GA_PointGroup *ptgroup,
892  const char *attrib,
893  bool enable_multithreading=true)
894  {
895  GEO_PointTreeGAOffset::build(gdp, ptgroup, attrib, enable_multithreading);
896  }
897 };
898 
900 {
901 public:
902  GU_GroomFastRamp(int numsamples=1000)
903  : myNumSamples(numsamples)
904  {}
905 
906  bool operator==(const GU_GroomFastRamp& ramp) const
907  {
908  return mySamples == ramp.mySamples
909  && myNumSamples == ramp.myNumSamples
910  && myIsConstant == ramp.myIsConstant;
911  }
912 
913  bool operator!=(const GU_GroomFastRamp& ramp) const
914  {
915  return !(*this == ramp);
916  }
917 
918  bool update(const UT_Ramp *widthramp);
919 
920  void update(const UT_FloatArray &values);
921 
923  bool isConstant() { return myIsConstant; }
924 
926  float lookup(float u) const
927  {
928  float us = u * (mySamples.size()-1);
929  int indexA = SYSclamp(exint(us), exint(0), mySamples.size()-1);
930  if (indexA == mySamples.size()-1)
931  return mySamples(indexA);
932  int indexB = indexA + 1;
933  return SYSlerp(mySamples(indexA), mySamples(indexB), us - indexA);
934  }
935 
937  float lookupNearest(float u) const
938  {
939  int index = SYSclamp(
940  exint(u * (mySamples.size()-1) + 0.5),
941  exint(0),
942  mySamples.size()-1);
943  return mySamples(index);
944  }
945 
946  const UT_FloatArray &samples() const;
947 
948 private:
949  UT_FloatArray mySamples;
950  int myNumSamples;
951  bool myIsConstant = false;
952 };
953 
955 {
956 public:
958  GU_Detail *dest,
959  const GU_Detail *source,
960  const GA_AttributeOwner destowner)
961  : dest(dest)
962  , source(source)
963  , destowner(destowner)
964  {}
965 
966  using AttribMatchTuple = std::pair<GA_AttributeOwner, UT_StringHolder>;
967 
968  void addSourceAttribs(
969  const GA_AttributeOwner sourceowner,
970  const char *matchpattern,
971  UT_Set<AttribMatchTuple> &alreadymapped,
972  const GA_AttributeFilter *filter=nullptr);
973 
975  {
978  STORAGE
979  };
980 
981  class CacheData
982  {
983  private:
984  UT_ValArray<int> targetindices;
987  UT_ValArray<int> typeids;
988  UT_ValArray<int> tuplesizes;
989  UT_ValArray<GA_DataId> dataids;
990 
992 
993  };
994 
996  GA_AttributeRefMap &map) const
997  {
998  mapAndCreateAttribs(map, nullptr);
999  }
1000 
1001 
1002  void mapAndCreateAttrib(
1003  GA_AttributeRefMap &map,
1004  const UT_ValArray<AttribChange> *changes,
1005  int index) const;
1006 
1007  // Creates or updates attributes as required by the changes array
1008  //
1009  // Attributes that need their data copied are added to map.
1011  GA_AttributeRefMap &map,
1012  const UT_ValArray<AttribChange> *changes) const
1013  {
1014  for (int i = 0; i < sourceattribs.size(); ++i)
1015  mapAndCreateAttrib(map, changes, i);
1016  }
1017 
1018  // Creates or updates attributes as required by the changes array
1019  //
1020  // Attributes that need their data copied are added to map.
1022  GA_AttributeRefMap &map,
1023  const UT_ValArray<AttribChange> *changes,
1024  const std::function<bool(const GA_Attribute &)>& filter ) const
1025  {
1026  for (int i = 0; i < sourceattribs.size(); ++i)
1027  if (filter(*sourceattribs[i]))
1028  mapAndCreateAttrib(map, changes, i);
1029  }
1030 
1031  void
1033  GA_AttributeRefMap &map,
1034  const UT_ValArray<AttribChange> *changes) const
1035  {
1036  static auto &&theFilter = [](const GA_Attribute &attrib)
1037  {
1038  return attrib.getStorageClass() == GA_STORECLASS_REAL;
1039  };
1040 
1041  mapAndCreateAttribs(map, changes, theFilter);
1042  }
1043 
1044  void
1046  GA_AttributeRefMap &map,
1047  const UT_ValArray<AttribChange> *changes) const
1048  {
1049  static auto &&theFilter = [](const GA_Attribute &attrib)
1050  {
1051  return attrib.getStorageClass() != GA_STORECLASS_REAL;
1052  };
1053 
1054  mapAndCreateAttribs(map, changes, theFilter);
1055  }
1056 
1057  bool updateCache(
1058  UT_UniquePtr<CacheData> &cachedata,
1059  UT_ValArray<AttribChange> &changes) const;
1060 
1061 private:
1062  CacheData* getCacheData() const;
1063 
1064  const GU_Detail *source;
1065  UT_ValArray<const GA_Attribute*> sourceattribs;
1066  GU_Detail *dest;
1067  const GA_AttributeOwner destowner;
1068 };
1069 
1071 {
1072 public:
1073  GU_ROWeightArrayPairHandle() = default;
1074 
1076  const GEO_Detail &detail,
1077  GA_AttributeOwner attrib_owner,
1078  const UT_StringHolder &key_attrib_name,
1079  const UT_StringHolder &weight_attrib_name)
1080  {
1081  bind(detail, attrib_owner, key_attrib_name, weight_attrib_name);
1082  }
1083 
1084  bool bind(
1085  const GEO_Detail &detail,
1086  GA_AttributeOwner attrib_owner,
1087  const UT_StringHolder &key_attrib_name,
1088  const UT_StringHolder &weight_attrib_name)
1089  {
1090  return bindImpl(SYSconst_cast(detail), attrib_owner, key_attrib_name, weight_attrib_name);
1091  }
1092 
1093  void bind(
1094  const GA_Attribute *key_attrib,
1095  const GA_Attribute *weight_attrib)
1096  {
1097  bindImpl(SYSconst_cast(key_attrib), SYSconst_cast(weight_attrib));
1098 
1099  setAIFArrays();
1100  }
1101 
1102  bool
1103  isValid() const
1104  {
1105  return myKeyAttrib && myWeightAttrib;
1106  }
1107 
1108  const GA_Attribute *
1109  keyAttrib() const
1110  {
1111  return myKeyAttrib;
1112  }
1113 
1114  const GA_Attribute *
1116  {
1117  return myWeightAttrib;
1118  }
1119 
1120  const GA_AIFNumericArray*
1121  indexArray() const
1122  {
1123  return myIndexArray;
1124  }
1125 
1127  nameArray() const
1128  {
1129  return myNameArray;
1130  }
1131 
1132  const GA_AIFNumericArray*
1133  weightArray() const
1134  {
1135  return myWeightArray;
1136  }
1137 
1139  {
1142  : nullptr;
1145  : nullptr;
1146 
1149  : nullptr;
1150  }
1151 
1152 protected:
1153  bool bindImpl(
1154  GEO_Detail &detail,
1155  GA_AttributeOwner attrib_owner,
1156  const UT_StringHolder &key_attrib_name,
1157  const UT_StringHolder &weight_attrib_name)
1158  {
1159  GA_Attribute *weight_attrib, *key_attrib;
1160 
1161  weight_attrib =
1162  SYSconst_cast(detail.findFloatArray(attrib_owner, weight_attrib_name));
1163  key_attrib =
1164  SYSconst_cast(detail.findStringArray(attrib_owner, key_attrib_name));
1165 
1166  if (!key_attrib)
1167  {
1168  key_attrib =
1169  SYSconst_cast(detail.findIntArray( attrib_owner, key_attrib_name));
1170  }
1171 
1172  if (!key_attrib || !weight_attrib)
1173  return false;
1174 
1175  myWeightAttrib = weight_attrib;
1176  myKeyAttrib = key_attrib;
1177  setAIFArrays();
1178 
1179  return true;
1180  }
1181 
1182  void bindImpl(
1183  GA_Attribute *key_attrib,
1184  GA_Attribute *weight_attrib)
1185  {
1186  myKeyAttrib = key_attrib;
1187  myWeightAttrib = weight_attrib;
1188 
1189  setAIFArrays();
1190  }
1191 
1195 
1198 };
1199 
1201 {
1202 public:
1203  GU_RWWeightArrayPairHandle() = default;
1204 
1206  GEO_Detail &detail,
1207  GA_AttributeOwner attrib_owner,
1208  const UT_StringHolder &key_attrib_name,
1209  const UT_StringHolder &weight_attrib_name)
1210  {
1211  bind(detail, attrib_owner, key_attrib_name, weight_attrib_name);
1212  }
1213 
1214  bool bind(
1215  GEO_Detail &detail,
1216  GA_AttributeOwner attrib_owner,
1217  const UT_StringHolder &key_attrib_name,
1218  const UT_StringHolder &weight_attrib_name)
1219  {
1220  return bindImpl(detail, attrib_owner, key_attrib_name, weight_attrib_name);
1221  }
1222 
1223  void bind(
1224  GA_Attribute *key_attrib,
1225  GA_Attribute *weight_attrib)
1226  {
1227  bindImpl(key_attrib, weight_attrib);
1228  }
1229 
1230  bool
1231  isValid() const
1232  {
1233  return myKeyAttrib && myWeightAttrib;
1234  }
1235 
1236  GA_Attribute *
1237  keyAttrib() const
1238  {
1239  return myKeyAttrib;
1240  }
1241 
1242  GA_Attribute *
1244  {
1245  return myWeightAttrib;
1246  }
1247 
1248  void set(
1249  GA_Offset primoff,
1250  const UT_StringArray &names,
1251  const UT_FloatArray &weights)
1252  {
1253  nameArray()->set(keyAttrib(), primoff, names);
1254  weightArray()->set(weightAttrib(), primoff, weights);
1255  }
1256 
1258  {
1259  if (myKeyAttrib != nullptr)
1261 
1262  if (myWeightAttrib != nullptr)
1264  }
1265 };
1266 
1268 {
1269 public:
1270  struct TempData
1271  {
1275 
1278  };
1279 
1281  const GEO_Detail &weightgeo)
1282  : myWeightGeo(weightgeo)
1283  {
1284  }
1285 
1287  const GEO_Detail &weightgeo,
1288  GA_AttributeOwner attrib_owner,
1289  const UT_StringRef &key_attrib_name,
1290  const UT_StringRef &weight_attrib_name)
1291  : myWeightGeo(weightgeo)
1292  {
1293  myInputAttribs.bind(
1294  myWeightGeo,
1295  attrib_owner,
1296  key_attrib_name,
1297  weight_attrib_name);
1298  }
1299 
1301  const GEO_Detail &weightgeo,
1302  const GA_Attribute &keyattrib,
1303  const GA_Attribute &weightattrib)
1304  : myWeightGeo(weightgeo)
1305  {
1306  myInputAttribs.bind(
1307  &keyattrib,
1308  &weightattrib);
1309  }
1310 
1312  GA_AttributeOwner attrib_owner,
1313  const UT_StringRef &key_attrib_name,
1314  const UT_StringRef &weight_attrib_name)
1315  {
1316  return myInputAttribs.bind(
1317  myWeightGeo,
1318  attrib_owner,
1319  key_attrib_name,
1320  weight_attrib_name);
1321  }
1322 
1323  bool
1324  isValid() const
1325  {
1326  return myInputAttribs.keyAttrib() && myInputAttribs.weightAttrib();
1327  }
1328 
1329  const GA_Attribute *
1330  keyAttrib() const
1331  {
1332  return myInputAttribs.keyAttrib();
1333  }
1334 
1335  const GA_Attribute *
1337  {
1338  return myInputAttribs.weightAttrib();
1339  }
1340 
1343  {
1344  return myInputAttribs;
1345  }
1346 
1347  void
1349  GU_Detail &gdp,
1350  GA_AttributeOwner owner)
1351  {
1352  bool key_is_string = false;
1353 
1354  if (!isValid())
1355  return;
1356 
1357  const UT_StringHolder key_name = keyAttrib()->getName();
1358  const UT_StringHolder weight_name = weightAttrib()->getName();
1359 
1360  key_is_string = keyAttrib()->getAIFSharedStringArray() != nullptr;
1361 
1362  GA_Attribute *keyattrib = nullptr;
1363  GA_Attribute *weightattrib = nullptr;
1364 
1365  if (key_is_string)
1366  {
1367  UTlhsTuple(keyattrib, weightattrib) =
1369  gdp, owner, key_name, weight_name);
1370  }
1371  else
1372  {
1373  UTlhsTuple(keyattrib, weightattrib) =
1375  gdp, owner, key_name, weight_name);
1376  }
1377 
1378  myOutputAttribs.bind(keyattrib, weightattrib);
1379  }
1380 
1382  const GA_OffsetArray &offsets,
1383  const UT_FloatArray &weights,
1384  GA_Offset &out_offset,
1385  TempData &tempdata) const
1386  {
1387  bool success = false;
1388  if (myInputAttribs.nameArray() != nullptr)
1389  {
1390  interpolate(
1391  tempdata.outnames,
1392  tempdata.outweights,
1393  offsets,
1394  weights,
1395  tempdata.tempnameset);
1396 
1397  myOutputAttribs.nameArray()->set(
1398  myOutputAttribs.keyAttrib(), out_offset, tempdata.outnames);
1399 
1400  success = true;
1401  }
1402  else if (myInputAttribs.indexArray())
1403  {
1404  interpolate(
1405  tempdata.outindices,
1406  tempdata.outweights,
1407  offsets,
1408  weights,
1409  tempdata.tempindexset);
1410 
1411  myOutputAttribs.indexArray()->set(
1412  myOutputAttribs.keyAttrib(), out_offset, tempdata.outindices);
1413 
1414  success = true;
1415  }
1416 
1417  if (success)
1418  {
1419  myOutputAttribs.weightArray()->set(
1420  myOutputAttribs.weightAttrib(), out_offset, tempdata.outweights);
1421  }
1422  }
1423 
1424  template<typename C>
1425  void
1427  C &out_keys,
1428  UT_Array<float> &out_weights,
1429  const GA_OffsetArray &offsets,
1430  const UT_FloatArray &weights,
1431  UT_Map<typename C::value_type, float> &blended_weights_set) const
1432  {
1433  UT_ASSERT(offsets.size() == weights.size());
1434 
1435  interpolate(
1436  out_keys,
1437  out_weights,
1438  offsets.data(),
1439  weights.data(),
1440  offsets.size(),
1441  blended_weights_set);
1442  }
1443 
1444  template<typename C>
1445  void
1447  C &out_keys,
1448  UT_Array<float> &out_weights,
1449  const GA_Offset *offsets,
1450  const float *weights,
1451  const int weightcount,
1452  UT_Map<typename C::value_type, float> &blended_weights_set) const
1453  {
1454  using T = typename C::value_type;
1455 
1456  blended_weights_set.clear();
1457 
1458  float pw_sum(0.0f);
1459 
1460  for (int i=0; i<weightcount; ++i)
1461  {
1462  GA_Offset offset = offsets[i];
1463  float pw = weights[i];
1464 
1465  if (pw < SYS_FTOLERANCE)
1466  continue;
1467 
1468  if constexpr(SYS_IsSame_v<C, UT_StringArray>)
1469  myInputAttribs.nameArray()->get(myInputAttribs.keyAttrib(), offset, out_keys);
1470  else
1471  myInputAttribs.indexArray()->get(myInputAttribs.keyAttrib(), offset, out_keys);
1472 
1473  myInputAttribs.weightArray()->get(myInputAttribs.weightAttrib(), offset, out_weights);
1474 
1475  int count = SYSmin(out_keys.size(), out_weights.size());
1476 
1477  for (int j=0; j<count; ++j)
1478  {
1479  T key = out_keys[j];
1480  float w = out_weights[j];
1481 
1482  blended_weights_set[key] =
1483  blended_weights_set.get(key, 0.0f) + pw * w;
1484  }
1485 
1486  pw_sum += pw;
1487  }
1488 
1489  float inv_pw_sum = SYSsaferecip(pw_sum);
1490 
1491  out_keys.setSize(0);
1492  out_weights.setSize(0);
1493  for (auto &wpair : blended_weights_set)
1494  {
1495  out_keys.append(wpair.first);
1496  out_weights.append(inv_pw_sum * wpair.second);
1497  }
1498  }
1499 
1500  void
1502  const GU_Detail &geo,
1503  const GA_Attribute &prim_attrib,
1504  const GA_Attribute &primuv_attrib,
1505  GA_Attribute &key_attrib,
1506  GA_Attribute &weight_attrib) const
1507  {
1508  const GA_AIFNumericArray *index_array = key_attrib.getAIFNumericArray();
1509  const GA_AIFNumericArray *weight_array = weight_attrib.getAIFNumericArray();
1510 
1511  const GA_Size num_weight_prims = myWeightGeo.getNumPrimitives();
1512 
1513  GA_ROHandleI prim_handle(&prim_attrib);
1514  GA_ROHandleV3 primuv_handle(&primuv_attrib);
1515 
1516  GA_Range elemrange;
1517 
1518  switch (prim_attrib.getOwner())
1519  {
1520  default: UT_ASSERT(0); SYS_FALLTHROUGH;
1521  case GA_ATTRIB_VERTEX: elemrange = geo.getVertexRange(); break;
1522  case GA_ATTRIB_POINT: elemrange = geo.getPointRange(); break;
1523  case GA_ATTRIB_PRIMITIVE: elemrange = geo.getPrimitiveRange(); break;
1524  case GA_ATTRIB_DETAIL: elemrange = geo.getGlobalRange(); break;
1525  }
1526 
1527  UTparallelFor(GA_SplittableRange(elemrange),
1528  [&](const GA_SplittableRange &r)
1529  {
1530  GA_Offset startoff;
1531  GA_Offset endoff;
1532 
1533  GA_OffsetArray pt_offsets;
1534  UT_FloatArray pt_weights;
1535 
1537  UT_ValArray<float> weights;
1538 
1539  UT_Map<GA_Size, float> temp_set;
1540 
1541  for (GA_Iterator ptit(r); ptit.blockAdvance(startoff, endoff); )
1542  {
1543  for (GA_Offset elemoff = startoff; elemoff < endoff; ++elemoff)
1544  {
1545  GA_Index skinprimindex = prim_handle.get(elemoff);
1546 
1547  if (skinprimindex < 0 || skinprimindex >= num_weight_prims)
1548  continue;
1549 
1550  GA_Offset primoff = myWeightGeo.primitiveOffset(skinprimindex);
1551  UT_Vector3 primuv = primuv_handle.get(elemoff);
1552 
1553  auto skinprim = myWeightGeo.getGEOPrimitive(primoff);
1554  skinprim->computeInteriorPointWeights(
1555  pt_offsets, pt_weights,
1556  primuv.x(),
1557  primuv.y(),
1558  primuv.z());
1559 
1560  if (myInputAttribs.weightAttrib()->getOwner() != GA_ATTRIB_VERTEX)
1561  for (int i=0; i<pt_offsets.size(); ++i)
1562  pt_offsets[i] = myWeightGeo.vertexPoint(pt_offsets[i]);
1563 
1564  interpolate(
1565  indices, weights,
1566  pt_offsets, pt_weights,
1567  temp_set);
1568 
1569  index_array->set(&key_attrib, elemoff, indices);
1570  weight_array->set(&weight_attrib, elemoff, weights);
1571  }
1572  }
1573  });
1574  }
1575 
1576 private:
1577  const GEO_Detail &myWeightGeo;
1578 
1579  GU_ROWeightArrayPairHandle myInputAttribs;
1580  GU_RWWeightArrayPairHandle myOutputAttribs;
1581 };
1582 
1583 struct
1585 {
1586  enum class SkinGuideMode
1587  {
1588  MATCHBYGUIDEID = 0,
1589  WEIGHTARRAYS
1590  };
1591 
1592  struct CacheData
1593  {
1594  private:
1595  GA_DataId myTopoId = -2;
1596  GA_DataId myPrimlistId = -2;
1597  GA_DataId myGuidesArrayId = -2;
1598  GA_DataId myWeightsArrayId = -2;
1599 
1600  friend GU_SkinInterpolate;
1601  };
1602 
1604  const GU_Detail &guidegdp,
1605  const GU_Detail &interpgdp,
1606  const GA_ROHandleV3 &rootlookuppos,
1607  SkinGuideMode guidemode,
1608  const GA_ROHandleI &skinguidelookupattrib,
1609  const UT_StringHolder &guidesattrib,
1610  const UT_StringHolder &weightsattrib)
1611  : myGuideGdp(guidegdp)
1612  , myInterpGdp(interpgdp)
1613  , myWeightArrayInterp(
1614  guidemode == SkinGuideMode::WEIGHTARRAYS
1616  myInterpGdp, GA_ATTRIB_POINT,
1617  guidesattrib, weightsattrib)
1618  : nullptr)
1619  , myRootLookupPos(rootlookuppos)
1620  , myRayIntersect(UTmakeUnique<GU_RayIntersect>(&interpgdp))
1621  , myGuideMode(guidemode)
1622  , mySkinGuideLookupAttrib(skinguidelookupattrib)
1623  {
1624  }
1625 
1626  bool isValid()
1627  {
1628  if (guideMode() == SkinGuideMode::WEIGHTARRAYS)
1629  {
1630  return myWeightArrayInterp->isValid();
1631  }
1632 
1633  return true;
1634  }
1635 
1637  const GA_PrimitiveGroup *guidegroup)
1638  {
1639  if (myGuideMode != SkinGuideMode::WEIGHTARRAYS)
1640  return true;
1641 
1643 
1644  for (GA_Offset ptoff : myInterpGdp.getPointRange())
1645  {
1646  auto handle = myWeightArrayInterp->getWeightArrayPairHandle();
1647  handle.indexArray()->get(handle.keyAttrib(), ptoff, indices);
1648 
1649  for (GA_Size index : indices)
1650  {
1651  if (index < 0 || index >= myGuideGdp.getNumPrimitives())
1652  return false;
1653 
1654  if (guidegroup != nullptr && !guidegroup->containsIndex(index))
1655  {
1656  return false;
1657  }
1658  }
1659  }
1660 
1661  return true;
1662  }
1663 
1664  bool updateCache(CacheData &cache)
1665  {
1666  GA_DataId topoid = interpMesh().getTopology().getDataId();
1667  GA_DataId primlistid = interpMesh().getPrimitiveList().getDataId();
1668 
1669  GA_DataId guidesarrayid = GA_INVALID_DATAID;
1670  GA_DataId weightsarrayid = GA_INVALID_DATAID;
1671 
1672  if (guideMode() == SkinGuideMode::WEIGHTARRAYS
1673  && myWeightArrayInterp->isValid())
1674  {
1675  guidesarrayid = myWeightArrayInterp->keyAttrib()->getDataId();
1676  weightsarrayid = myWeightArrayInterp->weightAttrib()->getDataId();
1677  }
1678 
1679  if (topoid != cache.myTopoId ||
1680  primlistid != cache.myPrimlistId ||
1681  guidesarrayid != cache.myGuidesArrayId ||
1682  weightsarrayid != cache.myWeightsArrayId)
1683  {
1684  cache.myTopoId = topoid;
1685  cache.myPrimlistId = primlistid;
1686  cache.myGuidesArrayId = guidesarrayid;
1687  cache.myWeightsArrayId = weightsarrayid;
1688  return true;
1689  }
1690 
1691  return false;
1692  }
1693 
1695  {
1696  return myGuideMode;
1697  }
1698 
1699  const GU_Detail &interpMesh() const
1700  {
1701  return myInterpGdp;
1702  }
1703 
1705  GA_Offset sourceptoff,
1706  GU_MinInfo &mininfo,
1708  UT_FloatArray &weights) const
1709  {
1710  myRayIntersect->minimumPoint(
1711  myRootLookupPos.get(sourceptoff), mininfo);
1712 
1713 
1714  GA_Offset hitprim_offset = mininfo.prim.offset();
1715  auto skinprim = myInterpGdp.getGEOPrimitive(hitprim_offset);
1716  skinprim->computeInteriorPointWeights(
1717  offsets, weights,
1718  mininfo.u1, mininfo.v1,
1719  0.0f);
1720 
1721  GU_GroomUtils::vertexToPointOffsets(offsets, myInterpGdp);
1722  }
1723 
1724  // Populates guideoffsets and guideweights array using the method
1725  // specified by the guidemode argument to the constructor.
1727  GA_OffsetArray &guideoffsets,
1728  UT_FloatArray &guideweights,
1729  const GA_OffsetArray &skinoffsets,
1730  const UT_FloatArray &skinweights,
1731  UT_ValArray<GA_Size> &tempindices,
1732  UT_Map<GA_Size, float> &tempset) const
1733  {
1734  if (guideMode() == SkinGuideMode::WEIGHTARRAYS)
1735  {
1736  interpolateWeightArrays(
1737  guideoffsets,
1738  guideweights,
1739  skinoffsets,
1740  skinweights,
1741  tempindices,
1742  tempset);
1743  }
1744  else
1745  {
1746  interpolateGuides(
1747  guideoffsets,
1748  guideweights,
1749  skinoffsets,
1750  skinweights);
1751  }
1752  }
1753 
1754 private:
1755  void interpolateWeightArrays(
1756  GA_OffsetArray &guideoffsets,
1757  UT_FloatArray &guideweights,
1758  const GA_OffsetArray &skinoffsets,
1759  const UT_FloatArray &skinweights,
1760  UT_ValArray<GA_Size> &tempindices,
1761  UT_Map<GA_Size, float> &tempset) const
1762  {
1763  myWeightArrayInterp->interpolate(
1764  tempindices,
1765  guideweights,
1766  skinoffsets,
1767  skinweights,
1768  tempset);
1769 
1770  guideoffsets.setCapacity(tempindices.size());
1771  guideoffsets.setSize(0);
1772  for (GA_Size index : tempindices)
1773  {
1774  guideoffsets.append(
1775  myGuideGdp.primitiveOffset(index));
1776  }
1777  }
1778 
1779  void interpolateGuides(
1780  GA_OffsetArray &guideoffsets,
1781  UT_FloatArray &guideweights,
1782  const GA_OffsetArray &skinoffsets,
1783  const UT_FloatArray &skinweights) const
1784  {
1785  guideoffsets.setSize(skinoffsets.size());
1786  guideweights.setSize(skinoffsets.size());
1787 
1788  for (GA_Size j = 0; j < skinoffsets.size(); ++j)
1789  {
1790  GA_Offset skinptoff = skinoffsets(j);
1791  GA_Index guideindex;
1792 
1793  if (mySkinGuideLookupAttrib.isValid())
1794  {
1795  guideindex = mySkinGuideLookupAttrib.get(skinptoff);
1796 
1797  if (guideindex == GA_INVALID_INDEX)
1798  {
1799  guideoffsets.clear();
1800  guideweights.clear();
1801  break;
1802  }
1803  }
1804  else
1805  {
1806  guideindex = interpMesh().pointIndex(skinptoff);
1807  }
1808 
1809  GA_Offset guideprimoff = myGuideGdp.primitiveOffset(guideindex);
1810 
1811  guideoffsets(j) = guideprimoff;
1812  guideweights(j) = skinweights(j);
1813  }
1814  }
1815 
1816  const GU_Detail &myGuideGdp;
1817  const GU_Detail &myInterpGdp;
1818  UT_UniquePtr<GU_WeightArrayInterpolate> myWeightArrayInterp;
1819  const GA_ROHandleV3 &myRootLookupPos;
1820  UT_UniquePtr<GU_RayIntersect> myRayIntersect;
1821  const SkinGuideMode myGuideMode;
1822  const GA_ROHandleI &mySkinGuideLookupAttrib;
1823 };
1824 
1825 #endif
GU_GroomCurveAttribSampler(GU_Detail &dstgdp, const GU_Detail *srcgdp=nullptr)
#define SYSmax(a, b)
Definition: SYS_Math.h:1952
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:315
SYS_FORCE_INLINE const GA_Detail & getDetail() const
Definition: GA_Attribute.h:213
void interpolate(const GA_OffsetArray &offsets, const UT_FloatArray &weights, GA_Offset &out_offset, TempData &tempdata) const
SYS_FORCE_INLINE const GA_PrimitiveGroup * findPrimitiveGroup(const UT_StringRef &name) const
Definition: GA_Detail.h:1276
Definition of a geometry attribute.
Definition: GA_Attribute.h:203
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)
SYS_FORCE_INLINE GA_Offset getPrimitiveVertexOffset(GA_Offset primoff, GA_Size i) const
Definition: GA_Primitive.h:915
GU_GroomSourceAttribList(GU_Detail *dest, const GU_Detail *source, const GA_AttributeOwner destowner)
bool set(GA_Attribute *attrib, GA_Offset o, const UT_StringArray &v) const
Definition: UT_Set.h:58
void interpolate(C &out_keys, UT_Array< float > &out_weights, const GA_Offset *offsets, const float *weights, const int weightcount, UT_Map< typename C::value_type, float > &blended_weights_set) const
UT_Map< GA_Size, float > tempindexset
GU_WeightArrayInterpolate(const GEO_Detail &weightgeo, GA_AttributeOwner attrib_owner, const UT_StringRef &key_attrib_name, const UT_StringRef &weight_attrib_name)
GLsizei GLenum const void * indices
Definition: glcorearb.h:406
Generic Attribute Interface class to access an attribute as a array.
GLenum GLint * range
Definition: glcorearb.h:1925
GA_Range getVertexRange(const GA_VertexGroup *group=0) const
Get a range of all vertices in the detail.
Definition: GA_Detail.h:1788
const GA_AIFNumericArray * weightArray() const
GA_Attribute * keyAttrib() const
Iteration over a range of elements.
Definition: GA_Iterator.h:29
bool bindInputAttribs(GA_AttributeOwner attrib_owner, const UT_StringRef &key_attrib_name, const UT_StringRef &weight_attrib_name)
static GA_Attribute * findOrCreateIntArrayAttribute(GU_Detail &gdp, GA_AttributeOwner owner, const UT_StringRef &name, int tsize, GA_Storage storage=GA_STORE_INT32)
Definition: GU_GroomUtils.h:80
Class which stores the default values for a GA_Attribute.
Definition: GA_Defaults.h:32
GU_GroomSkinIntersect(const GU_RayIntersect &rayintersect, const GA_Attribute &normal)
void bindImpl(GA_Attribute *key_attrib, GA_Attribute *weight_attrib)
virtual void computeInteriorPointWeights(UT_Array< GA_Offset > &vtxlist, UT_Array< float > &weightlist, fpreal u, fpreal v, fpreal w) const
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
const DataType & getData() const
int64 GA_DataId
Definition: GA_Types.h:703
static GA_PrimitiveGroup * findGroup(GU_Detail &gdp)
GA_Offset getLineOffset1(int id) const
SYS_FORCE_INLINE const GA_IndexMap & getIndexMap() const
Definition: GA_Attribute.h:212
bool blockAdvance(GA_Offset &start, GA_Offset &end)
GLsizei const GLfloat * value
Definition: glcorearb.h:824
SYS_FORCE_INLINE bool closestPos(UT_Vector3 &pos, GU_MinInfo &mininfo, GU_GroomInterpCoords &interpcoords)
void mapAndCreateNonRealAttribs(GA_AttributeRefMap &map, const UT_ValArray< AttribChange > *changes) const
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
Definition: SYS_Types.h:136
SYS_FORCE_INLINE UT_Vector3 interpP(const GU_GroomInterpCoords &interpcoords) const
SYS_FORCE_INLINE bool isConstant()
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector3.h:669
int64 exint
Definition: SYS_Types.h:125
SkinGuideMode guideMode() const
GU_GroomPartingLineLookup(unsigned int maxpartlines, GU_GroomSkinIntersect *skinintersect)
SYS_FORCE_INLINE void setValue(int pointindex, int attribindex, int guideindex, UT_QuaternionF &quat)
GU_WeightArrayInterpolate(const GEO_Detail &weightgeo, const GA_Attribute &keyattrib, const GA_Attribute &weightattrib)
void build(const GEO_Detail *gdp, const GA_PointGroup *ptgroup=NULL, bool enable_multithreading=true)
std::pair< GA_AttributeOwner, UT_StringHolder > AttribMatchTuple
SYS_FORCE_INLINE void setDim(int pointcount, int attribcount, int guidecount)
SYS_FORCE_INLINE const GA_PrimitiveTypeId & getTypeId() const
Definition: GA_Primitive.h:177
exint UT_PageNum
Definition: UT_PageArray.h:34
void setPageConstant(GA_PageNum pagenum, const BASETYPE *values) const
Definition: GA_Handle.h:497
GEO_ConstPrimitiveP myPrim
static UT_Tuple< GA_Attribute *, GA_Attribute * > createNameAndWeightArrayAttributes(GU_Detail &gdp, GA_AttributeOwner owner, const UT_StringHolder &name_attrib_name, const UT_StringHolder &weight_attrib_name)
GEO_ConstPrimitiveP prim
void setCapacity(exint new_capacity)
static GA_PrimitiveGroup & findOrCreateGroup(GU_Detail &gdp)
const GU_RayIntersect & rayIntersect()
void createOutputAttribs(GU_Detail &gdp, GA_AttributeOwner owner)
#define UTmakeTuple
Definition: UT_Tuple.h:59
GA_OffsetArray offsets
const GA_Attribute * weightAttrib() const
GA_Attribute * addStringArray(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size=1, const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, GA_Storage storage=GA_STORE_STRING, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
GA_Offset getLineOffset2(int id) const
SYS_FORCE_INLINE const UT_QuaternionFArray & getData()
std::tuple< Types...> UT_Tuple
Definition: UT_Tuple.h:53
void addAttrib(const UT_StringRef &name)
void mapAndCreateAttribs(GA_AttributeRefMap &map) const
bool bind(const GEO_Detail &detail, GA_AttributeOwner attrib_owner, const UT_StringHolder &key_attrib_name, const UT_StringHolder &weight_attrib_name)
UT_Matrix2T< T > SYSlerp(const UT_Matrix2T< T > &v1, const UT_Matrix2T< T > &v2, S t)
Definition: UT_Matrix2.h:675
#define GA_INVALID_DATAID
Definition: GA_Types.h:704
void clear()
Definition: UT_Map.h:191
const GA_Attribute * weightAttrib() const
UT_UniquePtr< GU_GroomPartingLineAttribs > gu_PartingLineAttribsUPtr
V get(const key_type &key, const V &defval) const
Definition: UT_Map.h:176
GU_RWWeightArrayPairHandle()=default
bool containsIndex(GA_Index ai) const
static SYS_FORCE_INLINE void computeVertWeights(const GU_MinInfo &mininfo, GU_GroomInterpCoords &interpcoords)
exint size() const
Definition: UT_Array.h:667
uint64 value_type
Definition: GA_PrimCompat.h:29
void setSize(exint newsize)
Definition: UT_Array.h:690
exint GA_Size
Defines the bit width for index and offset types in GA.
Definition: GA_Types.h:243
#define GA_INVALID_OFFSET
Definition: GA_Types.h:694
A range of elements in an index-map.
Definition: GA_Range.h:42
bool bindImpl(GEO_Detail &detail, GA_AttributeOwner attrib_owner, const UT_StringHolder &key_attrib_name, const UT_StringHolder &weight_attrib_name)
GA_PrimitiveGroup * newPrimitiveGroup(const UT_StringHolder &name)
Definition: GA_Detail.h:1319
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
void mapAndCreateAttribs(GA_AttributeRefMap &map, const UT_ValArray< AttribChange > *changes) const
GLuint GLsizei const GLuint const GLintptr * offsets
Definition: glcorearb.h:2621
SYS_FORCE_INLINE const UT_StringHolder & getName() const
Definition: GA_Attribute.h:292
const GA_AIFNumericArray * myIndexArray
const GA_Attribute * findIntArray(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringRef &name, int min_size=1, int max_size=-1) const
SYS_FORCE_INLINE GEO_Primitive * getGEOPrimitive(GA_Offset primoff)
Definition: GEO_Detail.h:1197
GA_Attribute * addFloatArray(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size=1, const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, GA_Storage storage=GA_STORE_REAL32, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
const GA_ATINumeric * getAttribute() const
Definition: GA_Handle.h:168
GA_Size GA_Offset
Definition: GA_Types.h:653
void mapAndCreateAttribs(GA_AttributeRefMap &map, const UT_ValArray< AttribChange > *changes, const std::function< bool(const GA_Attribute &)> &filter) const
static bool checkSkinPrimIndices(const GA_ROHandleI &skinprimattrib, const GU_Detail &skingdp, const GA_ElementGroup *group=nullptr)
vint4 blend(const vint4 &a, const vint4 &b, const vbool4 &mask)
Definition: simd.h:4949
static SYS_FORCE_INLINE GA_Offset primVertexOffset(const GU_Detail &gdp, GA_Offset primoff, GA_Size index=0)
static SYS_FORCE_INLINE void computeVertWeights(const GU_RayInfo &hitinfo, GU_GroomInterpCoords &interpcoords)
GA_Attribute * addIntArray(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size=1, const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, GA_Storage storage=GA_STORE_INT32, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
GU_ROWeightArrayPairHandle(const GEO_Detail &detail, GA_AttributeOwner attrib_owner, const UT_StringHolder &key_attrib_name, const UT_StringHolder &weight_attrib_name)
static void vertexToPointOffsets(GA_OffsetArray &offsets, const GU_Detail &gdp)
GLdouble n
Definition: glcorearb.h:2008
GLfloat f
Definition: glcorearb.h:1926
const GA_Attribute * findStringArray(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringRef &name, int min_size=1, int max_size=-1) const
GA_Range getPointRange(const GA_PointGroup *group=0) const
Get a range of all points in the detail.
Definition: GA_Detail.h:1758
void addLengthScaledAttrib(const UT_StringRef &name)
GLintptr offset
Definition: glcorearb.h:665
GA_Range getGlobalRange() const
Get a range representing the global (detail) data.
Definition: GA_Detail.h:1791
const GU_ROWeightArrayPairHandle & getWeightArrayPairHandle() const
bool operator!=(const GU_GroomFastRamp &ramp) const
void sampleAttribs(const GEO_PrimPoly &dstprimpoly, GA_Offset sourceprimoff, float blend)
static SYS_FORCE_INLINE UT_PageNum numPages(IDX_T nelements)
const GA_Attribute * keyAttrib() const
const GA_Attribute * keyAttrib() const
SYS_FORCE_INLINE const UT_QuaternionF & getValue(int pointindex, int attribindex, int guideindex)
#define UTlhsTuple
Definition: UT_Tuple.h:61
#define SYS_FALLTHROUGH
Definition: SYS_Compiler.h:61
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
UT_Vector3T< T > SYSclamp(const UT_Vector3T< T > &v, const UT_Vector3T< T > &min, const UT_Vector3T< T > &max)
Definition: UT_Vector3.h:1059
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
GA_API const UT_StringHolder perimeter
bool checkGuides(const GA_PrimitiveGroup *guidegroup)
A specialization of GA_AIFStringArray to access "shared strings".
const GU_Detail & interpMesh() const
long long int64
Definition: SYS_Types.h:116
A handle to simplify manipulation of multiple attributes.
static float openCurveUnitToReal(int n, float u_unit)
virtual const GA_AIFSharedStringArray * getAIFSharedStringArray() const
Return the attribute's shared string array interface or NULL.
SYS_FORCE_INLINE int getLinearIndex(int pointindex, int attribindex, int guideindex)
SYS_FORCE_INLINE T get(GA_Offset off, int comp=0) const
Definition: GA_Handle.h:210
SYS_FORCE_INLINE GA_Offset vertexPoint(GA_Offset vertex) const
Given a vertex, return the point it references.
Definition: GA_Detail.h:538
UT_Map< UT_StringHolder, float > tempnameset
#define GU_API
Definition: GU_API.h:14
T interpAttrib(const GA_ROHandleT< T > &attrib, const GU_GroomInterpCoords &interpcoords) const
GLuint const GLchar * name
Definition: glcorearb.h:786
void build(const GEO_Detail *gdp, const GA_PointGroup *ptgroup, bool enable_multithreading=true)
void interpolate(const GU_Detail &geo, const GA_Attribute &prim_attrib, const GA_Attribute &primuv_attrib, GA_Attribute &key_attrib, GA_Attribute &weight_attrib) const
UT_UniquePtr< T > UTmakeUnique(REST &&...args)
Definition: UT_UniquePtr.h:50
SYS_FORCE_INLINE GA_Offset offset() const
GA_Size GA_Index
Define the strictness of GA_Offset/GA_Index.
Definition: GA_Types.h:647
static fpreal computeNewLength(fpreal prevlength, SetLengthMode mode, fpreal value)
SYS_FORCE_INLINE UT_Vector3 interpN(const GU_GroomInterpCoords &interpcoords) const
exint append()
Definition: UT_Array.h:142
bool bind(GEO_Detail &detail, GA_AttributeOwner attrib_owner, const UT_StringHolder &key_attrib_name, const UT_StringHolder &weight_attrib_name)
void interpolate(C &out_keys, UT_Array< float > &out_weights, const GA_OffsetArray &offsets, const UT_FloatArray &weights, UT_Map< typename C::value_type, float > &blended_weights_set) const
GLsizei samples
Definition: glcorearb.h:1298
static UT_Tuple< UT_Vector2, UT_Vector2i > openCurvePointWeights(int n, fpreal u)
SYS_FORCE_INLINE const GU_Detail & detail()
GLenum mode
Definition: glcorearb.h:99
GU_GroomPartingLineLookup(unsigned int maxpartlines)
GU_GroomFastRamp(int numsamples=1000)
bool updateCache(CacheData &cache)
GLint j
Definition: glad.h:2733
const GA_AIFSharedStringArray * nameArray() const
GLsizeiptr size
Definition: glcorearb.h:664
GA_AttributeOwner
Definition: GA_Types.h:35
int64 getMemoryUsage(bool inclusive) const
IMATH_NAMESPACE::V2f IMATH_NAMESPACE::Box2i std::string this attribute is obsolete as of OpenEXR v3 float
const GA_AIFNumericArray * myWeightArray
void build(const GEO_Detail *gdp, const GA_PointGroup *ptgroup, const char *attrib, bool enable_multithreading=true)
GU_RWWeightArrayPairHandle(GEO_Detail &detail, GA_AttributeOwner attrib_owner, const UT_StringHolder &key_attrib_name, const UT_StringHolder &weight_attrib_name)
virtual bool get(const GA_Attribute *attrib, GA_Offset ai, UT_Array< fpreal16 > &data) const =0
static SYS_FORCE_INLINE GA_Offset primPointOffset(const GU_Detail &gdp, GA_Offset primoff, GA_Size index=0)
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
const UT_StringHolder & attribMissingWarning()
bool operator==(const GU_GroomFastRamp &ramp) const
fpreal64 fpreal
Definition: SYS_Types.h:283
GA_Attribute * weightAttrib() const
GU_ROWeightArrayPairHandle()=default
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:428
T * data()
Definition: UT_Array.h:866
GA_Size GA_PageNum
Definition: GA_Types.h:656
Utility class for containing a color ramp.
Definition: UT_Ramp.h:96
virtual bool set(GA_Attribute *attrib, GA_Offset ai, const UT_Span< const fpreal16 > &data) const =0
GLuint index
Definition: glcorearb.h:786
const GA_AIFNumericArray * indexArray() const
bool get(const GA_Attribute *attrib, GA_Offset o, UT_StringArray &v) const
UT_ValArray< GA_Size > outindices
SYS_FORCE_INLINE GA_Size getNumPrimitives() const
Return the number of primitives.
Definition: GA_Detail.h:417
UT_FloatArray weights
SYS_FORCE_INLINE float lookup(float u) const
const GA_Attribute * findFloatArray(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringRef &name, int min_size=1, int max_size=-1) const
#define GA_INVALID_INDEX
Definition: GA_Types.h:693
SYS_FORCE_INLINE GA_AttributeOwner getOwner() const
Definition: GA_Attribute.h:215
const GA_AIFSharedStringArray * myNameArray
static fpreal getIndices(fpreal c, int &c1, int &c2, int max_index, int wrap)
#define SYS_FTOLERANCE
Definition: SYS_Types.h:208
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
static T interpAttrib(const GA_ROHandleT< T > &attrib, const GA_Offset &primoff, const GA_OffsetArray &vertoffsets, const UT_FloatArray &vertweights, const T defaultvalue=T(0.0f))
static GA_Attribute * findOrCreateStringArrayAttribute(GU_Detail &gdp, GA_AttributeOwner owner, const UT_StringRef &name, int tsize)
Definition: GU_GroomUtils.h:65
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:165
GU_SkinInterpolate(const GU_Detail &guidegdp, const GU_Detail &interpgdp, const GA_ROHandleV3 &rootlookuppos, SkinGuideMode guidemode, const GA_ROHandleI &skinguidelookupattrib, const UT_StringHolder &guidesattrib, const UT_StringHolder &weightsattrib)
static const GA_PrimitiveGroup * findGroup(const GU_Detail &gdp)
void computePointWeights(GA_Offset sourceptoff, GU_MinInfo &mininfo, GA_OffsetArray &offsets, UT_FloatArray &weights) const
void set(GA_Offset primoff, const UT_StringArray &names, const UT_FloatArray &weights)
GLboolean r
Definition: glcorearb.h:1222
static void setPagesConstant(GA_RWHandleF attribhandle, float value)
void interpolate(GA_OffsetArray &guideoffsets, UT_FloatArray &guideweights, const GA_OffsetArray &skinoffsets, const UT_FloatArray &skinweights, UT_ValArray< GA_Size > &tempindices, UT_Map< GA_Size, float > &tempset) const
void clear()
Resets list to an empty list.
Definition: UT_Array.h:753
GLuint attribindex
Definition: glcorearb.h:2746
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector3.h:667
GA_Range getPrimitiveRange(const GA_PrimitiveGroup *group=0) const
Get a range of all primitives in the detail.
Definition: GA_Detail.h:1761
void mapAndCreateRealAttribs(GA_AttributeRefMap &map, const UT_ValArray< AttribChange > *changes) const
#define SYSmin(a, b)
Definition: SYS_Math.h:1953
static UT_Tuple< GA_Attribute *, GA_Attribute * > createIndexAndWeightArrayAttributes(GU_Detail &gdp, GA_AttributeOwner owner, const UT_StringHolder &name_attrib_name, const UT_StringHolder &weight_attrib_name)
GA_Storage
Definition: GA_Types.h:52
static GA_Attribute * findOrCreateFloatArrayAttribute(GU_Detail &gdp, GA_AttributeOwner owner, const UT_StringRef &name, int tsize, GA_Storage storage=GA_STORE_REAL32)
Definition: GU_GroomUtils.h:96
GU_WeightArrayInterpolate(const GEO_Detail &weightgeo)
GLint GLsizei count
Definition: glcorearb.h:405
SYS_FORCE_INLINE bool hitPointInfo(UT_Vector3 pos, UT_Vector3 dir, GU_RayInfo &info)
SYS_FORCE_INLINE GA_Offset offsetSize() const
Definition: GA_IndexMap.h:98
SYS_FORCE_INLINE bool closestPointInfo(const UT_Vector3 &pos, GU_MinInfo &mininfo) const
void bind(GA_Attribute *key_attrib, GA_Attribute *weight_attrib)
SYS_FORCE_INLINE float lookupNearest(float u) const
GU_GroomSkinIntersect(const GU_RayIntersect &rayintersect)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector3.h:665
void bind(const GA_Attribute *key_attrib, const GA_Attribute *weight_attrib)
virtual const GA_AIFNumericArray * getAIFNumericArray() const
Return the attribute's arraydata interface or NULL.