Blender V5.0
topology_refiner_factory.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2015 Blender Foundation
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 *
5 * Author: Sergey Sharybin. */
6
7#ifdef _MSC_VER
8# include <iso646.h>
9#endif
10
11#include <cassert>
12#include <cstdio>
13
14#include <opensubdiv/far/topologyRefinerFactory.h>
15
18
21
26
27using TopologyRefinerFactoryType = OpenSubdiv::Far::TopologyRefinerFactory<TopologyRefinerData>;
28
30
31template<>
32inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology(
33 TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
34{
36
37 const OpenSubdiv_Converter *converter = cb_data.converter;
38 MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;
39
40 // Vertices.
41 const int num_vertices = converter->getNumVertices(converter);
42 base_mesh_topology->setNumVertices(num_vertices);
43 setNumBaseVertices(refiner, num_vertices);
44
45 // Edges.
46 //
47 // NOTE: Always store edges in the base mesh topology so then comparison can
48 // happen, but only provide edges to TopologyRefiner if full topology is
49 // specified (if full topology is not specified then topology refiner must
50 // not see any edges, which will indicate to it that winding and edges are to
51 // be reconstructed).
52 //
53 // NOTE: it is a possible use case when user code does not need crease at all
54 // (which is the only real reason why converter would want to provide edges in
55 // the case of partial topology specification). So it might be so getNumEdges
56 // callback is nullptr.
57 if (converter->getNumEdges != nullptr) {
58 const int num_edges = converter->getNumEdges(converter);
59 base_mesh_topology->setNumEdges(num_edges);
60 }
61
62 // Faces and face-vertices.
63 const blender::OffsetIndices<int> src_faces = converter->faces;
64 base_mesh_topology->setNumFaces(src_faces.size());
65 setNumBaseFaces(refiner, src_faces.size());
66 for (const int face_index : src_faces.index_range()) {
67 const int num_face_vertices = src_faces[face_index].size();
68 base_mesh_topology->setNumFaceVertices(face_index, num_face_vertices);
69 setNumBaseFaceVertices(refiner, face_index, num_face_vertices);
70 }
71
72 // If converter does not provide full topology, we are done.
73 //
74 // The rest is needed to define relations between faces-of-edge and
75 // edges-of-vertex, which is not available for partially specified mesh.
76 if (!converter->specifiesFullTopology(converter)) {
77 base_mesh_topology->finishResizeTopology();
78 return true;
79 }
80
81 // Edges and edge-faces.
82 const int num_edges = converter->getNumEdges(converter);
83 setNumBaseEdges(refiner, num_edges);
84 for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
85 const int num_edge_faces = converter->getNumEdgeFaces(converter, edge_index);
86 setNumBaseEdgeFaces(refiner, edge_index, num_edge_faces);
87 }
88
89 // Vertex-faces and vertex-edges.
90 for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
91 const int num_vert_edges = converter->getNumVertexEdges(converter, vertex_index);
92 const int num_vert_faces = converter->getNumVertexFaces(converter, vertex_index);
93 setNumBaseVertexEdges(refiner, vertex_index, num_vert_edges);
94 setNumBaseVertexFaces(refiner, vertex_index, num_vert_faces);
95 }
96
97 base_mesh_topology->finishResizeTopology();
98 return true;
99}
100
101template<>
102inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
103 TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
104{
106 using Far::IndexArray;
107
108 const OpenSubdiv_Converter *converter = cb_data.converter;
109 MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;
110
111 const bool full_topology_specified = converter->specifiesFullTopology(converter);
112
113 const blender::OffsetIndices<int> src_faces = converter->faces;
114
115 // Vertices of face.
116 for (const int face_index : src_faces.index_range()) {
117 IndexArray dst_face_verts = getBaseFaceVertices(refiner, face_index);
118 converter->getFaceVertices(converter, face_index, &dst_face_verts[0]);
119
120 base_mesh_topology->setFaceVertexIndices(
121 face_index, dst_face_verts.size(), &dst_face_verts[0]);
122 }
123
124 // If converter does not provide full topology, we are done.
125 //
126 // The rest is needed to define relations between faces-of-edge and
127 // edges-of-vertex, which is not available for partially specified mesh.
128 if (!full_topology_specified) {
129 return true;
130 }
131
132 // Vertex relations.
133 const int num_vertices = converter->getNumVertices(converter);
134 std::vector<int> vertex_faces, vertex_edges;
135 for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
136 // Vertex-faces.
137 IndexArray dst_vertex_faces = getBaseVertexFaces(refiner, vertex_index);
138 const int num_vertex_faces = converter->getNumVertexFaces(converter, vertex_index);
139 vertex_faces.resize(num_vertex_faces);
140 converter->getVertexFaces(converter, vertex_index, vertex_faces.data());
141
142 // Vertex-edges.
143 IndexArray dst_vertex_edges = getBaseVertexEdges(refiner, vertex_index);
144 const int num_vertex_edges = converter->getNumVertexEdges(converter, vertex_index);
145 vertex_edges.resize(num_vertex_edges);
146 converter->getVertexEdges(converter, vertex_index, vertex_edges.data());
147 memcpy(&dst_vertex_edges[0], vertex_edges.data(), sizeof(int) * num_vertex_edges);
148 memcpy(&dst_vertex_faces[0], vertex_faces.data(), sizeof(int) * num_vertex_faces);
149 }
150
151 // Edge relations.
152 const int num_edges = converter->getNumEdges(converter);
153 for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
154 // Vertices this edge connects.
155 IndexArray dst_edge_vertices = getBaseEdgeVertices(refiner, edge_index);
156 converter->getEdgeVertices(converter, edge_index, &dst_edge_vertices[0]);
157
158 // Faces adjacent to this edge.
159 IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge_index);
160 converter->getEdgeFaces(converter, edge_index, &dst_edge_faces[0]);
161 }
162
163 // Face relations.
164 for (const int face_index : src_faces.index_range()) {
165 IndexArray dst_face_edges = getBaseFaceEdges(refiner, face_index);
166 converter->getFaceEdges(converter, face_index, &dst_face_edges[0]);
167 }
168
169 populateBaseLocalIndices(refiner);
170
171 return true;
172}
173
174template<>
175inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
176 TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
177{
179 using OpenSubdiv::Sdc::Crease;
180
181 const OpenSubdiv_Converter *converter = cb_data.converter;
182 MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;
183
184 const bool full_topology_specified = converter->specifiesFullTopology(converter);
185 if (full_topology_specified || converter->getEdgeVertices != nullptr) {
186 const int num_edges = converter->getNumEdges(converter);
187 for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
188 const float sharpness = converter->getEdgeSharpness(converter, edge_index);
189 if (sharpness < 1e-6f) {
190 continue;
191 }
192
193 int edge_vertices[2];
194 converter->getEdgeVertices(converter, edge_index, edge_vertices);
195 base_mesh_topology->setEdgeVertexIndices(edge_index, edge_vertices[0], edge_vertices[1]);
196 base_mesh_topology->setEdgeSharpness(edge_index, sharpness);
197
198 if (full_topology_specified) {
199 setBaseEdgeSharpness(refiner, edge_index, sharpness);
200 }
201 else {
202 // TODO(sergey): Should be a faster way to find reconstructed edge to
203 // specify sharpness for (assuming, findBaseEdge has linear complexity).
204 const int base_edge_index = findBaseEdge(refiner, edge_vertices[0], edge_vertices[1]);
205 if (base_edge_index == OpenSubdiv::Far::INDEX_INVALID) {
206 printf("OpenSubdiv Error: failed to find reconstructed edge\n");
207 return false;
208 }
209 setBaseEdgeSharpness(refiner, base_edge_index, sharpness);
210 }
211 }
212 }
213
214 // OpenSubdiv expects non-manifold vertices to be sharp but at the time it
215 // handles correct cases when vertex is a corner of plane. Currently mark
216 // vertices which are adjacent to a loose edge as sharp, but this decision
217 // needs some more investigation.
218 const int num_vertices = converter->getNumVertices(converter);
219 for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
220 ConstIndexArray vertex_edges = getBaseVertexEdges(refiner, vertex_index);
221 if (converter->isInfiniteSharpVertex(converter, vertex_index)) {
222 base_mesh_topology->setVertexSharpness(vertex_index, Crease::SHARPNESS_INFINITE);
223 setBaseVertexSharpness(refiner, vertex_index, Crease::SHARPNESS_INFINITE);
224 continue;
225 }
226
227 // Get sharpness provided by the converter.
228 float sharpness = 0.0f;
229 if (converter->getVertexSharpness != nullptr) {
230 sharpness = converter->getVertexSharpness(converter, vertex_index);
231 base_mesh_topology->setVertexSharpness(vertex_index, sharpness);
232 }
233
234 // If its vertex where 2 non-manifold edges meet adjust vertex sharpness to
235 // the edges.
236 // This way having a plane with all 4 edges set to be sharp produces sharp
237 // corners in the subdivided result.
238 if (vertex_edges.size() == 2) {
239 const int edge0 = vertex_edges[0], edge1 = vertex_edges[1];
240 const float sharpness0 = refiner._levels[0]->getEdgeSharpness(edge0);
241 const float sharpness1 = refiner._levels[0]->getEdgeSharpness(edge1);
242 // TODO(sergey): Find a better mixing between edge and vertex sharpness.
243 sharpness += std::min(sharpness0, sharpness1);
244 sharpness = std::min(sharpness, 10.0f);
245 }
246
247 setBaseVertexSharpness(refiner, vertex_index, sharpness);
248 }
249 return true;
250}
251
252template<>
253inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology(
254 TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
255{
256 const OpenSubdiv_Converter *converter = cb_data.converter;
257 if (converter->getNumUVLayers == nullptr) {
258 assert(converter->precalcUVLayer == nullptr);
259 assert(converter->getNumUVCoordinates == nullptr);
260 assert(converter->getFaceCornerUVIndex == nullptr);
261 assert(converter->finishUVLayer == nullptr);
262 return true;
263 }
264 const int num_layers = converter->getNumUVLayers(converter);
265 if (num_layers <= 0) {
266 // No UV maps, we can skip any face-varying data.
267 return true;
268 }
269 const int num_faces = getNumBaseFaces(refiner);
270 for (int layer_index = 0; layer_index < num_layers; ++layer_index) {
271 converter->precalcUVLayer(converter, layer_index);
272 const int num_uvs = converter->getNumUVCoordinates(converter);
273 // Fill in per-corner index of the UV.
274 const int channel = createBaseFVarChannel(refiner, num_uvs);
275 // TODO(sergey): Need to check whether converter changed the winding of
276 // face to match OpenSubdiv's expectations.
277 for (int face_index = 0; face_index < num_faces; ++face_index) {
278 Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner, face_index, channel);
279 for (int corner = 0; corner < dst_face_uvs.size(); ++corner) {
280 const int uv_index = converter->getFaceCornerUVIndex(converter, face_index, corner);
281 dst_face_uvs[corner] = uv_index;
282 }
283 }
284 converter->finishUVLayer(converter);
285 }
286 return true;
287}
288
289template<>
290inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology(
291 TopologyError /*errCode*/, const char *msg, const TopologyRefinerData & /*mesh*/)
292{
293 printf("OpenSubdiv Error: %s\n", msg);
294}
295
296} // namespace OpenSubdiv::OPENSUBDIV_VERSION::Far
297
298namespace blender::opensubdiv {
299
300static OpenSubdiv::Sdc::Options getSDCOptions(OpenSubdiv_Converter *converter)
301{
302 using OpenSubdiv::Sdc::Options;
303
304 const Options::FVarLinearInterpolation linear_interpolation = getFVarLinearInterpolationFromCAPI(
305 converter->getFVarLinearInterpolation(converter));
306
308 options.SetVtxBoundaryInterpolation(
310 options.SetCreasingMethod(Options::CREASE_UNIFORM);
311 options.SetFVarLinearInterpolation(linear_interpolation);
312
313 return options;
314}
315
316static TopologyRefinerFactoryType::Options getTopologyRefinerOptions(
317 OpenSubdiv_Converter *converter)
318{
319 using OpenSubdiv::Sdc::SchemeType;
320
321 OpenSubdiv::Sdc::Options sdc_options = getSDCOptions(converter);
322
323 const SchemeType scheme_type = getSchemeTypeFromCAPI(converter->getSchemeType(converter));
324 TopologyRefinerFactoryType::Options topology_options(scheme_type, sdc_options);
325
326 // NOTE: When debugging topology conversion related functionality it is helpful to set this
327 // to truth. In all other cases leave it at false. so debugging of other areas is not affected
328 // by performance penalty happening in this module.
329 topology_options.validateFullTopology = false;
330
331 return topology_options;
332}
333
336{
337 using OpenSubdiv::Far::TopologyRefiner;
338
340
341 TopologyRefinerData cb_data;
342 cb_data.converter = converter;
344
345 // Create OpenSubdiv descriptor for the topology refiner.
346 TopologyRefinerFactoryType::Options topology_refiner_options = getTopologyRefinerOptions(
347 converter);
348 TopologyRefiner *topology_refiner = TopologyRefinerFactoryType::Create(cb_data,
349 topology_refiner_options);
350 if (topology_refiner == nullptr) {
351 return nullptr;
352 }
353
354 // Create Blender-side object holding all necessary data for the topology refiner.
355 TopologyRefinerImpl *topology_refiner_impl = new TopologyRefinerImpl();
356 topology_refiner_impl->topology_refiner = topology_refiner;
357 topology_refiner_impl->settings = settings;
358 topology_refiner_impl->base_mesh_topology = std::move(base_mesh_topology);
359
360 return topology_refiner_impl;
361}
362
363} // namespace blender::opensubdiv
OpenSubdiv::Far::TopologyRefiner * topology_refiner
static TopologyRefinerImpl * createFromConverter(OpenSubdiv_Converter *converter, const OpenSubdiv_TopologyRefinerSettings &settings)
CCL_NAMESPACE_BEGIN struct Options options
#define assert(assertion)
#define printf(...)
OpenSubdiv::Sdc::Options::VtxBoundaryInterpolation getVtxBoundaryInterpolationFromCAPI(OpenSubdiv_VtxBoundaryInterpolation boundary_interpolation)
OpenSubdiv::Sdc::SchemeType getSchemeTypeFromCAPI(OpenSubdiv_SchemeType type)
static OpenSubdiv::Sdc::Options getSDCOptions(OpenSubdiv_Converter *converter)
static TopologyRefinerFactoryType::Options getTopologyRefinerOptions(OpenSubdiv_Converter *converter)
OpenSubdiv::Sdc::Options::FVarLinearInterpolation getFVarLinearInterpolationFromCAPI(OpenSubdiv_FVarLinearInterpolation linear_interpolation)
int(* getFaceCornerUVIndex)(const OpenSubdiv_Converter *converter, const int face_index, const int corner_index)
int(* getNumEdges)(const OpenSubdiv_Converter *converter)
void(* precalcUVLayer)(const OpenSubdiv_Converter *converter, const int layer_index)
float(* getEdgeSharpness)(const OpenSubdiv_Converter *converter, const int edge_index)
int(* getNumVertexFaces)(const OpenSubdiv_Converter *converter, const int vertex_index)
void(* getVertexFaces)(const OpenSubdiv_Converter *converter, const int vertex_index, int *vertex_faces)
void(* getVertexEdges)(const OpenSubdiv_Converter *converter, const int vertex_index, int *vertex_edges)
OpenSubdiv_FVarLinearInterpolation(* getFVarLinearInterpolation)(const OpenSubdiv_Converter *converter)
bool(* specifiesFullTopology)(const OpenSubdiv_Converter *converter)
int(* getNumUVCoordinates)(const OpenSubdiv_Converter *converter)
void(* getFaceVertices)(const OpenSubdiv_Converter *converter, const int face_index, int *face_vertices)
OpenSubdiv_VtxBoundaryInterpolation(* getVtxBoundaryInterpolation)(const OpenSubdiv_Converter *converter)
int(* getNumUVLayers)(const OpenSubdiv_Converter *converter)
int(* getNumEdgeFaces)(const OpenSubdiv_Converter *converter, const int edge_index)
blender::OffsetIndices< int > faces
bool(* isInfiniteSharpVertex)(const OpenSubdiv_Converter *converter, const int vertex_index)
float(* getVertexSharpness)(const OpenSubdiv_Converter *converter, const int vertex_index)
OpenSubdiv_SchemeType(* getSchemeType)(const OpenSubdiv_Converter *converter)
void(* getEdgeVertices)(const OpenSubdiv_Converter *converter, const int edge_index, int edge_vertices[2])
int(* getNumVertexEdges)(const OpenSubdiv_Converter *converter, const int vertex_index)
int(* getNumVertices)(const OpenSubdiv_Converter *converter)
void(* getFaceEdges)(const OpenSubdiv_Converter *converter, const int face_index, int *face_edges)
void(* finishUVLayer)(const OpenSubdiv_Converter *converter)
void(* getEdgeFaces)(const OpenSubdiv_Converter *converter, const int edge, int *edge_faces)
blender::opensubdiv::MeshTopology * base_mesh_topology
const OpenSubdiv_Converter * converter
OpenSubdiv::Far::TopologyRefinerFactory< TopologyRefinerData > TopologyRefinerFactoryType