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