1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
|
// Copyright 2015 Blender Foundation. All rights reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// Author: Sergey Sharybin
#ifdef _MSC_VER
# include <iso646.h>
#endif
#include "internal/topology/topology_refiner_impl.h"
#include <cassert>
#include <cstdio>
#include <opensubdiv/far/topologyRefinerFactory.h>
#include "internal/base/type_convert.h"
#include "internal/opensubdiv_util.h"
#include "opensubdiv_converter_capi.h"
using blender::opensubdiv::min;
using blender::opensubdiv::stack;
using blender::opensubdiv::vector;
struct TopologyRefinerData {
const OpenSubdiv_Converter *converter;
};
typedef OpenSubdiv::Far::TopologyRefinerFactory<TopologyRefinerData> TopologyRefinerFactoryType;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology(
TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{
const OpenSubdiv_Converter *converter = cb_data.converter;
// Faces and face-vertices.
const int num_faces = converter->getNumFaces(converter);
setNumBaseFaces(refiner, num_faces);
for (int face_index = 0; face_index < num_faces; ++face_index) {
const int num_face_vertices = converter->getNumFaceVertices(converter, face_index);
setNumBaseFaceVertices(refiner, face_index, num_face_vertices);
}
// Vertices.
const int num_vertices = converter->getNumVertices(converter);
setNumBaseVertices(refiner, num_vertices);
// If converter does not provide full topology, we are done.
if (!converter->specifiesFullTopology(converter)) {
return true;
}
// Edges and edge-faces.
const int num_edges = converter->getNumEdges(converter);
setNumBaseEdges(refiner, num_edges);
for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
const int num_edge_faces = converter->getNumEdgeFaces(converter, edge_index);
setNumBaseEdgeFaces(refiner, edge_index, num_edge_faces);
}
// Vertex-faces and vertex-edges.
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
const int num_vert_edges = converter->getNumVertexEdges(converter, vertex_index);
const int num_vert_faces = converter->getNumVertexFaces(converter, vertex_index);
setNumBaseVertexEdges(refiner, vertex_index, num_vert_edges);
setNumBaseVertexFaces(refiner, vertex_index, num_vert_faces);
}
return true;
}
template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{
using Far::IndexArray;
const OpenSubdiv_Converter *converter = cb_data.converter;
const bool full_topology_specified = converter->specifiesFullTopology(converter);
// Face relations.
const int num_faces = converter->getNumFaces(converter);
for (int face_index = 0; face_index < num_faces; ++face_index) {
IndexArray dst_face_verts = getBaseFaceVertices(refiner, face_index);
converter->getFaceVertices(converter, face_index, &dst_face_verts[0]);
if (full_topology_specified) {
IndexArray dst_face_edges = getBaseFaceEdges(refiner, face_index);
converter->getFaceEdges(converter, face_index, &dst_face_edges[0]);
}
}
// If converter does not provide full topology, we are done.
if (!full_topology_specified) {
return true;
}
// Edge relations.
const int num_edges = converter->getNumEdges(converter);
for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
// Edge-vertices.
IndexArray dst_edge_vertices = getBaseEdgeVertices(refiner, edge_index);
converter->getEdgeVertices(converter, edge_index, &dst_edge_vertices[0]);
// Edge-faces.
IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge_index);
converter->getEdgeFaces(converter, edge_index, &dst_edge_faces[0]);
}
// Vertex relations.
const int num_vertices = converter->getNumVertices(converter);
vector<int> vertex_faces, vertex_edges;
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
// Vertex-faces.
IndexArray dst_vertex_faces = getBaseVertexFaces(refiner, vertex_index);
const int num_vertex_faces = converter->getNumVertexFaces(converter, vertex_index);
vertex_faces.resize(num_vertex_faces);
converter->getVertexFaces(converter, vertex_index, &vertex_faces[0]);
// Vertex-edges.
IndexArray dst_vertex_edges = getBaseVertexEdges(refiner, vertex_index);
const int num_vertex_edges = converter->getNumVertexEdges(converter, vertex_index);
vertex_edges.resize(num_vertex_edges);
converter->getVertexEdges(converter, vertex_index, &vertex_edges[0]);
memcpy(&dst_vertex_edges[0], &vertex_edges[0], sizeof(int) * num_vertex_edges);
memcpy(&dst_vertex_faces[0], &vertex_faces[0], sizeof(int) * num_vertex_faces);
}
populateBaseLocalIndices(refiner);
return true;
}
template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{
using OpenSubdiv::Sdc::Crease;
const OpenSubdiv_Converter *converter = cb_data.converter;
const bool full_topology_specified = converter->specifiesFullTopology(converter);
if (full_topology_specified || converter->getEdgeVertices != NULL) {
const int num_edges = converter->getNumEdges(converter);
for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
const float sharpness = converter->getEdgeSharpness(converter, edge_index);
if (sharpness < 1e-6f) {
continue;
}
if (full_topology_specified) {
setBaseEdgeSharpness(refiner, edge_index, sharpness);
}
else {
int edge_vertices[2];
converter->getEdgeVertices(converter, edge_index, edge_vertices);
const int base_edge_index = findBaseEdge(refiner, edge_vertices[0], edge_vertices[1]);
if (base_edge_index == OpenSubdiv::Far::INDEX_INVALID) {
printf("OpenSubdiv Error: failed to find reconstructed edge\n");
return false;
}
setBaseEdgeSharpness(refiner, base_edge_index, sharpness);
}
}
}
// OpenSubdiv expects non-manifold vertices to be sharp but at the time it
// handles correct cases when vertex is a corner of plane. Currently mark
// vertices which are adjacent to a loose edge as sharp, but this decision
// needs some more investigation.
const int num_vertices = converter->getNumVertices(converter);
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
ConstIndexArray vertex_edges = getBaseVertexEdges(refiner, vertex_index);
if (converter->isInfiniteSharpVertex(converter, vertex_index)) {
setBaseVertexSharpness(refiner, vertex_index, Crease::SHARPNESS_INFINITE);
continue;
}
// Get sharpness provided by the converter.
float sharpness = 0.0f;
if (converter->getVertexSharpness != NULL) {
sharpness = converter->getVertexSharpness(converter, vertex_index);
}
// If it's vertex where 2 non-manifold edges meet adjust vertex sharpness to
// the edges.
// This way having a plane with all 4 edges set to be sharp produces sharp
// corners in the subdivided result.
if (vertex_edges.size() == 2) {
const int edge0 = vertex_edges[0], edge1 = vertex_edges[1];
const float sharpness0 = refiner._levels[0]->getEdgeSharpness(edge0);
const float sharpness1 = refiner._levels[0]->getEdgeSharpness(edge1);
// TODO(sergey): Find a better mixing between edge and vertex sharpness.
sharpness += min(sharpness0, sharpness1);
sharpness = min(sharpness, 10.0f);
}
setBaseVertexSharpness(refiner, vertex_index, sharpness);
}
return true;
}
template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology(
TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{
const OpenSubdiv_Converter *converter = cb_data.converter;
if (converter->getNumUVLayers == NULL) {
assert(converter->precalcUVLayer == NULL);
assert(converter->getNumUVCoordinates == NULL);
assert(converter->getFaceCornerUVIndex == NULL);
assert(converter->finishUVLayer == NULL);
return true;
}
const int num_layers = converter->getNumUVLayers(converter);
if (num_layers <= 0) {
// No UV maps, we can skip any face-varying data.
return true;
}
const int num_faces = getNumBaseFaces(refiner);
for (int layer_index = 0; layer_index < num_layers; ++layer_index) {
converter->precalcUVLayer(converter, layer_index);
const int num_uvs = converter->getNumUVCoordinates(converter);
// Fill in per-corner index of the UV.
const int channel = createBaseFVarChannel(refiner, num_uvs);
// TODO(sergey): Need to check whether converter changed the winding of
// face to match OpenSubdiv's expectations.
for (int face_index = 0; face_index < num_faces; ++face_index) {
Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner, face_index, channel);
for (int corner = 0; corner < dst_face_uvs.size(); ++corner) {
const int uv_index = converter->getFaceCornerUVIndex(converter, face_index, corner);
dst_face_uvs[corner] = uv_index;
}
}
converter->finishUVLayer(converter);
}
return true;
}
template<>
inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology(
TopologyError /*errCode*/, const char *msg, const TopologyRefinerData & /*mesh*/)
{
printf("OpenSubdiv Error: %s\n", msg);
}
} /* namespace Far */
} /* namespace OPENSUBDIV_VERSION */
} /* namespace OpenSubdiv */
namespace blender {
namespace opensubdiv {
namespace {
OpenSubdiv::Sdc::Options::VtxBoundaryInterpolation getVtxBoundaryInterpolationFromCAPI(
OpenSubdiv_VtxBoundaryInterpolation boundary_interpolation)
{
using OpenSubdiv::Sdc::Options;
switch (boundary_interpolation) {
case OSD_VTX_BOUNDARY_NONE:
return Options::VTX_BOUNDARY_NONE;
case OSD_VTX_BOUNDARY_EDGE_ONLY:
return Options::VTX_BOUNDARY_EDGE_ONLY;
case OSD_VTX_BOUNDARY_EDGE_AND_CORNER:
return Options::VTX_BOUNDARY_EDGE_AND_CORNER;
}
assert(!"Unknown veretx boundary interpolation.");
return Options::VTX_BOUNDARY_EDGE_ONLY;
}
OpenSubdiv::Sdc::Options getSDCOptions(OpenSubdiv_Converter *converter)
{
using OpenSubdiv::Sdc::Options;
const Options::FVarLinearInterpolation linear_interpolation = getFVarLinearInterpolationFromCAPI(
converter->getFVarLinearInterpolation(converter));
Options options;
options.SetVtxBoundaryInterpolation(
getVtxBoundaryInterpolationFromCAPI(converter->getVtxBoundaryInterpolation(converter)));
options.SetCreasingMethod(Options::CREASE_UNIFORM);
options.SetFVarLinearInterpolation(linear_interpolation);
return options;
}
TopologyRefinerFactoryType::Options getTopologyRefinerOptions(OpenSubdiv_Converter *converter)
{
using OpenSubdiv::Sdc::SchemeType;
OpenSubdiv::Sdc::Options sdc_options = getSDCOptions(converter);
const SchemeType scheme_type = getSchemeTypeFromCAPI(converter->getSchemeType(converter));
TopologyRefinerFactoryType::Options topology_options(scheme_type, sdc_options);
// NOTE: When debugging topology conversion related functionality it is helpful to set this
// to truth. In all other cases leave it at false. so debugging of other areas is not affected
// by performance penalty happening in this module.
topology_options.validateFullTopology = false;
return topology_options;
}
} // namespace
TopologyRefinerImpl *TopologyRefinerImpl::createFromConverter(
OpenSubdiv_Converter *converter, const OpenSubdiv_TopologyRefinerSettings &settings)
{
using OpenSubdiv::Far::TopologyRefiner;
TopologyRefinerData cb_data;
cb_data.converter = converter;
// Create OpenSubdiv descriptor for the topology refiner.
TopologyRefinerFactoryType::Options topology_refiner_options = getTopologyRefinerOptions(
converter);
TopologyRefiner *topology_refiner = TopologyRefinerFactoryType::Create(cb_data,
topology_refiner_options);
if (topology_refiner == nullptr) {
return nullptr;
}
// Create Blender-side object holding all necessary data for the topology refiner.
TopologyRefinerImpl *topology_refiner_impl = new TopologyRefinerImpl();
topology_refiner_impl->topology_refiner = topology_refiner;
topology_refiner_impl->settings = settings;
return topology_refiner_impl;
}
} // namespace opensubdiv
} // namespace blender
|