HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
meshGeneratorBase.h
Go to the documentation of this file.
1 //
2 // Copyright 2022 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H
8 #define PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H
9 
11 
12 #include "pxr/base/arch/math.h"
13 #include "pxr/base/gf/math.h"
14 #include "pxr/base/gf/matrix4d.h"
15 #include "pxr/base/gf/vec3d.h"
16 #include "pxr/base/gf/vec3f.h"
17 
18 #include "pxr/pxr.h"
19 
20 #include <iterator>
21 #include <type_traits>
22 
24 
25 class PxOsdMeshTopology;
26 
27 /// This class provides common implementation for the different mesh generator
28 /// classes in GeomUtil. As the mesh generators are entirely implemented as
29 /// static functions, this "base class" is more of a grouping and access control
30 /// mechanism than a base class in the polymorphic sense.
31 ///
32 /// The mesh generator subclasses all follow a common pattern, providing static
33 /// methods for generating topology and point positions for their specific
34 /// geometric primitive. The data produced by these classes is only guaranteed
35 /// to be suitable for imaging the described surface; it is only one of many
36 /// possible interpretations of the surface, and should not be relied upon for
37 /// any other use. The generators may e.g. change the topology or ordering of
38 /// the produced data at any time. In short: these utilities are meant only to
39 /// be used to produce a blob of semi-blind data, for feeding to an imager that
40 /// supports PxOsdMeshTopology.
41 ///
42 /// The generators make use of templates and SFINAE to allow clients to pass any
43 /// output iterator that dereferences to either a GfVec3f or GfVec3d to their
44 /// GeneratePoints(...) method, and internally perform compile-time type erasure
45 /// in order to allow the implementations of their algorithms to be private
46 /// implementation detail, not defined in the headers. Although it's expected
47 /// that clients will typically want their point data in VtVec3fArray, the
48 /// described implementation was chosen to minimize the chance that any
49 /// prospective client with unusual data management requirements would be unable
50 /// to make use of the generators, or would be forced to resort to a container
51 /// copy in order to do so.
52 ///
53 /// The only public API on this class is a static GeneratePoints(...) template
54 /// method, intended to be added by subclasses to their GeneratePoints(...)
55 /// overload sets. It serves as the "error case" for callers that attempt to
56 /// pass iterators of unsupported types to the GeneratePoints(...) methods each
57 /// subclass declares. As all generator subclasses have this possibility and
58 /// the implementation requires SFINAE, it's implemented here and shared between
59 /// all subclasses. However, it's important that subclasses explicitly include
60 /// a "using" statement for the fallback to be included in overload resolution.
61 ///
63 {
64 private:
65 
66  // Delete the implicit default c'tor. This class and its subclasses are
67  // only for grouping; there's never any need to make instances.
68  GeomUtilMeshGeneratorBase() = delete;
69 
70 protected:
71 
72  // SFINAE helper types, for more compact and readable template shenanigans
73  // in the subclasses.
74  template<typename IterType>
76  {
78  static constexpr bool value =
81  };
82 
83  template<typename IterType>
85  : public std::enable_if<_IsGfVec3Iterator<IterType>::value, void>
86  {};
87 
88  template<typename IterType>
90  : public std::enable_if<!_IsGfVec3Iterator<IterType>::value, void>
91  {};
92 
93  // Helper struct to provide iterator type erasure, allowing subclasses to
94  // implement their GeneratePoints and GenerateNormals methods privately.
95  // Usage doesn't require any heap allocation or virtual dispatch/runtime
96  // typing. In addition to erasing the iterator type, this also provides a
97  // convenient way to allow subclasses to offer GeneratePoints and
98  // GenerateNormals methods that can apply an additional frame transform
99  // without having to actually plumb that detail into the guts of their point
100  // generator code.
101  //
102  // Note: Ensuring the interoperability of the PointType with the IterType
103  // used at construction is the responsibility of the client. It's typically
104  // guaranteed by the client deriving PointType from IterType; see subclass
105  // use for examples and how they guarantee IterType dereferences to a
106  // supportable point type.
107  template<typename PointType>
109  {
110  template<class IterType>
112  IterType& iter)
113  : _writeFnPtr(&_PointWriter<PointType>::_WritePoint<IterType>)
114  , _writeDirFnPtr(&_PointWriter<PointType>::_WriteDir<IterType>)
115  , _untypedIterPtr(static_cast<void*>(&iter))
116  {}
117 
118  template<class IterType>
120  IterType& iter,
121  const GfMatrix4d* const framePtr)
122  : _writeFnPtr(
123  &_PointWriter<PointType>::_TransformAndWritePoint<IterType>)
124  , _writeDirFnPtr(
125  &_PointWriter<PointType>::_TransformAndWriteDir<IterType>)
126  , _untypedIterPtr(static_cast<void*>(&iter))
127  , _framePtr(framePtr)
128  {}
129 
130  void Write(
131  const PointType& pt) const
132  {
133  (this->*_writeFnPtr)(pt);
134  }
135 
136  void WriteArc(
137  const typename PointType::ScalarType scaleXY,
138  const std::vector<std::array<
139  typename PointType::ScalarType, 2>>& arcXY,
140  const typename PointType::ScalarType arcZ) const
141  {
142  for (const auto& xy : arcXY) {
143  Write(PointType(scaleXY * xy[0], scaleXY * xy[1], arcZ));
144  }
145  }
146 
147  void WriteDir(
148  const PointType& dir) const
149  {
150  (this->*_writeDirFnPtr)(dir);
151  }
152 
154  const typename PointType::ScalarType scaleXY,
155  const std::vector<std::array<
156  typename PointType::ScalarType, 2>>& arcXY,
157  const typename PointType::ScalarType arcZ) const
158  {
159  for (const auto& xy : arcXY) {
160  WriteDir(PointType(scaleXY * xy[0], scaleXY * xy[1], arcZ));
161  }
162  }
163 
164  private:
165  template<class IterType>
166  void _WritePoint(
167  const PointType& pt) const
168  {
169  IterType& iter = *static_cast<IterType*>(_untypedIterPtr);
170  *iter = pt;
171  ++iter;
172  }
173 
174  template<class IterType>
175  void _TransformAndWritePoint(
176  const PointType& pt) const
177  {
178  IterType& iter = *static_cast<IterType*>(_untypedIterPtr);
179  using OutType = typename std::remove_reference_t<decltype(*iter)>;
180  *iter = static_cast<OutType>(_framePtr->Transform(pt));
181  ++iter;
182  }
183 
184  template<class IterType>
185  void _WriteDir(
186  const PointType& pt) const
187  {
188  IterType& iter = *static_cast<IterType*>(_untypedIterPtr);
189  *iter = pt;
190  ++iter;
191  }
192 
193  template<class IterType>
194  void _TransformAndWriteDir(
195  const PointType& dir) const
196  {
197  IterType& iter = *static_cast<IterType*>(_untypedIterPtr);
198  using OutType = typename std::remove_reference_t<decltype(*iter)>;
199  *iter = static_cast<OutType>(_framePtr->TransformDir(dir));
200  ++iter;
201  }
202 
203  using _WriteFnPtr =
204  void (_PointWriter<PointType>::*)(const PointType &) const;
205  _WriteFnPtr _writeFnPtr;
206  _WriteFnPtr _writeDirFnPtr;
207  void* _untypedIterPtr;
208  const GfMatrix4d* _framePtr;
209  };
210 
211  // Common topology helper method.
212  //
213  // Several of the subclasses make use of a common topology, specifically "a
214  // triangle fan around a 'bottom' point, some number of quad strips forming
215  // rings with shared edges, and another triangle fan surrounding a 'top'
216  // point." The two triangle fans can be considered "caps" on a "tube" of
217  // linked quad strips. This triangle fans + quad strips topology also
218  // describes the latitude/longitude topology of the globe, as another
219  // example.
220  //
221  // Because we currently rely on downstream machinery to infer surface
222  // normals from the topology, we sometimes want the "caps" to share their
223  // edge-ring with the adjacent quad strip, and other times need that edge-
224  // ring to not be shared between the "cap" and "body" surfaces. The edges
225  // are coincident in space but the surface is not continuous across that
226  // edge.
227  //
228  // Subclasses specify the "cap" conditions they require to support the
229  // surface-continuity condition described above, and other uses where a
230  // "cap" is not needed (e.g. the point-end of a cone).
231  //
232  // Subclasses also specify whether the surface is closed or open. This
233  // is typically exposed via a sweep parameter, wherein a sweep of a multiple
234  // of 2 * pi results in a "closed" surface. The generated points and by
235  // extension, the generated topology, differs for "open" and "closed"
236  // surfaces.
237  //
238  enum _CapStyle {
242  };
243 
245  const size_t numRadial,
246  const size_t numQuadStrips,
247  const _CapStyle bottomCapStyle,
248  const _CapStyle topCapStyle,
249  const bool closedSweep);
250 
251 
252  // Subclasses that use the topology helper method above generate one or more
253  // circular arcs during point generation. The number of radial points on
254  // each arc depends on the number of radial segments and whether the arc
255  // is fully swept (i.e., a ring).
256  static size_t _ComputeNumRadialPoints(
257  const size_t numRadial,
258  const bool closedSweep);
259 
260  // Subclasses that use the topology helper method above must generate points
261  // forming circular arcs and this method will compute the total number of
262  // points required for the topology generated using these same parameters.
264  const size_t numRadial,
265  const size_t numQuadStrips,
266  const _CapStyle bottomCapStyle,
267  const _CapStyle topCapStyle,
268  const bool closedSweep);
269 
270  // Subclasses can use this helper method to generate a unit circular arc
271  // in the XY plane that can then be passed into _PointWriter::WriteArc to
272  // write out the points of circular arcs using varying radii.
273  template<typename ScalarType>
274  static std::vector<std::array<ScalarType, 2>> _GenerateUnitArcXY(
275  const size_t numRadial,
276  const ScalarType sweepDegrees)
277  {
278  constexpr ScalarType twoPi = 2.0 * M_PI;
279  const ScalarType sweepRadians = GfDegreesToRadians(sweepDegrees);
280  const ScalarType sweep = GfClamp(sweepRadians, -twoPi, twoPi);
281  const bool closedSweep = GfIsClose(GfAbs(sweep), twoPi, 1e-6);
282  const size_t numPts = _ComputeNumRadialPoints(numRadial, closedSweep);
283 
284  // Construct a circular arc of unit radius in the XY plane.
285  std::vector<std::array<ScalarType, 2>> result(numPts);
286  for (size_t radIdx = 0; radIdx < numPts; ++radIdx) {
287  // Longitude range: [0, sweep]
288  const ScalarType longAngle =
289  (ScalarType(radIdx) / ScalarType(numRadial)) * sweep;
290  result[radIdx][0] = cos(longAngle);
291  result[radIdx][1] = sin(longAngle);
292  }
293  return result;
294  }
295 
296 public:
297 
298  // This template provides a "fallback" for GeneratePoints(...) calls that
299  // do not meet the SFINAE requirement that the given point-container-
300  // iterator must dereference to a GfVec3f or GfVec3d. This version
301  // generates a helpful compile time assertion in such a scenario. As noted
302  // earlier, subclasses should explicitly add a "using" statement with
303  // this method to include it in overload resolution.
304  //
305  template<typename PointIterType,
306  typename Enabled =
308  static void GeneratePoints(
309  PointIterType iter, ...)
310  {
312  "This function only supports iterators to GfVec3f or GfVec3d "
313  "objects.");
314  }
315 
316  // This template provides a "fallback" for GenerateNormals(...) calls that
317  // do not meet the SFINAE requirement that the given point-container-
318  // iterator must dereference to a GfVec3f or GfVec3d. This version
319  // generates a helpful compile time assertion in such a scenario. As noted
320  // earlier, subclasses should explicitly add a "using" statement with
321  // this method to include it in overload resolution.
322  //
323  template<typename PointIterType,
324  typename Enabled =
326  static void GenerateNormals(
327  PointIterType iter, ...)
328  {
330  "This function only supports iterators to GfVec3f or GfVec3d "
331  "objects.");
332  }
333 
334 };
335 
336 
338 
339 #endif // PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H
type
Definition: core.h:556
SYS_API double cos(double x)
void WriteDir(const PointType &dir) const
double GfClamp(double value, double min, double max)
Definition: math.h:263
void
Definition: png.h:1083
#define M_PI
Definition: fmath.h:98
GLsizei const GLfloat * value
Definition: glcorearb.h:824
**But if you need a result
Definition: thread.h:622
GfVec3d Transform(const GfVec3d &vec) const
Definition: matrix4d.h:642
GfVec3d TransformDir(const GfVec3d &vec) const
Definition: matrix4d.h:654
double GfAbs(double f)
Definition: math.h:222
uint64 value_type
Definition: GA_PrimCompat.h:29
_PointWriter(IterType &iter, const GfMatrix4d *const framePtr)
void Write(const PointType &pt) const
typename std::iterator_traits< IterType >::value_type PointType
void WriteArcDir(const typename PointType::ScalarType scaleXY, const std::vector< std::array< typename PointType::ScalarType, 2 >> &arcXY, const typename PointType::ScalarType arcZ) const
static PxOsdMeshTopology _GenerateCappedQuadTopology(const size_t numRadial, const size_t numQuadStrips, const _CapStyle bottomCapStyle, const _CapStyle topCapStyle, const bool closedSweep)
static size_t _ComputeNumCappedQuadTopologyPoints(const size_t numRadial, const size_t numQuadStrips, const _CapStyle bottomCapStyle, const _CapStyle topCapStyle, const bool closedSweep)
double GfDegreesToRadians(double degrees)
Definition: math.h:37
void WriteArc(const typename PointType::ScalarType scaleXY, const std::vector< std::array< typename PointType::ScalarType, 2 >> &arcXY, const typename PointType::ScalarType arcZ) const
static void GeneratePoints(PointIterType iter,...)
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
bool GfIsClose(GfColor const &c1, GfColor const &c2, double tolerance)
Definition: color.h:114
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
static size_t _ComputeNumRadialPoints(const size_t numRadial, const bool closedSweep)
static std::vector< std::array< ScalarType, 2 > > _GenerateUnitArcXY(const size_t numRadial, const ScalarType sweepDegrees)
SYS_API double sin(double x)
static void GenerateNormals(PointIterType iter,...)