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