1 //
2 // Copyright 2014 DreamWorks Animation LLC.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef OPENSUBDIV3_SDC_CREASE_H
25 #define OPENSUBDIV3_SDC_CREASE_H
26
27 #include "../version.h"
28
29 #include "../sdc/options.h"
30
31 namespace OpenSubdiv {
32 namespace OPENSUBDIV_VERSION {
33
34 namespace Sdc {
35
36 ///
37 /// \brief Types, constants and utilities related to semi-sharp creasing -- whose implementation
38 /// is independent of the subdivision scheme.
39 ///
40 /// Crease is intended to be a light-weight, trivially constructed class that computes
41 /// crease-related properties -- typically sharpness values and associated interpolation
42 /// weights. An instance of Crease is defined with a set of options that include current
43 /// and future variations that will impact computations involving sharpness values.
44 ///
45 /// The Crease methods do not use topological neighborhoods as input. The methods here
46 /// rely more on the sharpness values and less on the topology, so we choose to work directly
47 /// with the sharpness values. We also follow the trend of using primitive arrays in the
48 /// interface to encourage local gathering for re-use.
49 ///
50 /// Note on the need for and use of sharpness values:
51 /// In general, mask queries rely on the sharpness values. The common case of a smooth
52 /// vertex, when known, avoids the need to inspect them, but unless the rules are well understood,
53 /// users will be expected to provided them -- particularly when they expect the mask queries
54 /// to do all of the work (just determining if a vertex is smooth will require inspection of
55 /// incident edge sharpness).
56 /// Mask queries will occasionally require the subdivided sharpness values around the
57 /// child vertex. So users will be expected to either provide them up front when known, or to be
58 /// gathered on demand. Any implementation of subdivision with creasing cannot avoid subdividing
59 /// the sharpness values first, so keeping them available for re-use is a worthwhile consideration.
60 ///
61
62 class Crease {
63 public:
64 //@{
65 /// Constants and related queries of sharpness values:
66 ///
67 static float const SHARPNESS_SMOOTH; // = 0.0f, do we really need this?
68 static float const SHARPNESS_INFINITE; // = 10.0f;
69
IsSmooth(float sharpness)70 static bool IsSmooth(float sharpness) { return sharpness <= SHARPNESS_SMOOTH; }
IsSharp(float sharpness)71 static bool IsSharp(float sharpness) { return sharpness > SHARPNESS_SMOOTH; }
IsInfinite(float sharpness)72 static bool IsInfinite(float sharpness) { return sharpness >= SHARPNESS_INFINITE; }
IsSemiSharp(float sharpness)73 static bool IsSemiSharp(float sharpness) { return (SHARPNESS_SMOOTH < sharpness) && (sharpness < SHARPNESS_INFINITE); }
74 //@}
75
76 ///
77 /// Enum for the types of subdivision rules applied based on sharpness values (note these
78 /// correspond to Hbr's vertex "mask"). The values are assigned to bit positions as it is
79 /// useful to use bitwise operations to inspect collections of vertices (i.e. all of the
80 /// vertices incident a particular face).
81 ///
82 enum Rule {
83 RULE_UNKNOWN = 0,
84 RULE_SMOOTH = (1 << 0),
85 RULE_DART = (1 << 1),
86 RULE_CREASE = (1 << 2),
87 RULE_CORNER = (1 << 3)
88 };
89
90 public:
Crease()91 Crease() : _options() { }
Crease(Options const & options)92 Crease(Options const& options) : _options(options) { }
~Crease()93 ~Crease() { }
94
IsUniform()95 bool IsUniform() const { return _options.GetCreasingMethod() == Options::CREASE_UNIFORM; }
96
97 //@{
98 /// Optional sharp features:
99 /// Since options treat certain topological features as infinitely sharp -- boundaries
100 /// or (in future) non-manifold features -- sharpness values should be adjusted before use.
101 /// The following methods will adjust (by return) specific values according to the options
102 /// applied.
103 ///
104 float SharpenBoundaryEdge(float edgeSharpness) const;
105 float SharpenBoundaryVertex(float edgeSharpness) const;
106
107 // For future consideration
108 //float SharpenNonManifoldEdge(float edgeSharpness) const;
109 //float SharpenNonManifoldVertex(float edgeSharpness) const;
110 //@}
111
112 //@{
113 /// Sharpness subdivision:
114 /// The computation of a Uniform subdivided sharpness value is as follows:
115 /// - Smooth edges or verts stay Smooth
116 /// - Sharp edges or verts stay Sharp
117 /// - semi-sharp edges or verts are decremented by 1.0
118 /// but for Chaikin (and potentially future non-uniform schemes that improve upon it) the
119 /// computation is more involved. In the case of edges in particular, the sharpness of a
120 /// child edge is determined by the sharpness in the neighborhood of the end vertex
121 /// corresponding to the child. For this reason, an alternative to subdividing sharpness
122 /// that computes all child edges around a vertex is given.
123 ///
124 float SubdivideUniformSharpness(float vertexOrEdgeSharpness) const;
125
126 float SubdivideVertexSharpness(float vertexSharpness) const;
127
128 float SubdivideEdgeSharpnessAtVertex(float edgeSharpness,
129 int incidentEdgeCountAtEndVertex,
130 float const* edgeSharpnessAroundEndVertex) const;
131
132 void SubdivideEdgeSharpnessesAroundVertex(int incidentEdgeCountAtVertex,
133 float const* incidentEdgeSharpnessAroundVertex,
134 float* childEdgesSharpnessAroundVertex) const;
135 //@}
136
137 //@{
138 /// Rule determination:
139 /// Mask queries do not require the Rule to be known, it can be determined from
140 /// the information provided, but it is generally more efficient when the Rule is known
141 /// and provided. In particular, the Smooth case dominates and is known to be applicable
142 /// based on the origin of the vertex without inspection of sharpness.
143 ///
144 Rule DetermineVertexVertexRule(float vertexSharpness,
145 int incidentEdgeCount,
146 float const* incidentEdgeSharpness) const;
147 Rule DetermineVertexVertexRule(float vertexSharpness,
148 int sharpEdgeCount) const;
149 //@}
150
151 /// \brief Transitional weighting:
152 /// When the rules applicable to a parent vertex and its child differ, one or more
153 /// sharpness values has "decayed" to zero. Both rules are then applicable and blended
154 /// by a weight between 0 and 1 that reflects the transition. Most often this will be
155 /// a single sharpness value that decays from within the interval [0,1] to zero -- and
156 /// the weight to apply is exactly that sharpness value -- but more than one may decay,
157 /// and values > 1 may also decay to 0 in a single step while others within [0,1] may
158 /// remain > 0.
159 /// So to properly determine a transitional weight, sharpness values for both the
160 /// parent and child must be inspected, combined and clamped accordingly.
161 ///
162 float ComputeFractionalWeightAtVertex(float vertexSharpness,
163 float childVertexSharpness,
164 int incidentEdgeCount,
165 float const* incidentEdgeSharpness,
166 float const* childEdgesSharpness) const;
167
168 void GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness,
169 int incidentEdgeCount,
170 int sharpEdgePair[2]) const;
171
172 // Would these really help? Maybe only need Rules for the vertex-vertex case...
173 //
174 // Rule DetermineEdgeVertexRule(float parentEdgeSharpness) const;
175 // Rule DetermineEdgeVertexRule(float childEdge1Sharpness, float childEdge2Sharpness) const;
176
177 protected:
178 float decrementSharpness(float sharpness) const;
179
180 private:
181 Options _options;
182 };
183
184
185 //
186 // Inline declarations:
187 //
188 inline float
SharpenBoundaryEdge(float)189 Crease::SharpenBoundaryEdge(float /* edgeSharpness */) const {
190
191 //
192 // Despite the presence of the BOUNDARY_NONE option, boundary edges are always sharpened.
193 // Much of the code relies on sharpness to indicate boundaries to avoid the more complex
194 // topological inspection
195 //
196 return SHARPNESS_INFINITE;
197 }
198
199 inline float
SharpenBoundaryVertex(float vertexSharpness)200 Crease::SharpenBoundaryVertex(float vertexSharpness) const {
201
202 return (_options.GetVtxBoundaryInterpolation() == Options::VTX_BOUNDARY_EDGE_AND_CORNER) ?
203 SHARPNESS_INFINITE : vertexSharpness;
204 }
205
206 inline float
decrementSharpness(float sharpness)207 Crease::decrementSharpness(float sharpness) const {
208
209 if (IsSmooth(sharpness)) return Crease::SHARPNESS_SMOOTH; // redundant but most common
210 if (IsInfinite(sharpness)) return Crease::SHARPNESS_INFINITE;
211 if (sharpness > 1.0f) return (sharpness - 1.0f);
212 return Crease::SHARPNESS_SMOOTH;
213 }
214
215 inline float
SubdivideUniformSharpness(float vertexOrEdgeSharpness)216 Crease::SubdivideUniformSharpness(float vertexOrEdgeSharpness) const {
217
218 return decrementSharpness(vertexOrEdgeSharpness);
219 }
220
221 inline float
SubdivideVertexSharpness(float vertexSharpness)222 Crease::SubdivideVertexSharpness(float vertexSharpness) const {
223
224 return decrementSharpness(vertexSharpness);
225 }
226
227 inline void
GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness,int incidentEdgeCount,int sharpEdgePair[2])228 Crease::GetSharpEdgePairOfCrease(float const * incidentEdgeSharpness, int incidentEdgeCount,
229 int sharpEdgePair[2]) const {
230
231 // Only to be called when a crease is present at a vertex -- exactly two sharp
232 // edges are expected here:
233 //
234 sharpEdgePair[0] = 0;
235 while (IsSmooth(incidentEdgeSharpness[sharpEdgePair[0]])) ++ sharpEdgePair[0];
236
237 sharpEdgePair[1] = incidentEdgeCount - 1;
238 while (IsSmooth(incidentEdgeSharpness[sharpEdgePair[1]])) -- sharpEdgePair[1];
239 }
240
241 } // end namespace sdc
242
243 } // end namespace OPENSUBDIV_VERSION
244 using namespace OPENSUBDIV_VERSION;
245 } // end namespace OpenSubdiv
246
247 #endif /* OPENSUBDIV3_SDC_CREASE_H */
248