OpenVDB 13.0.0

Loading...
Searching...
No Matches
AttributeTransferUtil.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3//
4/// @file AttributeTransferUtil.h
5/// @author FX R&D Simulation team
6/// @brief Utility methods used by the From/To Polygons and From Particles SOPs
7
8#ifndef OPENVDB_HOUDINI_ATTRIBUTE_TRANSFER_UTIL_HAS_BEEN_INCLUDED
9#define OPENVDB_HOUDINI_ATTRIBUTE_TRANSFER_UTIL_HAS_BEEN_INCLUDED
10
11#include "Utils.h"
12
13#include <openvdb/openvdb.h>
14#include <openvdb/math/Proximity.h>
15#include <openvdb/util/NullInterrupter.h>
16#include <openvdb/util/Util.h>
17
18#include <GA/GA_PageIterator.h>
19#include <GA/GA_SplittableRange.h>
20#include <GEO/GEO_PrimPolySoup.h>
21#include <SYS/SYS_Types.h>
22
23#include <algorithm> // for std::sort()
24#include <cmath> // for std::floor()
25#include <limits>
26#include <memory>
27#include <set>
28#include <sstream>
29#include <string>
30#include <type_traits>
31#include <vector>
32
33
34 namespace openvdb_houdini {
35
36////////////////////////////////////////
37
38/// Get OpenVDB specific value, by calling GA_AIFTuple::get()
39/// with appropriate arguments.
40
41 template <typename ValueType> inline ValueType
42 evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif,
43 GA_Offset off, int idx)
44{
45 fpreal64 value;
46 aif->get(atr, off, value, idx);
47 return ValueType(value);
48}
49
50 template <> inline float
51 evalAttr<float>(const GA_Attribute* atr, const GA_AIFTuple* aif,
52 GA_Offset off, int idx)
53{
54 fpreal32 value;
55 aif->get(atr, off, value, idx);
56 return float(value);
57}
58
59 template <> inline openvdb::Int32
60 evalAttr<openvdb::Int32>(const GA_Attribute* atr, const GA_AIFTuple* aif,
61 GA_Offset off, int idx)
62{
63 int32 value;
64 aif->get(atr, off, value, idx);
65 return openvdb::Int32(value);
66}
67
68 template <> inline openvdb::Int64
69 evalAttr<openvdb::Int64>(const GA_Attribute* atr, const GA_AIFTuple* aif,
70 GA_Offset off, int idx)
71{
72 int64 value;
73 aif->get(atr, off, value, idx);
74 return openvdb::Int64(value);
75}
76
77 template <> inline openvdb::Vec3i
78 evalAttr<openvdb::Vec3i>(const GA_Attribute* atr, const GA_AIFTuple* aif,
79 GA_Offset off, int)
80{
81 openvdb::Vec3i vec;
82
83 int32 comp;
84 aif->get(atr, off, comp, 0);
85 vec[0] = openvdb::Int32(comp);
86
87 aif->get(atr, off, comp, 1);
88 vec[1] = openvdb::Int32(comp);
89
90 aif->get(atr, off, comp, 2);
91 vec[2] = openvdb::Int32(comp);
92
93 return vec;
94}
95
96 template <> inline openvdb::Vec3s
97 evalAttr<openvdb::Vec3s>(const GA_Attribute* atr, const GA_AIFTuple* aif,
98 GA_Offset off, int)
99{
100 openvdb::Vec3s vec;
101
102 fpreal32 comp;
103 aif->get(atr, off, comp, 0);
104 vec[0] = float(comp);
105
106 aif->get(atr, off, comp, 1);
107 vec[1] = float(comp);
108
109 aif->get(atr, off, comp, 2);
110 vec[2] = float(comp);
111
112 return vec;
113}
114
115 template <> inline openvdb::Vec3d
116 evalAttr<openvdb::Vec3d>(const GA_Attribute* atr, const GA_AIFTuple* aif,
117 GA_Offset off, int)
118{
119 openvdb::Vec3d vec;
120
121 fpreal64 comp;
122 aif->get(atr, off, comp, 0);
123 vec[0] = double(comp);
124
125 aif->get(atr, off, comp, 1);
126 vec[1] = double(comp);
127
128 aif->get(atr, off, comp, 2);
129 vec[2] = double(comp);
130
131 return vec;
132}
133
134
135////////////////////////////////////////
136
137
138/// Combine different value types.
139
140 template <typename ValueType> inline ValueType
141 combine(const ValueType& v0, const ValueType& v1, const ValueType& v2,
142 const openvdb::Vec3d& w)
143{
144 return ValueType(v0 * w[0] + v1 * w[1] + v2 * w[2]);
145}
146
147 template <> inline openvdb::Int32
148 combine(const openvdb::Int32& v0, const openvdb::Int32& v1,
149 const openvdb::Int32& v2, const openvdb::Vec3d& w)
150{
151 if (w[2] > w[0] && w[2] > w[1]) return v2;
152 if (w[1] > w[0] && w[1] > w[2]) return v1;
153 return v0;
154}
155
156 template <> inline openvdb::Int64
157 combine(const openvdb::Int64& v0, const openvdb::Int64& v1,
158 const openvdb::Int64& v2, const openvdb::Vec3d& w)
159{
160 if (w[2] > w[0] && w[2] > w[1]) return v2;
161 if (w[1] > w[0] && w[1] > w[2]) return v1;
162 return v0;
163}
164
165 template <> inline openvdb::Vec3i
166 combine(const openvdb::Vec3i& v0, const openvdb::Vec3i& v1,
167 const openvdb::Vec3i& v2, const openvdb::Vec3d& w)
168{
169 if (w[2] > w[0] && w[2] > w[1]) return v2;
170 if (w[1] > w[0] && w[1] > w[2]) return v1;
171 return v0;
172}
173
174 template <> inline openvdb::Vec3s
175 combine(const openvdb::Vec3s& v0, const openvdb::Vec3s& v1,
176 const openvdb::Vec3s& v2, const openvdb::Vec3d& w)
177{
178 openvdb::Vec3s vec;
179
180 vec[0] = float(v0[0] * w[0] + v1[0] * w[1] + v2[0] * w[2]);
181 vec[1] = float(v0[1] * w[0] + v1[1] * w[1] + v2[1] * w[2]);
182 vec[2] = float(v0[2] * w[0] + v1[2] * w[1] + v2[2] * w[2]);
183
184 return vec;
185}
186
187 template <> inline openvdb::Vec3d
188 combine(const openvdb::Vec3d& v0, const openvdb::Vec3d& v1,
189 const openvdb::Vec3d& v2, const openvdb::Vec3d& w)
190{
191 openvdb::Vec3d vec;
192
193 vec[0] = v0[0] * w[0] + v1[0] * w[1] + v2[0] * w[2];
194 vec[1] = v0[1] * w[0] + v1[1] * w[1] + v2[1] * w[2];
195 vec[2] = v0[2] * w[0] + v1[2] * w[1] + v2[2] * w[2];
196
197 return vec;
198}
199
200
201////////////////////////////////////////
202
203
204/// @brief Get an OpenVDB-specific value by evaluating GA_Default::get()
205/// with appropriate arguments.
206 template <typename ValueType> inline ValueType
207 evalAttrDefault(const GA_Defaults& defaults, int idx)
208{
209 fpreal64 value;
210 defaults.get(idx, value);
211 return ValueType(value);
212}
213
214 template <> inline float
215 evalAttrDefault<float>(const GA_Defaults& defaults, int /*idx*/)
216{
217 fpreal32 value;
218 defaults.get(0, value);
219 return float(value);
220}
221
222 template <> inline openvdb::Int32
223 evalAttrDefault<openvdb::Int32>(const GA_Defaults& defaults, int idx)
224{
225 int32 value;
226 defaults.get(idx, value);
227 return openvdb::Int32(value);
228}
229
230 template <> inline openvdb::Int64
231 evalAttrDefault<openvdb::Int64>(const GA_Defaults& defaults, int idx)
232{
233 int64 value;
234 defaults.get(idx, value);
235 return openvdb::Int64(value);
236}
237
238 template <> inline openvdb::Vec3i
239 evalAttrDefault<openvdb::Vec3i>(const GA_Defaults& defaults, int)
240{
241 openvdb::Vec3i vec;
242 int32 value;
243
244 defaults.get(0, value);
245 vec[0] = openvdb::Int32(value);
246
247 defaults.get(1, value);
248 vec[1] = openvdb::Int32(value);
249
250 defaults.get(2, value);
251 vec[2] = openvdb::Int32(value);
252
253 return vec;
254}
255
256 template <> inline openvdb::Vec3s
257 evalAttrDefault<openvdb::Vec3s>(const GA_Defaults& defaults, int)
258{
259 openvdb::Vec3s vec;
260 fpreal32 value;
261
262 defaults.get(0, value);
263 vec[0] = float(value);
264
265 defaults.get(1, value);
266 vec[1] = float(value);
267
268 defaults.get(2, value);
269 vec[2] = float(value);
270
271 return vec;
272}
273
274 template <> inline openvdb::Vec3d
275 evalAttrDefault<openvdb::Vec3d>(const GA_Defaults& defaults, int)
276{
277 openvdb::Vec3d vec;
278 fpreal64 value;
279
280 defaults.get(0, value);
281 vec[0] = double(value);
282
283 defaults.get(1, value);
284 vec[1] = double(value);
285
286 defaults.get(2, value);
287 vec[2] = double(value);
288
289 return vec;
290}
291
292 template <> inline openvdb::math::Quat<float>
293 evalAttrDefault<openvdb::math::Quat<float>>(const GA_Defaults& defaults, int)
294{
295 openvdb::math::Quat<float> quat;
296 fpreal32 value;
297
298 for (int i = 0; i < 4; i++) {
299 defaults.get(i, value);
300 quat[i] = float(value);
301 }
302
303 return quat;
304}
305
306 template <> inline openvdb::math::Quat<double>
307 evalAttrDefault<openvdb::math::Quat<double>>(const GA_Defaults& defaults, int)
308{
309 openvdb::math::Quat<double> quat;
310 fpreal64 value;
311
312 for (int i = 0; i < 4; i++) {
313 defaults.get(i, value);
314 quat[i] = double(value);
315 }
316
317 return quat;
318}
319
320 template <> inline openvdb::math::Mat3<float>
321 evalAttrDefault<openvdb::math::Mat3<float>>(const GA_Defaults& defaults, int)
322{
323 openvdb::math::Mat3<float> mat;
324 fpreal64 value;
325 float* data = mat.asPointer();
326
327 for (int i = 0; i < 9; i++) {
328 defaults.get(i, value);
329 data[i] = float(value);
330 }
331
332 return mat;
333}
334
335 template <> inline openvdb::math::Mat3<double>
336 evalAttrDefault<openvdb::math::Mat3<double>>(const GA_Defaults& defaults, int)
337{
338 openvdb::math::Mat3<double> mat;
339 fpreal64 value;
340 double* data = mat.asPointer();
341
342 for (int i = 0; i < 9; i++) {
343 defaults.get(i, value);
344 data[i] = double(value);
345 }
346
347 return mat;
348}
349
350 template <> inline openvdb::math::Mat4<float>
351 evalAttrDefault<openvdb::math::Mat4<float>>(const GA_Defaults& defaults, int)
352{
353 openvdb::math::Mat4<float> mat;
354 fpreal64 value;
355 float* data = mat.asPointer();
356
357 for (int i = 0; i < 16; i++) {
358 defaults.get(i, value);
359 data[i] = float(value);
360 }
361
362 return mat;
363}
364
365 template <> inline openvdb::math::Mat4<double>
366 evalAttrDefault<openvdb::math::Mat4<double>>(const GA_Defaults& defaults, int)
367{
368 openvdb::math::Mat4<double> mat;
369 fpreal64 value;
370 double* data = mat.asPointer();
371
372 for (int i = 0; i < 16; i++) {
373 defaults.get(i, value);
374 data[i] = double(value);
375 }
376
377 return mat;
378}
379
380
381////////////////////////////////////////
382
383
384 class AttributeDetailBase
385{
386 public:
387 using Ptr = std::shared_ptr<AttributeDetailBase>;
388
389 virtual ~AttributeDetailBase() = default;
390
391 AttributeDetailBase(const AttributeDetailBase&) = default;
392 AttributeDetailBase& operator=(const AttributeDetailBase&) = default;
393
394 virtual void set(const openvdb::Coord& ijk, const GA_Offset (&offsets)[3],
395 const openvdb::Vec3d& weights) = 0;
396
397 virtual void set(const openvdb::Coord& ijk, GA_Offset offset) = 0;
398
399 virtual openvdb::GridBase::Ptr& grid() = 0;
400 virtual std::string& name() = 0;
401
402 virtual AttributeDetailBase::Ptr copy() = 0;
403
404 protected:
405 AttributeDetailBase() {}
406};
407
408
409 using AttributeDetailList = std::vector<AttributeDetailBase::Ptr>;
410
411
412////////////////////////////////////////
413
414
415 template <class VDBGridType>
416 class AttributeDetail: public AttributeDetailBase
417{
418 public:
419 using ValueType = typename VDBGridType::ValueType;
420
421 AttributeDetail(
422 openvdb::GridBase::Ptr grid,
423 const GA_Attribute* attribute,
424 const GA_AIFTuple* tupleAIF,
425 const int tupleIndex,
426 const bool isVector = false);
427
428 void set(const openvdb::Coord& ijk, const GA_Offset (&offsets)[3],
429 const openvdb::Vec3d& weights) override;
430
431 void set(const openvdb::Coord& ijk, GA_Offset offset) override;
432
433 openvdb::GridBase::Ptr& grid() override { return mGrid; }
434 std::string& name() override { return mName; }
435
436 AttributeDetailBase::Ptr copy() override;
437
438 protected:
439 AttributeDetail();
440
441 private:
442 openvdb::GridBase::Ptr mGrid;
443 typename VDBGridType::Accessor mAccessor;
444
445 const GA_Attribute* mAttribute;
446 const GA_AIFTuple* mTupleAIF;
447 const int mTupleIndex;
448 std::string mName;
449};
450
451
452 template <class VDBGridType>
453 AttributeDetail<VDBGridType>::AttributeDetail():
454 mAttribute(nullptr),
455 mTupleAIF(nullptr),
456 mTupleIndex(0)
457{
458}
459
460
461 template <class VDBGridType>
462 AttributeDetail<VDBGridType>::AttributeDetail(
463 openvdb::GridBase::Ptr grid,
464 const GA_Attribute* attribute,
465 const GA_AIFTuple* tupleAIF,
466 const int tupleIndex,
467 const bool isVector):
468 mGrid(grid),
469 mAccessor(openvdb::GridBase::grid<VDBGridType>(mGrid)->getAccessor()),
470 mAttribute(attribute),
471 mTupleAIF(tupleAIF),
472 mTupleIndex(tupleIndex)
473{
474 std::ostringstream name;
475 name << mAttribute->getName();
476
477 const int tupleSize = mTupleAIF->getTupleSize(mAttribute);
478
479 if(!isVector && tupleSize != 1) {
480 name << "_" << mTupleIndex;
481 }
482
483 mName = name.str();
484}
485
486
487 template <class VDBGridType>
488 void
489 AttributeDetail<VDBGridType>::set(const openvdb::Coord& ijk,
490 const GA_Offset (&offsets)[3], const openvdb::Vec3d& weights)
491{
492 ValueType v0 = evalAttr<ValueType>(
493 mAttribute, mTupleAIF, offsets[0], mTupleIndex);
494
495 ValueType v1 = evalAttr<ValueType>(
496 mAttribute, mTupleAIF, offsets[1], mTupleIndex);
497
498 ValueType v2 = evalAttr<ValueType>(
499 mAttribute, mTupleAIF, offsets[2], mTupleIndex);
500
501 mAccessor.setValue(ijk, combine<ValueType>(v0, v1, v2, weights));
502}
503
504 template <class VDBGridType>
505 void
506 AttributeDetail<VDBGridType>::set(const openvdb::Coord& ijk, GA_Offset offset)
507{
508 mAccessor.setValue(ijk,
509 evalAttr<ValueType>(mAttribute, mTupleAIF, offset, mTupleIndex));
510}
511
512 template <class VDBGridType>
513 AttributeDetailBase::Ptr
514 AttributeDetail<VDBGridType>::copy()
515{
516 return AttributeDetailBase::Ptr(new AttributeDetail<VDBGridType>(*this));
517}
518
519
520////////////////////////////////////////
521
522
523// TBB object to transfer mesh attributes.
524// Only quads and/or triangles are supported
525// NOTE: This class has all code in the header and so it cannot have OPENVDB_HOUDINI_API.
526 class MeshAttrTransfer
527{
528 public:
529 using IterRange = openvdb::tree::IteratorRange<openvdb::Int32Tree::LeafCIter>;
530
531 inline
532 MeshAttrTransfer(
533 AttributeDetailList &pointAttributes,
534 AttributeDetailList &vertexAttributes,
535 AttributeDetailList &primitiveAttributes,
536 const openvdb::Int32Grid& closestPrimGrid,
537 const openvdb::math::Transform& transform,
538 const GU_Detail& meshGdp);
539
540 inline
541 MeshAttrTransfer(const MeshAttrTransfer &other);
542
543 inline
544 ~MeshAttrTransfer() {}
545
546 /// Main calls
547 inline void runParallel();
548 inline void runSerial();
549
550 inline void operator()(IterRange &range) const;
551
552 private:
553 AttributeDetailList mPointAttributes, mVertexAttributes, mPrimitiveAttributes;
554 const openvdb::Int32Grid& mClosestPrimGrid;
555
556 const openvdb::math::Transform& mTransform;
557
558 const GA_Detail &mMeshGdp;
559};
560
561
562 MeshAttrTransfer::MeshAttrTransfer(
563 AttributeDetailList& pointAttributes,
564 AttributeDetailList& vertexAttributes,
565 AttributeDetailList& primitiveAttributes,
566 const openvdb::Int32Grid& closestPrimGrid,
567 const openvdb::math::Transform& transform,
568 const GU_Detail& meshGdp):
569 mPointAttributes(pointAttributes),
570 mVertexAttributes(vertexAttributes),
571 mPrimitiveAttributes(primitiveAttributes),
572 mClosestPrimGrid(closestPrimGrid),
573 mTransform(transform),
574 mMeshGdp(meshGdp)
575{
576}
577
578
579 MeshAttrTransfer::MeshAttrTransfer(const MeshAttrTransfer &other):
580 mPointAttributes(other.mPointAttributes.size()),
581 mVertexAttributes(other.mVertexAttributes.size()),
582 mPrimitiveAttributes(other.mPrimitiveAttributes.size()),
583 mClosestPrimGrid(other.mClosestPrimGrid),
584 mTransform(other.mTransform),
585 mMeshGdp(other.mMeshGdp)
586{
587 // Deep-copy the AttributeDetail arrays, to construct unique tree
588 // accessors per thread.
589
590 for (size_t i = 0, N = other.mPointAttributes.size(); i < N; ++i) {
591 mPointAttributes[i] = other.mPointAttributes[i]->copy();
592 }
593
594 for (size_t i = 0, N = other.mVertexAttributes.size(); i < N; ++i) {
595 mVertexAttributes[i] = other.mVertexAttributes[i]->copy();
596 }
597
598 for (size_t i = 0, N = other.mPrimitiveAttributes.size(); i < N; ++i) {
599 mPrimitiveAttributes[i] = other.mPrimitiveAttributes[i]->copy();
600 }
601}
602
603
604 void
605 MeshAttrTransfer::runParallel()
606{
607 IterRange range(mClosestPrimGrid.tree().beginLeaf());
608 tbb::parallel_for(range, *this);
609}
610
611 void
612 MeshAttrTransfer::runSerial()
613{
614 IterRange range(mClosestPrimGrid.tree().beginLeaf());
615 (*this)(range);
616}
617
618
619 void
620 MeshAttrTransfer::operator()(IterRange &range) const
621{
622 openvdb::Int32Tree::LeafNodeType::ValueOnCIter iter;
623
624 openvdb::Coord ijk;
625
626 const bool ptnAttrTransfer = mPointAttributes.size() > 0;
627 const bool vtxAttrTransfer = mVertexAttributes.size() > 0;
628
629 GA_Offset vtxOffsetList[4], ptnOffsetList[4], vtxOffsets[3], ptnOffsets[3], prmOffset;
630 openvdb::Vec3d ptnList[4], xyz, cpt, cpt2, uvw, uvw2;
631
632 for ( ; range; ++range) {
633 iter = range.iterator()->beginValueOn();
634 for ( ; iter; ++iter) {
635
636 ijk = iter.getCoord();
637
638 const GA_Index prmIndex = iter.getValue();
639 prmOffset = mMeshGdp.primitiveOffset(prmIndex);
640
641 // Transfer primitive attributes
642 for (size_t i = 0, N = mPrimitiveAttributes.size(); i < N; ++i) {
643 mPrimitiveAttributes[i]->set(ijk, prmOffset);
644 }
645
646 if (!ptnAttrTransfer && !vtxAttrTransfer) continue;
647
648 // Transfer vertex and point attributes
649 const GA_Primitive * primRef = mMeshGdp.getPrimitiveList().get(prmOffset);
650
651 const GA_Size vtxn = primRef->getVertexCount();
652
653 // Get vertex and point offests
654 for (GA_Size vtx = 0; vtx < vtxn; ++vtx) {
655 const GA_Offset vtxoff = primRef->getVertexOffset(vtx);
656 ptnOffsetList[vtx] = mMeshGdp.vertexPoint(vtxoff);
657 vtxOffsetList[vtx] = vtxoff;
658
659 UT_Vector3 p = mMeshGdp.getPos3(ptnOffsetList[vtx]);
660 ptnList[vtx][0] = double(p[0]);
661 ptnList[vtx][1] = double(p[1]);
662 ptnList[vtx][2] = double(p[2]);
663 }
664
665 xyz = mTransform.indexToWorld(ijk);
666
667 // Compute barycentric coordinates
668
669 cpt = closestPointOnTriangleToPoint(
670 ptnList[0], ptnList[2], ptnList[1], xyz, uvw);
671
672 vtxOffsets[0] = vtxOffsetList[0]; // cpt offsets
673 ptnOffsets[0] = ptnOffsetList[0];
674 vtxOffsets[1] = vtxOffsetList[2];
675 ptnOffsets[1] = ptnOffsetList[2];
676 vtxOffsets[2] = vtxOffsetList[1];
677 ptnOffsets[2] = ptnOffsetList[1];
678
679 if (4 == vtxn) {
680 cpt2 = closestPointOnTriangleToPoint(
681 ptnList[0], ptnList[3], ptnList[2], xyz, uvw2);
682
683 if ((cpt2 - xyz).lengthSqr() < (cpt - xyz).lengthSqr()) {
684 uvw = uvw2;
685 vtxOffsets[1] = vtxOffsetList[3];
686 ptnOffsets[1] = ptnOffsetList[3];
687 vtxOffsets[2] = vtxOffsetList[2];
688 ptnOffsets[2] = ptnOffsetList[2];
689 }
690 }
691
692 // Transfer vertex attributes
693 for (size_t i = 0, N = mVertexAttributes.size(); i < N; ++i) {
694 mVertexAttributes[i]->set(ijk, vtxOffsets, uvw);
695 }
696
697 // Transfer point attributes
698 for (size_t i = 0, N = mPointAttributes.size(); i < N; ++i) {
699 mPointAttributes[i]->set(ijk, ptnOffsets, uvw);
700 }
701
702 } // end sparse voxel iteration.
703 } // end leaf-node iteration
704}
705
706
707////////////////////////////////////////
708
709
710// TBB object to transfer mesh attributes.
711// Only quads and/or triangles are supported
712// NOTE: This class has all code in the header and so it cannot have OPENVDB_HOUDINI_API.
713 class PointAttrTransfer
714{
715 public:
716 using IterRange = openvdb::tree::IteratorRange<openvdb::Int32Tree::LeafCIter>;
717
718 inline PointAttrTransfer(
719 AttributeDetailList &pointAttributes,
720 const openvdb::Int32Grid& closestPtnIdxGrid,
721 const GU_Detail& ptGeop);
722
723 inline PointAttrTransfer(const PointAttrTransfer &other);
724
725 inline ~PointAttrTransfer() {}
726
727 /// Main calls
728 inline void runParallel();
729 inline void runSerial();
730
731 inline void operator()(IterRange &range) const;
732
733 private:
734 AttributeDetailList mPointAttributes;
735 const openvdb::Int32Grid& mClosestPtnIdxGrid;
736 const GA_Detail &mPtGeo;
737};
738
739
740 PointAttrTransfer::PointAttrTransfer(
741 AttributeDetailList& pointAttributes,
742 const openvdb::Int32Grid& closestPtnIdxGrid,
743 const GU_Detail& ptGeop):
744 mPointAttributes(pointAttributes),
745 mClosestPtnIdxGrid(closestPtnIdxGrid),
746 mPtGeo(ptGeop)
747{
748}
749
750
751 PointAttrTransfer::PointAttrTransfer(const PointAttrTransfer &other):
752 mPointAttributes(other.mPointAttributes.size()),
753 mClosestPtnIdxGrid(other.mClosestPtnIdxGrid),
754 mPtGeo(other.mPtGeo)
755{
756 // Deep-copy the AttributeDetail arrays, to construct unique tree
757 // accessors per thread.
758
759 for (size_t i = 0, N = other.mPointAttributes.size(); i < N; ++i) {
760 mPointAttributes[i] = other.mPointAttributes[i]->copy();
761 }
762}
763
764
765 void
766 PointAttrTransfer::runParallel()
767{
768 IterRange range(mClosestPtnIdxGrid.tree().beginLeaf());
769 tbb::parallel_for(range, *this);
770}
771
772 void
773 PointAttrTransfer::runSerial()
774{
775 IterRange range(mClosestPtnIdxGrid.tree().beginLeaf());
776 (*this)(range);
777}
778
779
780 void
781 PointAttrTransfer::operator()(IterRange &range) const
782{
783 openvdb::Int32Tree::LeafNodeType::ValueOnCIter iter;
784 openvdb::Coord ijk;
785
786 for ( ; range; ++range) {
787 iter = range.iterator()->beginValueOn();
788 for ( ; iter; ++iter) {
789
790 ijk = iter.getCoord();
791
792 const GA_Index pointIndex = iter.getValue();
793 const GA_Offset pointOffset = mPtGeo.pointOffset(pointIndex);
794
795 // Transfer point attributes
796 for (size_t i = 0, N = mPointAttributes.size(); i < N; ++i) {
797 mPointAttributes[i]->set(ijk, pointOffset);
798 }
799 } // end sparse voxel iteration.
800 } // end leaf-node iteration
801}
802
803
804////////////////////////////////////////
805
806// Mesh to Mesh Attribute Transfer Utils
807
808
809 struct AttributeCopyBase
810{
811 using Ptr = std::shared_ptr<AttributeCopyBase>;
812
813 virtual ~AttributeCopyBase() {}
814 virtual void copy(GA_Offset /*source*/, GA_Offset /*target*/) = 0;
815 virtual void copy(GA_Offset&, GA_Offset&, GA_Offset&, GA_Offset /*target*/,
816 const openvdb::Vec3d& /*uvw*/) = 0;
817 protected:
818 AttributeCopyBase() {}
819};
820
821
822 template<class ValueType>
823 struct AttributeCopy: public AttributeCopyBase
824{
825 public:
826 AttributeCopy(const GA_Attribute& sourceAttr, GA_Attribute& targetAttr)
827 : mSourceAttr(sourceAttr)
828 , mTargetAttr(targetAttr)
829 , mAIFTuple(*mSourceAttr.getAIFTuple())
830 , mTupleSize(mAIFTuple.getTupleSize(&mSourceAttr))
831 {
832 }
833
834 void copy(GA_Offset source, GA_Offset target) override
835 {
836 ValueType data;
837 for (int i = 0; i < mTupleSize; ++i) {
838 mAIFTuple.get(&mSourceAttr, source, data, i);
839 mAIFTuple.set(&mTargetAttr, target, data, i);
840 }
841 }
842
843 void copy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target,
844 const openvdb::Vec3d& uvw) override
845 {
846 doCopy<ValueType>(v0, v1, v2, target, uvw);
847 }
848
849 private:
850 template<typename T>
851 typename std::enable_if<std::is_integral<T>::value>::type
852 doCopy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target, const openvdb::Vec3d& uvw)
853 {
854 GA_Offset source = v0;
855 double min = uvw[0];
856
857 if (uvw[1] < min) {
858 min = uvw[1];
859 source = v1;
860 }
861 if (uvw[2] < min) source = v2;
862
863
864 ValueType data;
865 for (int i = 0; i < mTupleSize; ++i) {
866 mAIFTuple.get(&mSourceAttr, source, data, i);
867 mAIFTuple.set(&mTargetAttr, target, data, i);
868 }
869 }
870
871 template <typename T>
872 typename std::enable_if<std::is_floating_point<T>::value>::type
873 doCopy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target, const openvdb::Vec3d& uvw)
874 {
875 ValueType a, b, c;
876 for (int i = 0; i < mTupleSize; ++i) {
877 mAIFTuple.get(&mSourceAttr, v0, a, i);
878 mAIFTuple.get(&mSourceAttr, v1, b, i);
879 mAIFTuple.get(&mSourceAttr, v2, c, i);
880 mAIFTuple.set(&mTargetAttr, target, a*uvw[0] + b*uvw[1] + c*uvw[2], i);
881 }
882 }
883
884
885 const GA_Attribute& mSourceAttr;
886 GA_Attribute& mTargetAttr;
887 const GA_AIFTuple& mAIFTuple;
888 int mTupleSize;
889};
890
891
892 struct StrAttributeCopy: public AttributeCopyBase
893{
894 public:
895 StrAttributeCopy(const GA_Attribute& sourceAttr, GA_Attribute& targetAttr)
896 : mSourceAttr(sourceAttr)
897 , mTargetAttr(targetAttr)
898 , mAIF(*mSourceAttr.getAIFSharedStringTuple())
899 , mTupleSize(mAIF.getTupleSize(&mSourceAttr))
900 {
901 }
902
903 void copy(GA_Offset source, GA_Offset target) override
904 {
905 for (int i = 0; i < mTupleSize; ++i) {
906 mAIF.setString(&mTargetAttr, target, mAIF.getString(&mSourceAttr, source, i), i);
907 }
908 }
909
910 void copy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target,
911 const openvdb::Vec3d& uvw) override
912 {
913 GA_Offset source = v0;
914 double min = uvw[0];
915
916 if (uvw[1] < min) {
917 min = uvw[1];
918 source = v1;
919 }
920 if (uvw[2] < min) source = v2;
921
922 for (int i = 0; i < mTupleSize; ++i) {
923 mAIF.setString(&mTargetAttr, target, mAIF.getString(&mSourceAttr, source, i), i);
924 }
925 }
926
927 protected:
928 const GA_Attribute& mSourceAttr;
929 GA_Attribute& mTargetAttr;
930 const GA_AIFSharedStringTuple& mAIF;
931 int mTupleSize;
932};
933
934
935////////////////////////////////////////
936
937
938 inline AttributeCopyBase::Ptr
939 createAttributeCopier(const GA_Attribute& sourceAttr, GA_Attribute& targetAttr)
940{
941 const GA_AIFTuple * aifTuple = sourceAttr.getAIFTuple();
942 AttributeCopyBase::Ptr attr;
943
944 if (aifTuple) {
945 const GA_Storage sourceStorage = aifTuple->getStorage(&sourceAttr);
946 const GA_Storage targetStorage = aifTuple->getStorage(&targetAttr);
947
948 const int sourceTupleSize = aifTuple->getTupleSize(&sourceAttr);
949 const int targetTupleSize = aifTuple->getTupleSize(&targetAttr);
950
951 if (sourceStorage == targetStorage && sourceTupleSize == targetTupleSize) {
952 switch (sourceStorage)
953 {
954 case GA_STORE_INT16:
955 case GA_STORE_INT32:
956 attr = AttributeCopyBase::Ptr(
957 new AttributeCopy<int32>(sourceAttr, targetAttr));
958 break;
959 case GA_STORE_INT64:
960 attr = AttributeCopyBase::Ptr(
961 new AttributeCopy<int64>(sourceAttr, targetAttr));
962 break;
963 case GA_STORE_REAL16:
964 case GA_STORE_REAL32:
965 attr = AttributeCopyBase::Ptr(
966 new AttributeCopy<fpreal32>(sourceAttr, targetAttr));
967 break;
968 case GA_STORE_REAL64:
969 attr = AttributeCopyBase::Ptr(
970 new AttributeCopy<fpreal64>(sourceAttr, targetAttr));
971 break;
972 default:
973 break;
974 }
975 }
976 } else {
977 const GA_AIFSharedStringTuple * aifString = sourceAttr.getAIFSharedStringTuple();
978 if (aifString) {
979 attr = AttributeCopyBase::Ptr(new StrAttributeCopy(sourceAttr, targetAttr));
980 }
981 }
982
983 return attr;
984}
985
986
987////////////////////////////////////////
988
989
990 inline GA_Offset
991 findClosestPrimitiveToPoint(
992 const GU_Detail& geo, const std::set<GA_Index>& primitives, const openvdb::Vec3d& p,
993 GA_Offset& vert0, GA_Offset& vert1, GA_Offset& vert2, openvdb::Vec3d& uvw)
994{
995 std::set<GA_Index>::const_iterator it = primitives.begin();
996
997 GA_Offset primOffset = GA_INVALID_OFFSET;
998 const GA_Primitive * primRef = nullptr;
999 double minDist = std::numeric_limits<double>::max();
1000
1001 openvdb::Vec3d a, b, c, d, tmpUVW;
1002 UT_Vector3 tmpPoint;
1003
1004 for (; it != primitives.end(); ++it) {
1005
1006 const GA_Offset offset = geo.primitiveOffset(*it);
1007 primRef = geo.getPrimitiveList().get(offset);
1008
1009 const GA_Size vertexCount = primRef->getVertexCount();
1010
1011
1012 if (vertexCount == 3 || vertexCount == 4) {
1013
1014 tmpPoint = geo.getPos3(primRef->getPointOffset(0));
1015 a[0] = tmpPoint.x();
1016 a[1] = tmpPoint.y();
1017 a[2] = tmpPoint.z();
1018
1019 tmpPoint = geo.getPos3(primRef->getPointOffset(1));
1020 b[0] = tmpPoint.x();
1021 b[1] = tmpPoint.y();
1022 b[2] = tmpPoint.z();
1023
1024 tmpPoint = geo.getPos3(primRef->getPointOffset(2));
1025 c[0] = tmpPoint.x();
1026 c[1] = tmpPoint.y();
1027 c[2] = tmpPoint.z();
1028
1029 double tmpDist =
1030 (p - openvdb::math::closestPointOnTriangleToPoint(a, c, b, p, tmpUVW)).lengthSqr();
1031
1032 if (tmpDist < minDist) {
1033 minDist = tmpDist;
1034 primOffset = offset;
1035 uvw = tmpUVW;
1036 vert0 = primRef->getVertexOffset(0);
1037 vert1 = primRef->getVertexOffset(2);
1038 vert2 = primRef->getVertexOffset(1);
1039 }
1040
1041 if (vertexCount == 4) {
1042 tmpPoint = geo.getPos3(primRef->getPointOffset(3));
1043 d[0] = tmpPoint.x();
1044 d[1] = tmpPoint.y();
1045 d[2] = tmpPoint.z();
1046
1047 tmpDist = (p - openvdb::math::closestPointOnTriangleToPoint(
1048 a, d, c, p, tmpUVW)).lengthSqr();
1049 if (tmpDist < minDist) {
1050 minDist = tmpDist;
1051 primOffset = offset;
1052 uvw = tmpUVW;
1053 vert0 = primRef->getVertexOffset(0);
1054 vert1 = primRef->getVertexOffset(3);
1055 vert2 = primRef->getVertexOffset(2);
1056 }
1057 }
1058
1059 }
1060 }
1061
1062 return primOffset;
1063}
1064
1065
1066// Faster for small primitive counts
1067 inline GA_Offset
1068 findClosestPrimitiveToPoint(
1069 const GU_Detail& geo, std::vector<GA_Index>& primitives, const openvdb::Vec3d& p,
1070 GA_Offset& vert0, GA_Offset& vert1, GA_Offset& vert2, openvdb::Vec3d& uvw)
1071{
1072 GA_Offset primOffset = GA_INVALID_OFFSET;
1073 const GA_Primitive * primRef = nullptr;
1074 double minDist = std::numeric_limits<double>::max();
1075
1076 openvdb::Vec3d a, b, c, d, tmpUVW;
1077 UT_Vector3 tmpPoint;
1078
1079 std::sort(primitives.begin(), primitives.end());
1080
1081 GA_Index lastPrim = -1;
1082 for (size_t n = 0, N = primitives.size(); n < N; ++n) {
1083 if (primitives[n] == lastPrim) continue;
1084 lastPrim = primitives[n];
1085
1086 const GA_Offset offset = geo.primitiveOffset(lastPrim);
1087 primRef = geo.getPrimitiveList().get(offset);
1088
1089 const GA_Size vertexCount = primRef->getVertexCount();
1090
1091
1092 if (vertexCount == 3 || vertexCount == 4) {
1093
1094 tmpPoint = geo.getPos3(primRef->getPointOffset(0));
1095 a[0] = tmpPoint.x();
1096 a[1] = tmpPoint.y();
1097 a[2] = tmpPoint.z();
1098
1099 tmpPoint = geo.getPos3(primRef->getPointOffset(1));
1100 b[0] = tmpPoint.x();
1101 b[1] = tmpPoint.y();
1102 b[2] = tmpPoint.z();
1103
1104 tmpPoint = geo.getPos3(primRef->getPointOffset(2));
1105 c[0] = tmpPoint.x();
1106 c[1] = tmpPoint.y();
1107 c[2] = tmpPoint.z();
1108
1109 double tmpDist =
1110 (p - openvdb::math::closestPointOnTriangleToPoint(a, c, b, p, tmpUVW)).lengthSqr();
1111
1112 if (tmpDist < minDist) {
1113 minDist = tmpDist;
1114 primOffset = offset;
1115 uvw = tmpUVW;
1116 vert0 = primRef->getVertexOffset(0);
1117 vert1 = primRef->getVertexOffset(2);
1118 vert2 = primRef->getVertexOffset(1);
1119 }
1120
1121 if (vertexCount == 4) {
1122 tmpPoint = geo.getPos3(primRef->getPointOffset(3));
1123 d[0] = tmpPoint.x();
1124 d[1] = tmpPoint.y();
1125 d[2] = tmpPoint.z();
1126
1127 tmpDist = (p - openvdb::math::closestPointOnTriangleToPoint(
1128 a, d, c, p, tmpUVW)).lengthSqr();
1129 if (tmpDist < minDist) {
1130 minDist = tmpDist;
1131 primOffset = offset;
1132 uvw = tmpUVW;
1133 vert0 = primRef->getVertexOffset(0);
1134 vert1 = primRef->getVertexOffset(3);
1135 vert2 = primRef->getVertexOffset(2);
1136 }
1137 }
1138
1139 }
1140 }
1141
1142 return primOffset;
1143}
1144
1145
1146////////////////////////////////////////
1147
1148
1149 template<class GridType>
1150 class TransferPrimitiveAttributesOp
1151{
1152 public:
1153 using IndexT = typename GridType::ValueType;
1154 using IndexAccT = typename GridType::ConstAccessor;
1155 using AttrCopyPtrVec = std::vector<AttributeCopyBase::Ptr>;
1156
1157 TransferPrimitiveAttributesOp(
1158 const GU_Detail& sourceGeo,
1159 GU_Detail& targetGeo,
1160 const GridType& indexGrid,
1161 AttrCopyPtrVec& primAttributes,
1162 AttrCopyPtrVec& vertAttributes)
1163 : mSourceGeo(sourceGeo)
1164 , mTargetGeo(targetGeo)
1165 , mIndexGrid(indexGrid)
1166 , mPrimAttributes(primAttributes)
1167 , mVertAttributes(vertAttributes)
1168 {
1169 }
1170
1171 inline void operator()(const GA_SplittableRange&) const;
1172
1173 private:
1174 inline void copyPrimAttrs(const GA_Primitive&, const UT_Vector3&, IndexAccT&) const;
1175
1176 template<typename PrimT>
1177 inline void copyVertAttrs(const PrimT&, const UT_Vector3&, IndexAccT&) const;
1178
1179 const GU_Detail& mSourceGeo;
1180 GU_Detail& mTargetGeo;
1181 const GridType& mIndexGrid;
1182 AttrCopyPtrVec& mPrimAttributes;
1183 AttrCopyPtrVec& mVertAttributes;
1184};
1185
1186
1187 template<class GridType>
1188 inline void
1189 TransferPrimitiveAttributesOp<GridType>::operator()(const GA_SplittableRange& range) const
1190{
1191 if (mPrimAttributes.empty() && mVertAttributes.empty()) return;
1192
1193 auto polyIdxAcc = mIndexGrid.getConstAccessor();
1194
1195 for (GA_PageIterator pageIt = range.beginPages(); !pageIt.atEnd(); ++pageIt) {
1196 auto start = GA_Offset(), end = GA_Offset();
1197 for (GA_Iterator blockIt(pageIt.begin()); blockIt.blockAdvance(start, end); ) {
1198 for (auto targetOffset = start; targetOffset < end; ++targetOffset) {
1199 const auto* target = mTargetGeo.getPrimitiveList().get(targetOffset);
1200 if (!target) continue;
1201
1202 const auto targetN = mTargetGeo.getGEOPrimitive(targetOffset)->computeNormal();
1203
1204 if (!mPrimAttributes.empty()) {
1205 // Transfer primitive attributes.
1206 copyPrimAttrs(*target, targetN, polyIdxAcc);
1207 }
1208
1209 if (!mVertAttributes.empty()) {
1210 if (target->getTypeId() != GA_PRIMPOLYSOUP) {
1211 copyVertAttrs(*target, targetN, polyIdxAcc);
1212 } else {
1213 if (const auto* soup = UTverify_cast<const GEO_PrimPolySoup*>(target)) {
1214 // Iterate in parallel over the member polygons of a polygon soup.
1215 using SizeRange = UT_BlockedRange<GA_Size>;
1216 const auto processPolyRange = [&](const SizeRange& range) {
1217 auto threadLocalPolyIdxAcc = mIndexGrid.getConstAccessor();
1218 for (GEO_PrimPolySoup::PolygonIterator it(*soup, range.begin());
1219 !it.atEnd() && (it.polygon() < range.end()); ++it)
1220 {
1221 copyVertAttrs(it, it.computeNormal(), threadLocalPolyIdxAcc);
1222 }
1223 };
1224 UTparallelFor(SizeRange(0, soup->getPolygonCount()), processPolyRange);
1225 }
1226 }
1227 }
1228 }
1229 }
1230 }
1231}
1232
1233
1234/// @brief Find the closest match to the target primitive from among the source primitives
1235/// and copy primitive attributes from that primitive to the target primitive.
1236/// @note This isn't a particularly useful operation when the target is a polygon soup,
1237/// because the entire soup is a single primitive, whereas the source primitives
1238/// are likely to be individual polygons.
1239 template<class GridType>
1240 inline void
1241TransferPrimitiveAttributesOp<GridType>::copyPrimAttrs(
1242 const GA_Primitive& targetPrim,
1243 const UT_Vector3& targetNormal,
1244 IndexAccT& polyIdxAcc) const
1245{
1246 const auto& transform = mIndexGrid.transform();
1247
1248 UT_Vector3 sourceN, targetN = targetNormal;
1249 const bool isPolySoup = (targetPrim.getTypeId() == GA_PRIMPOLYSOUP);
1250
1251 // Compute avg. vertex position.
1252 openvdb::Vec3d pos(0, 0, 0);
1253 int count = static_cast<int>(targetPrim.getVertexCount());
1254 for (int vtx = 0; vtx < count; ++vtx) {
1255 pos += UTvdbConvert(targetPrim.getPos3(vtx));
1256 }
1257 if (count > 1) pos /= double(count);
1258
1259 // Find closest source primitive to current avg. vertex position.
1260 const auto coord = openvdb::Coord::floor(transform.worldToIndex(pos));
1261
1262 std::vector<GA_Index> primitives, similarPrimitives;
1263 IndexT primIndex;
1264 openvdb::Coord ijk;
1265
1266 primitives.reserve(8);
1267 similarPrimitives.reserve(8);
1268 for (int d = 0; d < 8; ++d) {
1269 ijk[0] = coord[0] + (((d & 0x02) >> 1) ^ (d & 0x01));
1270 ijk[1] = coord[1] + ((d & 0x02) >> 1);
1271 ijk[2] = coord[2] + ((d & 0x04) >> 2);
1272
1273 if (polyIdxAcc.probeValue(ijk, primIndex) &&
1274 openvdb::Index32(primIndex) != openvdb::util::INVALID_IDX) {
1275
1276 GA_Offset tmpOffset = mSourceGeo.primitiveOffset(primIndex);
1277 sourceN = mSourceGeo.getGEOPrimitive(tmpOffset)->computeNormal();
1278
1279 // Skip the normal test when the target is a polygon soup, because
1280 // the entire soup is a single primitive, whose normal is unlikely
1281 // to coincide with any of the source primitives.
1282 if (isPolySoup || sourceN.dot(targetN) > 0.5) {
1283 similarPrimitives.push_back(primIndex);
1284 } else {
1285 primitives.push_back(primIndex);
1286 }
1287 }
1288 }
1289
1290 if (!primitives.empty() || !similarPrimitives.empty()) {
1291 GA_Offset source, v0, v1, v2;
1292 openvdb::Vec3d uvw;
1293 if (!similarPrimitives.empty()) {
1294 source = findClosestPrimitiveToPoint(
1295 mSourceGeo, similarPrimitives, pos, v0, v1, v2, uvw);
1296 } else {
1297 source = findClosestPrimitiveToPoint(
1298 mSourceGeo, primitives, pos, v0, v1, v2, uvw);
1299 }
1300
1301 // Transfer attributes
1302 const auto targetOffset = targetPrim.getMapOffset();
1303 for (size_t n = 0, N = mPrimAttributes.size(); n < N; ++n) {
1304 mPrimAttributes[n]->copy(source, targetOffset);
1305 }
1306 }
1307}
1308
1309
1310/// @brief Find the closest match to the target primitive from among the source primitives
1311/// (using slightly different criteria than copyPrimAttrs()) and copy vertex attributes
1312/// from that primitive's vertices to the target primitive's vertices.
1313/// @note When the target is a polygon soup, @a targetPrim should be a
1314/// @b GEO_PrimPolySoup::PolygonIterator that points to one of the member polygons of the soup.
1315 template<typename GridType>
1316 template<typename PrimT>
1317 inline void
1318TransferPrimitiveAttributesOp<GridType>::copyVertAttrs(
1319 const PrimT& targetPrim,
1320 const UT_Vector3& targetNormal,
1321 IndexAccT& polyIdxAcc) const
1322{
1323 const auto& transform = mIndexGrid.transform();
1324
1325 openvdb::Vec3d pos, uvw;
1326 openvdb::Coord ijk;
1327 UT_Vector3 sourceNormal;
1328 std::vector<GA_Index> primitives, similarPrimitives;
1329
1330 primitives.reserve(8);
1331 similarPrimitives.reserve(8);
1332 for (GA_Size vtx = 0, vtxN = targetPrim.getVertexCount(); vtx < vtxN; ++vtx) {
1333 pos = UTvdbConvert(targetPrim.getPos3(vtx));
1334 const auto coord = openvdb::Coord::floor(transform.worldToIndex(pos));
1335
1336 primitives.clear();
1337 similarPrimitives.clear();
1338 int primIndex;
1339 for (int d = 0; d < 8; ++d) {
1340 ijk[0] = coord[0] + (((d & 0x02) >> 1) ^ (d & 0x01));
1341 ijk[1] = coord[1] + ((d & 0x02) >> 1);
1342 ijk[2] = coord[2] + ((d & 0x04) >> 2);
1343
1344 if (polyIdxAcc.probeValue(ijk, primIndex) &&
1345 (openvdb::Index32(primIndex) != openvdb::util::INVALID_IDX))
1346 {
1347 GA_Offset tmpOffset = mSourceGeo.primitiveOffset(primIndex);
1348 sourceNormal = mSourceGeo.getGEOPrimitive(tmpOffset)->computeNormal();
1349 if (sourceNormal.dot(targetNormal) > 0.5) {
1350 primitives.push_back(primIndex);
1351 }
1352 }
1353 }
1354
1355 if (!primitives.empty() || !similarPrimitives.empty()) {
1356 GA_Offset v0, v1, v2;
1357 if (!similarPrimitives.empty()) {
1358 findClosestPrimitiveToPoint(mSourceGeo, similarPrimitives, pos, v0, v1, v2, uvw);
1359 } else {
1360 findClosestPrimitiveToPoint(mSourceGeo, primitives, pos, v0, v1, v2, uvw);
1361 }
1362
1363 for (size_t n = 0, N = mVertAttributes.size(); n < N; ++n) {
1364 mVertAttributes[n]->copy(v0, v1, v2, targetPrim.getVertexOffset(vtx), uvw);
1365 }
1366 }
1367 }
1368}
1369
1370
1371////////////////////////////////////////
1372
1373
1374 template<class GridType>
1375 class TransferPointAttributesOp
1376{
1377 public:
1378 TransferPointAttributesOp(
1379 const GU_Detail& sourceGeo, GU_Detail& targetGeo, const GridType& indexGrid,
1380 std::vector<AttributeCopyBase::Ptr>& pointAttributes,
1381 const GA_PrimitiveGroup* surfacePrims = nullptr);
1382
1383 void operator()(const GA_SplittableRange&) const;
1384 private:
1385 const GU_Detail& mSourceGeo;
1386 GU_Detail& mTargetGeo;
1387 const GridType& mIndexGrid;
1388 std::vector<AttributeCopyBase::Ptr>& mPointAttributes;
1389 const GA_PrimitiveGroup* mSurfacePrims;
1390};
1391
1392 template<class GridType>
1393 TransferPointAttributesOp<GridType>::TransferPointAttributesOp(
1394 const GU_Detail& sourceGeo, GU_Detail& targetGeo, const GridType& indexGrid,
1395 std::vector<AttributeCopyBase::Ptr>& pointAttributes,
1396 const GA_PrimitiveGroup* surfacePrims)
1397 : mSourceGeo(sourceGeo)
1398 , mTargetGeo(targetGeo)
1399 , mIndexGrid(indexGrid)
1400 , mPointAttributes(pointAttributes)
1401 , mSurfacePrims(surfacePrims)
1402{
1403}
1404
1405 template<class GridType>
1406 void
1407 TransferPointAttributesOp<GridType>::operator()(const GA_SplittableRange& range) const
1408{
1409 using IndexT = typename GridType::ValueType;
1410
1411 GA_Offset start, end, vtxOffset, primOffset, target, v0, v1, v2;
1412
1413 typename GridType::ConstAccessor polyIdxAcc = mIndexGrid.getConstAccessor();
1414 const openvdb::math::Transform& transform = mIndexGrid.transform();
1415 openvdb::Vec3d pos, indexPos, uvw;
1416 std::vector<GA_Index> primitives;
1417 openvdb::Coord ijk, coord;
1418
1419 primitives.reserve(8);
1420 for (GA_PageIterator pageIt = range.beginPages(); !pageIt.atEnd(); ++pageIt) {
1421 for (GA_Iterator blockIt(pageIt.begin()); blockIt.blockAdvance(start, end); ) {
1422 for (target = start; target < end; ++target) {
1423
1424
1425 vtxOffset = mTargetGeo.pointVertex(target);
1426
1427 // Check if point is referenced by a surface primitive.
1428 if (mSurfacePrims) {
1429 bool surfacePrim = false;
1430
1431 while (GAisValid(vtxOffset)) {
1432
1433 primOffset = mTargetGeo.vertexPrimitive(vtxOffset);
1434
1435 if (mSurfacePrims->containsIndex(mTargetGeo.primitiveIndex(primOffset))) {
1436 surfacePrim = true;
1437 break;
1438 }
1439
1440 vtxOffset = mTargetGeo.vertexToNextVertex(vtxOffset);
1441 }
1442
1443 if (!surfacePrim) continue;
1444 }
1445
1446 const UT_Vector3 p = mTargetGeo.getPos3(target);
1447 pos[0] = p.x();
1448 pos[1] = p.y();
1449 pos[2] = p.z();
1450
1451 indexPos = transform.worldToIndex(pos);
1452 coord[0] = int(std::floor(indexPos[0]));
1453 coord[1] = int(std::floor(indexPos[1]));
1454 coord[2] = int(std::floor(indexPos[2]));
1455
1456 primitives.clear();
1457 IndexT primIndex;
1458
1459 for (int d = 0; d < 8; ++d) {
1460 ijk[0] = coord[0] + (((d & 0x02) >> 1) ^ (d & 0x01));
1461 ijk[1] = coord[1] + ((d & 0x02) >> 1);
1462 ijk[2] = coord[2] + ((d & 0x04) >> 2);
1463
1464 if (polyIdxAcc.probeValue(ijk, primIndex) &&
1465 openvdb::Index32(primIndex) != openvdb::util::INVALID_IDX) {
1466 primitives.push_back(primIndex);
1467 }
1468 }
1469
1470 if (!primitives.empty()) {
1471 findClosestPrimitiveToPoint(mSourceGeo, primitives, pos, v0, v1, v2, uvw);
1472
1473 v0 = mSourceGeo.vertexPoint(v0);
1474 v1 = mSourceGeo.vertexPoint(v1);
1475 v2 = mSourceGeo.vertexPoint(v2);
1476
1477 for (size_t n = 0, N = mPointAttributes.size(); n < N; ++n) {
1478 mPointAttributes[n]->copy(v0, v1, v2, target, uvw);
1479 }
1480 }
1481 }
1482 }
1483 }
1484}
1485
1486
1487////////////////////////////////////////
1488
1489
1490 template<class GridType>
1491 inline void
1492 transferPrimitiveAttributes(
1493 const GU_Detail& sourceGeo,
1494 GU_Detail& targetGeo,
1495 GridType& indexGrid,
1496 openvdb::util::NullInterrupter& boss,
1497 const GA_PrimitiveGroup* primitives = nullptr)
1498{
1499 // Match public primitive attributes
1500 GA_AttributeDict::iterator it = sourceGeo.primitiveAttribs().begin(GA_SCOPE_PUBLIC);
1501
1502 if (indexGrid.activeVoxelCount() == 0) return;
1503
1504 std::vector<AttributeCopyBase::Ptr> primAttributeList;
1505
1506 // Primitive attributes
1507 for (; !it.atEnd(); ++it) {
1508 const GA_Attribute* sourceAttr = it.attrib();
1509 if (nullptr == targetGeo.findPrimitiveAttribute(it.name())) {
1510 targetGeo.addPrimAttrib(sourceAttr);
1511 }
1512 GA_Attribute* targetAttr = targetGeo.findPrimitiveAttribute(it.name());
1513
1514 if (sourceAttr && targetAttr) {
1515 AttributeCopyBase::Ptr att = createAttributeCopier(*sourceAttr, *targetAttr);
1516 if(att) primAttributeList.push_back(att);
1517 }
1518 }
1519
1520 if (boss.wasInterrupted()) return;
1521
1522 std::vector<AttributeCopyBase::Ptr> vertAttributeList;
1523
1524 it = sourceGeo.vertexAttribs().begin(GA_SCOPE_PUBLIC);
1525
1526 // Vertex attributes
1527 for (; !it.atEnd(); ++it) {
1528 const GA_Attribute* sourceAttr = it.attrib();
1529 if (nullptr == targetGeo.findVertexAttribute(it.name())) {
1530 targetGeo.addVertexAttrib(sourceAttr);
1531 }
1532 GA_Attribute* targetAttr = targetGeo.findVertexAttribute(it.name());
1533
1534 if (sourceAttr && targetAttr) {
1535 targetAttr->hardenAllPages();
1536 AttributeCopyBase::Ptr att = createAttributeCopier(*sourceAttr, *targetAttr);
1537 if(att) vertAttributeList.push_back(att);
1538 }
1539 }
1540
1541 if (!boss.wasInterrupted() && (!primAttributeList.empty() || !vertAttributeList.empty())) {
1542
1543 UTparallelFor(GA_SplittableRange(targetGeo.getPrimitiveRange(primitives)),
1544 TransferPrimitiveAttributesOp<GridType>(sourceGeo, targetGeo, indexGrid,
1545 primAttributeList, vertAttributeList));
1546 }
1547
1548 if (!boss.wasInterrupted()) {
1549 std::vector<AttributeCopyBase::Ptr> pointAttributeList;
1550 it = sourceGeo.pointAttribs().begin(GA_SCOPE_PUBLIC);
1551
1552 // Point attributes
1553 for (; !it.atEnd(); ++it) {
1554 if (std::string(it.name()) == "P") continue; // Ignore previous point positions.
1555
1556 const GA_Attribute* sourceAttr = it.attrib();
1557 if (nullptr == targetGeo.findPointAttribute(it.name())) {
1558 targetGeo.addPointAttrib(sourceAttr);
1559 }
1560 GA_Attribute* targetAttr = targetGeo.findPointAttribute(it.name());
1561
1562 if (sourceAttr && targetAttr) {
1563 AttributeCopyBase::Ptr att = createAttributeCopier(*sourceAttr, *targetAttr);
1564 if(att) pointAttributeList.push_back(att);
1565 }
1566 }
1567
1568 if (!boss.wasInterrupted() && !pointAttributeList.empty()) {
1569 UTparallelFor(GA_SplittableRange(targetGeo.getPointRange()),
1570 TransferPointAttributesOp<GridType>(sourceGeo, targetGeo, indexGrid,
1571 pointAttributeList, primitives));
1572
1573 }
1574 }
1575}
1576
1577} // namespace openvdb_houdini
1578
1579#endif // OPENVDB_HOUDINI_ATTRIBUTE_TRANSFER_UTIL_HAS_BEEN_INCLUDED
NullInterrupter.h
Proximity.h
openvdb::v13_0::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
openvdb::v13_0::math::Coord::floor
static Coord floor(const Vec3< T > &xyz)
Return the largest integer coordinates that are not greater than xyz (node centered conversion).
Definition Coord.h:57
openvdb::v13_0::math::Vec3::x
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition Vec3.h:86
openvdb::v13_0::math::Vec3::y
T & y()
Definition Vec3.h:87
openvdb::v13_0::math::Vec3::z
T & z()
Definition Vec3.h:88
openvdb::v13_0::tree::Tree::ValueOnCIter
TreeValueIteratorBase< const Tree, typename RootNodeType::ValueOnCIter > ValueOnCIter
Definition Tree.h:1049
openvdb::v13_0::tree::TreeValueIteratorBase::getValue
const ValueT & getValue() const
Return the tile or voxel value to which this iterator is currently pointing.
Definition TreeIterator.h:693
openvdb::v13_0::tree::TreeValueIteratorBase::getCoord
Coord getCoord() const
Return the global coordinates of the voxel or tile to which this iterator is currently pointing.
Definition TreeIterator.h:672
openvdb_houdini::AttributeDetailBase::set
virtual void set(const openvdb::Coord &ijk, GA_Offset offset)=0
openvdb_houdini::AttributeDetailBase::grid
virtual openvdb::GridBase::Ptr & grid()=0
openvdb_houdini::AttributeDetailBase::Ptr
std::shared_ptr< AttributeDetailBase > Ptr
Definition AttributeTransferUtil.h:387
openvdb_houdini::AttributeDetailBase::name
virtual std::string & name()=0
openvdb_houdini::AttributeDetailBase::AttributeDetailBase
AttributeDetailBase()
Definition AttributeTransferUtil.h:405
openvdb_houdini::AttributeDetailBase::set
virtual void set(const openvdb::Coord &ijk, const GA_Offset(&offsets)[3], const openvdb::Vec3d &weights)=0
openvdb_houdini::AttributeDetailBase::copy
virtual AttributeDetailBase::Ptr copy()=0
openvdb_houdini::AttributeDetailBase::AttributeDetailBase
AttributeDetailBase(const AttributeDetailBase &)=default
openvdb_houdini::AttributeDetailBase::operator=
AttributeDetailBase & operator=(const AttributeDetailBase &)=default
openvdb_houdini::AttributeDetailBase::~AttributeDetailBase
virtual ~AttributeDetailBase()=default
openvdb_houdini::AttributeDetail::ValueType
typename VDBGridType::ValueType ValueType
Definition AttributeTransferUtil.h:419
openvdb_houdini::AttributeDetail::AttributeDetail
AttributeDetail(openvdb::GridBase::Ptr grid, const GA_Attribute *attribute, const GA_AIFTuple *tupleAIF, const int tupleIndex, const bool isVector=false)
Definition AttributeTransferUtil.h:462
openvdb_houdini::AttributeDetail::grid
openvdb::GridBase::Ptr & grid() override
Definition AttributeTransferUtil.h:433
openvdb_houdini::AttributeDetail::set
void set(const openvdb::Coord &ijk, const GA_Offset(&offsets)[3], const openvdb::Vec3d &weights) override
Definition AttributeTransferUtil.h:489
openvdb_houdini::AttributeDetail::name
std::string & name() override
Definition AttributeTransferUtil.h:434
openvdb_houdini::AttributeDetail::AttributeDetail
AttributeDetail()
Definition AttributeTransferUtil.h:453
openvdb_houdini::AttributeDetail::copy
AttributeDetailBase::Ptr copy() override
Definition AttributeTransferUtil.h:514
openvdb_houdini::MeshAttrTransfer::runSerial
void runSerial()
Definition AttributeTransferUtil.h:612
openvdb_houdini::MeshAttrTransfer::runParallel
void runParallel()
Main calls.
Definition AttributeTransferUtil.h:605
openvdb_houdini::MeshAttrTransfer::~MeshAttrTransfer
~MeshAttrTransfer()
Definition AttributeTransferUtil.h:544
openvdb_houdini::MeshAttrTransfer::operator()
void operator()(IterRange &range) const
Definition AttributeTransferUtil.h:620
openvdb_houdini::MeshAttrTransfer::MeshAttrTransfer
MeshAttrTransfer(AttributeDetailList &pointAttributes, AttributeDetailList &vertexAttributes, AttributeDetailList &primitiveAttributes, const openvdb::Int32Grid &closestPrimGrid, const openvdb::math::Transform &transform, const GU_Detail &meshGdp)
Definition AttributeTransferUtil.h:562
openvdb_houdini::MeshAttrTransfer::IterRange
openvdb::tree::IteratorRange< openvdb::Int32Tree::LeafCIter > IterRange
Definition AttributeTransferUtil.h:529
openvdb_houdini::PointAttrTransfer::runSerial
void runSerial()
Definition AttributeTransferUtil.h:773
openvdb_houdini::PointAttrTransfer::runParallel
void runParallel()
Main calls.
Definition AttributeTransferUtil.h:766
openvdb_houdini::PointAttrTransfer::operator()
void operator()(IterRange &range) const
Definition AttributeTransferUtil.h:781
openvdb_houdini::PointAttrTransfer::~PointAttrTransfer
~PointAttrTransfer()
Definition AttributeTransferUtil.h:725
openvdb_houdini::PointAttrTransfer::IterRange
openvdb::tree::IteratorRange< openvdb::Int32Tree::LeafCIter > IterRange
Definition AttributeTransferUtil.h:716
openvdb_houdini::PointAttrTransfer::PointAttrTransfer
PointAttrTransfer(AttributeDetailList &pointAttributes, const openvdb::Int32Grid &closestPtnIdxGrid, const GU_Detail &ptGeop)
Definition AttributeTransferUtil.h:740
openvdb_houdini::TransferPointAttributesOp
Definition AttributeTransferUtil.h:1376
openvdb_houdini::TransferPointAttributesOp::TransferPointAttributesOp
TransferPointAttributesOp(const GU_Detail &sourceGeo, GU_Detail &targetGeo, const GridType &indexGrid, std::vector< AttributeCopyBase::Ptr > &pointAttributes, const GA_PrimitiveGroup *surfacePrims=nullptr)
Definition AttributeTransferUtil.h:1393
openvdb_houdini::TransferPointAttributesOp::operator()
void operator()(const GA_SplittableRange &) const
Definition AttributeTransferUtil.h:1407
openvdb_houdini::TransferPrimitiveAttributesOp
Definition AttributeTransferUtil.h:1151
openvdb_houdini::TransferPrimitiveAttributesOp::IndexT
typename GridType::ValueType IndexT
Definition AttributeTransferUtil.h:1153
openvdb_houdini::TransferPrimitiveAttributesOp::operator()
void operator()(const GA_SplittableRange &) const
Definition AttributeTransferUtil.h:1189
openvdb_houdini::TransferPrimitiveAttributesOp::TransferPrimitiveAttributesOp
TransferPrimitiveAttributesOp(const GU_Detail &sourceGeo, GU_Detail &targetGeo, const GridType &indexGrid, AttrCopyPtrVec &primAttributes, AttrCopyPtrVec &vertAttributes)
Definition AttributeTransferUtil.h:1157
openvdb_houdini::TransferPrimitiveAttributesOp::IndexAccT
typename GridType::ConstAccessor IndexAccT
Definition AttributeTransferUtil.h:1154
openvdb_houdini::TransferPrimitiveAttributesOp::AttrCopyPtrVec
std::vector< AttributeCopyBase::Ptr > AttrCopyPtrVec
Definition AttributeTransferUtil.h:1155
openvdb::v13_0::math::Vec3d
Vec3< double > Vec3d
Definition Vec3.h:665
openvdb::v13_0::math::Vec3i
Vec3< int32_t > Vec3i
Definition Vec3.h:662
openvdb::v13_0::math::Vec3s
Vec3< float > Vec3s
Definition Vec3.h:664
openvdb::v13_0::Int64
int64_t Int64
Definition Types.h:37
openvdb::v13_0::Index32
uint32_t Index32
Definition Types.h:32
openvdb::v13_0::Int32
int32_t Int32
Definition Types.h:36
openvdb::v13_0::Int32Grid
Grid< Int32Tree > Int32Grid
Definition openvdb.h:78
openvdb_houdini
Definition AttributeTransferUtil.h:34
openvdb_houdini::evalAttrDefault< openvdb::Int32 >
openvdb::Int32 evalAttrDefault< openvdb::Int32 >(const GA_Defaults &defaults, int idx)
Definition AttributeTransferUtil.h:223
openvdb_houdini::evalAttr< openvdb::Vec3i >
openvdb::Vec3i evalAttr< openvdb::Vec3i >(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int)
Definition AttributeTransferUtil.h:78
openvdb_houdini::AttributeDetailList
std::vector< AttributeDetailBase::Ptr > AttributeDetailList
Definition AttributeTransferUtil.h:409
openvdb_houdini::evalAttr
ValueType evalAttr(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int idx)
Definition AttributeTransferUtil.h:42
openvdb_houdini::evalAttr< openvdb::Int64 >
openvdb::Int64 evalAttr< openvdb::Int64 >(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int idx)
Definition AttributeTransferUtil.h:69
openvdb_houdini::evalAttr< float >
float evalAttr< float >(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int idx)
Definition AttributeTransferUtil.h:51
openvdb_houdini::evalAttrDefault
ValueType evalAttrDefault(const GA_Defaults &defaults, int idx)
Get an OpenVDB-specific value by evaluating GA_Default::get() with appropriate arguments.
Definition AttributeTransferUtil.h:207
openvdb_houdini::createAttributeCopier
AttributeCopyBase::Ptr createAttributeCopier(const GA_Attribute &sourceAttr, GA_Attribute &targetAttr)
Definition AttributeTransferUtil.h:939
openvdb_houdini::evalAttr< openvdb::Int32 >
openvdb::Int32 evalAttr< openvdb::Int32 >(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int idx)
Definition AttributeTransferUtil.h:60
openvdb_houdini::combine
ValueType combine(const ValueType &v0, const ValueType &v1, const ValueType &v2, const openvdb::Vec3d &w)
Combine different value types.
Definition AttributeTransferUtil.h:141
openvdb_houdini::evalAttrDefault< openvdb::Vec3s >
openvdb::Vec3s evalAttrDefault< openvdb::Vec3s >(const GA_Defaults &defaults, int)
Definition AttributeTransferUtil.h:257
openvdb_houdini::transferPrimitiveAttributes
void transferPrimitiveAttributes(const GU_Detail &sourceGeo, GU_Detail &targetGeo, GridType &indexGrid, openvdb::util::NullInterrupter &boss, const GA_PrimitiveGroup *primitives=nullptr)
Definition AttributeTransferUtil.h:1492
openvdb_houdini::evalAttr< openvdb::Vec3d >
openvdb::Vec3d evalAttr< openvdb::Vec3d >(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int)
Definition AttributeTransferUtil.h:116
openvdb_houdini::findClosestPrimitiveToPoint
GA_Offset findClosestPrimitiveToPoint(const GU_Detail &geo, const std::set< GA_Index > &primitives, const openvdb::Vec3d &p, GA_Offset &vert0, GA_Offset &vert1, GA_Offset &vert2, openvdb::Vec3d &uvw)
Definition AttributeTransferUtil.h:991
openvdb_houdini::evalAttrDefault< openvdb::Vec3d >
openvdb::Vec3d evalAttrDefault< openvdb::Vec3d >(const GA_Defaults &defaults, int)
Definition AttributeTransferUtil.h:275
openvdb_houdini::evalAttr< openvdb::Vec3s >
openvdb::Vec3s evalAttr< openvdb::Vec3s >(const GA_Attribute *atr, const GA_AIFTuple *aif, GA_Offset off, int)
Definition AttributeTransferUtil.h:97
openvdb_houdini::evalAttrDefault< openvdb::Vec3i >
openvdb::Vec3i evalAttrDefault< openvdb::Vec3i >(const GA_Defaults &defaults, int)
Definition AttributeTransferUtil.h:239
openvdb_houdini::evalAttrDefault< float >
float evalAttrDefault< float >(const GA_Defaults &defaults, int)
Definition AttributeTransferUtil.h:215
openvdb_houdini::evalAttrDefault< openvdb::Int64 >
openvdb::Int64 evalAttrDefault< openvdb::Int64 >(const GA_Defaults &defaults, int idx)
Definition AttributeTransferUtil.h:231
openvdb
Definition Exceptions.h:13
Util.h
openvdb.h
Utils.h
Utility classes and functions for OpenVDB plugins.
openvdb_houdini::AttributeCopyBase::~AttributeCopyBase
virtual ~AttributeCopyBase()
Definition AttributeTransferUtil.h:813
openvdb_houdini::AttributeCopyBase::Ptr
std::shared_ptr< AttributeCopyBase > Ptr
Definition AttributeTransferUtil.h:811
openvdb_houdini::AttributeCopyBase::copy
virtual void copy(GA_Offset, GA_Offset)=0
openvdb_houdini::AttributeCopyBase::copy
virtual void copy(GA_Offset &, GA_Offset &, GA_Offset &, GA_Offset, const openvdb::Vec3d &)=0
openvdb_houdini::AttributeCopyBase::AttributeCopyBase
AttributeCopyBase()
Definition AttributeTransferUtil.h:818
openvdb_houdini::AttributeCopy
Definition AttributeTransferUtil.h:824
openvdb_houdini::AttributeCopy::copy
void copy(GA_Offset source, GA_Offset target) override
Definition AttributeTransferUtil.h:834
openvdb_houdini::AttributeCopy::copy
void copy(GA_Offset &v0, GA_Offset &v1, GA_Offset &v2, GA_Offset target, const openvdb::Vec3d &uvw) override
Definition AttributeTransferUtil.h:843
openvdb_houdini::AttributeCopy::AttributeCopy
AttributeCopy(const GA_Attribute &sourceAttr, GA_Attribute &targetAttr)
Definition AttributeTransferUtil.h:826
openvdb_houdini::StrAttributeCopy
Definition AttributeTransferUtil.h:893
openvdb_houdini::StrAttributeCopy::copy
void copy(GA_Offset source, GA_Offset target) override
Definition AttributeTransferUtil.h:903
openvdb_houdini::StrAttributeCopy::mSourceAttr
const GA_Attribute & mSourceAttr
Definition AttributeTransferUtil.h:928
openvdb_houdini::StrAttributeCopy::copy
void copy(GA_Offset &v0, GA_Offset &v1, GA_Offset &v2, GA_Offset target, const openvdb::Vec3d &uvw) override
Definition AttributeTransferUtil.h:910
openvdb_houdini::StrAttributeCopy::mAIF
const GA_AIFSharedStringTuple & mAIF
Definition AttributeTransferUtil.h:930
openvdb_houdini::StrAttributeCopy::mTupleSize
int mTupleSize
Definition AttributeTransferUtil.h:931
openvdb_houdini::StrAttributeCopy::StrAttributeCopy
StrAttributeCopy(const GA_Attribute &sourceAttr, GA_Attribute &targetAttr)
Definition AttributeTransferUtil.h:895
openvdb_houdini::StrAttributeCopy::mTargetAttr
GA_Attribute & mTargetAttr
Definition AttributeTransferUtil.h:929