1 //
2 //   Copyright 2013 Pixar
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 
25 
26 //------------------------------------------------------------------------------
27 // Tutorial description:
28 //
29 // This tutorial shows how to interpolate surface limits at arbitrary
30 // parametric locations using feature adaptive Far::PatchTables.
31 //
32 // The evaluation of the limit surface at arbitrary locations requires the
33 // adaptive isolation of topological features. This process converts the
34 // input polygonal control cage into a collection of bi-cubic patches.
35 //
36 // We can then evaluate the patches at random parametric locations and
37 // obtain analytical positions and tangents on the limit surface.
38 //
39 // The results are dumped into a MEL script that draws 'streak' particle
40 // systems that show the tangent and bi-tangent at the random samples locations.
41 //
42 
43 #include <opensubdiv/far/topologyDescriptor.h>
44 #include <opensubdiv/far/primvarRefiner.h>
45 #include <opensubdiv/far/patchTableFactory.h>
46 #include <opensubdiv/far/patchMap.h>
47 #include <opensubdiv/far/ptexIndices.h>
48 
49 #include <cassert>
50 #include <cstdio>
51 #include <cstring>
52 #include <cfloat>
53 
54 using namespace OpenSubdiv;
55 
56 typedef double Real;
57 
58 // pyramid geometry from catmark_pyramid_crease0.h
59 static int const g_nverts = 5;
60 static Real  const g_verts[24] = { 0.0f,  0.0f, 2.0f,
61                                    0.0f, -2.0f, 0.0f,
62                                    2.0f,  0.0f, 0.0f,
63                                    0.0f,  2.0f, 0.0f,
64                                   -2.0f,  0.0f, 0.0f, };
65 
66 
67 static int const g_vertsperface[5] = { 3, 3, 3, 3, 4 };
68 
69 static int const g_nfaces = 5;
70 static int const g_faceverts[16] = { 0, 1, 2,
71                                      0, 2, 3,
72                                      0, 3, 4,
73                                      0, 4, 1,
74                                      4, 3, 2, 1 };
75 
76 static int const g_ncreases = 4;
77 static int const g_creaseverts[8] = { 4, 3, 3, 2, 2, 1, 1, 4 };
78 static float const g_creaseweights[4] = { 3.0f, 3.0f, 3.0f, 3.0f };
79 
80 // Creates a Far::TopologyRefiner from the pyramid shape above
81 static Far::TopologyRefiner * createTopologyRefiner();
82 
83 //------------------------------------------------------------------------------
84 // Vertex container implementation.
85 //
86 struct Vertex {
87 
88     // Minimal required interface ----------------------
VertexVertex89     Vertex() { }
90 
ClearVertex91     void Clear( void * =0 ) {
92          point[0] = point[1] = point[2] = 0.0f;
93     }
94 
AddWithWeightVertex95     void AddWithWeight(Vertex const & src, Real weight) {
96         point[0] += weight * src.point[0];
97         point[1] += weight * src.point[1];
98         point[2] += weight * src.point[2];
99     }
100 
101     Real point[3];
102 };
103 
104 //------------------------------------------------------------------------------
105 // Limit frame container implementation -- this interface is not strictly
106 // required but follows a similar pattern to Vertex.
107 //
108 struct LimitFrame {
109 
ClearLimitFrame110     void Clear( void * =0 ) {
111          point[0] =  point[1] =  point[2] = 0.0f;
112         deriv1[0] = deriv1[1] = deriv1[2] = 0.0f;
113         deriv2[0] = deriv2[1] = deriv2[2] = 0.0f;
114     }
115 
AddWithWeightLimitFrame116     void AddWithWeight(Vertex const & src,
117         Real weight, Real d1Weight, Real d2Weight) {
118 
119         point[0] += weight * src.point[0];
120         point[1] += weight * src.point[1];
121         point[2] += weight * src.point[2];
122 
123         deriv1[0] += d1Weight * src.point[0];
124         deriv1[1] += d1Weight * src.point[1];
125         deriv1[2] += d1Weight * src.point[2];
126 
127         deriv2[0] += d2Weight * src.point[0];
128         deriv2[1] += d2Weight * src.point[1];
129         deriv2[2] += d2Weight * src.point[2];
130     }
131 
132     Real point[3],
133          deriv1[3],
134          deriv2[3];
135 };
136 
137 //------------------------------------------------------------------------------
main(int,char **)138 int main(int, char **) {
139 
140     // Generate a Far::TopologyRefiner (see tutorial_1_1 for details).
141     Far::TopologyRefiner * refiner = createTopologyRefiner();
142 
143     // Patches are constructed from adaptively refined faces, but the processes
144     // of constructing the PatchTable and of applying adaptive refinement have
145     // historically been separate.  Adaptive refinement is applied purely to
146     // satisfy the needs of the desired PatchTable, so options associated with
147     // adaptive refinement should be derived from those specified for the
148     // PatchTable.  This is not a strict requirement, but it will avoid
149     // problems arising from specifying/coordinating the two independently
150     // (especially when dealing with face-varying patches).
151 
152     // Initialize options for the PatchTable:
153     //
154     // Choose patches adaptively refined to level 3 since the sharpest crease
155     // in the shape is 3.0f (in g_creaseweights[]), and include the inf-sharp
156     // crease option just to illustrate the need to syncronize options.
157     //
158     int maxPatchLevel = 3;
159 
160     Far::PatchTableFactory::Options patchOptions(maxPatchLevel);
161     patchOptions.SetPatchPrecision<Real>();
162     patchOptions.useInfSharpPatch = true;
163     patchOptions.generateVaryingTables = false;
164     patchOptions.endCapType =
165         Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
166 
167     // Initialize corresonding options for adaptive refinement:
168     Far::TopologyRefiner::AdaptiveOptions adaptiveOptions(maxPatchLevel);
169 
170     bool assignAdaptiveOptionsExplicitly = false;
171     if (assignAdaptiveOptionsExplicitly) {
172         adaptiveOptions.useInfSharpPatch = true;
173     } else {
174         // Be sure patch options were intialized with the desired max level
175         adaptiveOptions = patchOptions.GetRefineAdaptiveOptions();
176     }
177     assert(adaptiveOptions.useInfSharpPatch == patchOptions.useInfSharpPatch);
178 
179     // Apply adaptive refinement and construct the associated PatchTable to
180     // evaluate the limit surface:
181     refiner->RefineAdaptive(adaptiveOptions);
182 
183     Far::PatchTable const * patchTable =
184         Far::PatchTableFactory::Create(*refiner, patchOptions);
185 
186     // Compute the total number of points we need to evaluate the PatchTable.
187     // Approximations at irregular or extraordinary features require the use
188     // of additional points associated with the patches that are referred to
189     // as "local points" (i.e. local to the PatchTable).
190     int nRefinerVertices = refiner->GetNumVerticesTotal();
191     int nLocalPoints = patchTable->GetNumLocalPoints();
192 
193     // Create a buffer to hold the position of the refined verts and
194     // local points, then copy the coarse positions at the beginning.
195     std::vector<Vertex> verts(nRefinerVertices + nLocalPoints);
196     std::memcpy(&verts[0], g_verts, g_nverts*3*sizeof(Real));
197 
198     // Adaptive refinement may result in fewer levels than the max specified.
199     int nRefinedLevels = refiner->GetNumLevels();
200 
201     // Interpolate vertex primvar data : they are the control vertices
202     // of the limit patches (see tutorial_1_1 for details)
203     Far::PrimvarRefinerReal<Real> primvarRefiner(*refiner);
204 
205     Vertex * src = &verts[0];
206     for (int level = 1; level < nRefinedLevels; ++level) {
207         Vertex * dst = src + refiner->GetLevel(level-1).GetNumVertices();
208         primvarRefiner.Interpolate(level, src, dst);
209         src = dst;
210     }
211 
212     // Evaluate local points from interpolated vertex primvars.
213     if (nLocalPoints) {
214         patchTable->GetLocalPointStencilTable<Real>()->UpdateValues(
215             &verts[0], &verts[nRefinerVertices]);
216     }
217 
218     // Create a Far::PatchMap to help locating patches in the table
219     Far::PatchMap patchmap(*patchTable);
220 
221     // Create a Far::PtexIndices to help find indices of ptex faces.
222     Far::PtexIndices ptexIndices(*refiner);
223 
224     // Generate random samples on each ptex face
225     int nsamples = 200,
226         nfaces = ptexIndices.GetNumFaces();
227 
228     std::vector<LimitFrame> samples(nsamples * nfaces);
229 
230     srand( static_cast<int>(2147483647) );
231 
232     Real pWeights[20], dsWeights[20], dtWeights[20];
233 
234     for (int face=0, count=0; face<nfaces; ++face) {
235 
236         for (int sample=0; sample<nsamples; ++sample, ++count) {
237 
238             Real s = (Real)rand()/(Real)RAND_MAX,
239                  t = (Real)rand()/(Real)RAND_MAX;
240 
241             // Locate the patch corresponding to the face ptex idx and (s,t)
242             Far::PatchTable::PatchHandle const * handle =
243                 patchmap.FindPatch(face, s, t);
244             assert(handle);
245 
246             // Evaluate the patch weights, identify the CVs and compute the limit frame:
247             patchTable->EvaluateBasis(*handle, s, t, pWeights, dsWeights, dtWeights);
248 
249             Far::ConstIndexArray cvs = patchTable->GetPatchVertices(*handle);
250 
251             LimitFrame & dst = samples[count];
252             dst.Clear();
253             for (int cv=0; cv < cvs.size(); ++cv) {
254                 dst.AddWithWeight(verts[cvs[cv]], pWeights[cv], dsWeights[cv], dtWeights[cv]);
255             }
256 
257         }
258     }
259 
260     { // Visualization with Maya : print a MEL script that generates particles
261       // at the location of the limit vertices
262 
263         int nsamples = (int)samples.size();
264 
265         printf("file -f -new;\n");
266 
267         // Output particle positions for the tangent
268         printf("particle -n deriv1 ");
269         for (int sample=0; sample<nsamples; ++sample) {
270             Real const * pos = samples[sample].point;
271             printf("-p %f %f %f\n", pos[0], pos[1], pos[2]);
272         }
273         printf(";\n");
274         // Set per-particle direction using the limit tangent (display as 'Streak')
275         printf("setAttr \"deriv1.particleRenderType\" 6;\n");
276         printf("setAttr \"deriv1.velocity\" -type \"vectorArray\" %d ",nsamples);
277         for (int sample=0; sample<nsamples; ++sample) {
278             Real const * tan1 = samples[sample].deriv1;
279             printf("%f %f %f\n", tan1[0], tan1[1], tan1[2]);
280         }
281         printf(";\n");
282 
283         // Output particle positions for the bi-tangent
284         printf("particle -n deriv2 ");
285         for (int sample=0; sample<nsamples; ++sample) {
286             Real const * pos = samples[sample].point;
287             printf("-p %f %f %f\n", pos[0], pos[1], pos[2]);
288         }
289         printf(";\n");
290         printf("setAttr \"deriv2.particleRenderType\" 6;\n");
291         printf("setAttr \"deriv2.velocity\" -type \"vectorArray\" %d ",nsamples);
292         for (int sample=0; sample<nsamples; ++sample) {
293             Real const * tan2 = samples[sample].deriv2;
294             printf("%f %f %f\n", tan2[0], tan2[1], tan2[2]);
295         }
296         printf(";\n");
297 
298         // Exercise to the reader : cross tangent & bi-tangent for limit
299         // surface normal...
300 
301         // Force Maya DAG update to see the result in the viewport
302         printf("currentTime -edit `currentTime -q`;\n");
303         printf("select deriv1Shape deriv2Shape;\n");
304     }
305 
306 }
307 
308 //------------------------------------------------------------------------------
309 static Far::TopologyRefiner *
createTopologyRefiner()310 createTopologyRefiner() {
311 
312 
313     typedef Far::TopologyDescriptor Descriptor;
314 
315     Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;
316 
317     Sdc::Options options;
318     options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
319 
320     Descriptor desc;
321     desc.numVertices = g_nverts;
322     desc.numFaces = g_nfaces;
323     desc.numVertsPerFace = g_vertsperface;
324     desc.vertIndicesPerFace = g_faceverts;
325     desc.numCreases = g_ncreases;
326     desc.creaseVertexIndexPairs = g_creaseverts;
327     desc.creaseWeights = g_creaseweights;
328 
329     // Instantiate a Far::TopologyRefiner from the descriptor.
330     Far::TopologyRefiner * refiner =
331         Far::TopologyRefinerFactory<Descriptor>::Create(desc,
332             Far::TopologyRefinerFactory<Descriptor>::Options(type, options));
333 
334     return refiner;
335 }
336