Loading...
Searching...
No Matches
meshGeneratorBase.h
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
10#include "pxr/imaging/geomUtil/api.h"
11
12#include "pxr/base/arch/math.h"
13#include "pxr/base/gf/math.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
23PXR_NAMESPACE_OPEN_SCOPE
24
26
63{
64private:
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.
69
70protected:
71
72 // SFINAE helper types, for more compact and readable template shenanigans
73 // in the subclasses.
74 template<typename IterType>
75 struct _IsGfVec3Iterator
76 {
77 using PointType = typename std::iterator_traits<IterType>::value_type;
78 static constexpr bool value =
79 std::is_same<PointType, GfVec3f>::value ||
80 std::is_same<PointType, GfVec3d>::value;
81 };
82
83 template<typename IterType>
84 struct _EnableIfGfVec3Iterator
85 : public std::enable_if<_IsGfVec3Iterator<IterType>::value, void>
86 {};
87
88 template<typename IterType>
89 struct _EnableIfNotGfVec3Iterator
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>
108 struct _PointWriter
109 {
110 template<class IterType>
111 _PointWriter(
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>
119 _PointWriter(
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
153 void WriteArcDir(
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 {
239 CapStyleNone,
240 CapStyleSharedEdge,
241 CapStyleSeparateEdge
242 };
243
244 static PxOsdMeshTopology _GenerateCappedQuadTopology(
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.
263 static size_t _ComputeNumCappedQuadTopologyPoints(
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
296public:
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 =
307 typename _EnableIfNotGfVec3Iterator<PointIterType>::type>
308 static void GeneratePoints(
309 PointIterType iter, ...)
310 {
311 static_assert(_IsGfVec3Iterator<PointIterType>::value,
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 =
325 typename _EnableIfNotGfVec3Iterator<PointIterType>::type>
326 static void GenerateNormals(
327 PointIterType iter, ...)
328 {
329 static_assert(_IsGfVec3Iterator<PointIterType>::value,
330 "This function only supports iterators to GfVec3f or GfVec3d "
331 "objects.");
332 }
333
334};
335
336
337PXR_NAMESPACE_CLOSE_SCOPE
338
339#endif // PXR_IMAGING_GEOM_UTIL_MESH_GENERATOR_BASE_H
Architecture-specific math function calls.
This class provides common implementation for the different mesh generator classes in GeomUtil.
Stores a 4x4 matrix of double elements.
Definition: matrix4d.h:71
Topology data for meshes.
Definition: meshTopology.h:52
bool GfIsClose(GfColor const &c1, GfColor const &c2, double tolerance)
Tests for equality of the RGB tuple in a color with a given tolerance, returning true if the length o...
Definition: color.h:114
Assorted mathematical utility functions.
double GfDegreesToRadians(double degrees)
Converts an angle in degrees to radians.
Definition: math.h:37
double GfClamp(double value, double min, double max)
Return the resulting of clamping value to lie between min and max.
Definition: math.h:263
double GfAbs(double f)
Return abs(f).
Definition: math.h:222