Welcome to mirror list, hosted at ThFree Co, Russian Federation.

topology_refiner_factory.cc « topology « internal « opensubdiv « intern - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: deab5f0644f6f03c530a51c34b33be72037c9321 (plain)
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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
// 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.h"
#include "internal/base/type_convert.h"
#include "internal/topology/mesh_topology.h"

#include "opensubdiv_converter_capi.h"

using blender::opensubdiv::min;
using blender::opensubdiv::stack;
using blender::opensubdiv::vector;

struct TopologyRefinerData {
  const OpenSubdiv_Converter *converter;
  blender::opensubdiv::MeshTopology *base_mesh_topology;
};

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)
{
  using blender::opensubdiv::MeshTopology;

  const OpenSubdiv_Converter *converter = cb_data.converter;
  MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;

  // Vertices.
  const int num_vertices = converter->getNumVertices(converter);
  base_mesh_topology->setNumVertices(num_vertices);
  setNumBaseVertices(refiner, num_vertices);

  // Edges.
  //
  // NOTE: Always store edges in the base mesh topology so then comparison can
  // happen, but only provide edges to TopologyRefiner if full topology is
  // specified (if full topology is not specified then topology refiner must
  // not see any edges, which will indicate to it that winding and edges are to
  // be reconstructed).
  //
  // NOTE: it is a possible use case when user code does not need crease at all
  // (which is the only real reason why converter would want to provide edges in
  // the case of partial topology specification). So it might be so getNumEdges
  // callback is nullptr.
  if (converter->getNumEdges != nullptr) {
    const int num_edges = converter->getNumEdges(converter);
    base_mesh_topology->setNumEdges(num_edges);
  }

  // Faces and face-vertices.
  const int num_faces = converter->getNumFaces(converter);
  base_mesh_topology->setNumFaces(num_faces);
  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);
    base_mesh_topology->setNumFaceVertices(face_index, num_face_vertices);
    setNumBaseFaceVertices(refiner, face_index, num_face_vertices);
  }

  // If converter does not provide full topology, we are done.
  //
  // The rest is needed to define relations between faces-of-edge and
  // edges-of-vertex, which is not available for partially specified mesh.
  if (!converter->specifiesFullTopology(converter)) {
    base_mesh_topology->finishResizeTopology();
    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);
  }

  base_mesh_topology->finishResizeTopology();
  return true;
}

template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
    TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{
  using blender::opensubdiv::MeshTopology;
  using Far::IndexArray;

  const OpenSubdiv_Converter *converter = cb_data.converter;
  MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;

  const bool full_topology_specified = converter->specifiesFullTopology(converter);

  // Vertices of face.
  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]);

    base_mesh_topology->setFaceVertexIndices(
        face_index, dst_face_verts.size(), &dst_face_verts[0]);
  }

  // If converter does not provide full topology, we are done.
  //
  // The rest is needed to define relations between faces-of-edge and
  // edges-of-vertex, which is not available for partially specified mesh.
  if (!full_topology_specified) {
    return true;
  }

  // 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);
  }

  // Edge relations.
  const int num_edges = converter->getNumEdges(converter);
  for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
    // Vertices this edge connects.
    IndexArray dst_edge_vertices = getBaseEdgeVertices(refiner, edge_index);
    converter->getEdgeVertices(converter, edge_index, &dst_edge_vertices[0]);

    // Faces adjacent to this edge.
    IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge_index);
    converter->getEdgeFaces(converter, edge_index, &dst_edge_faces[0]);
  }

  // Face relations.
  for (int face_index = 0; face_index < num_faces; ++face_index) {
    IndexArray dst_face_edges = getBaseFaceEdges(refiner, face_index);
    converter->getFaceEdges(converter, face_index, &dst_face_edges[0]);
  }

  populateBaseLocalIndices(refiner);

  return true;
}

template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
    TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{
  using blender::opensubdiv::MeshTopology;
  using OpenSubdiv::Sdc::Crease;

  const OpenSubdiv_Converter *converter = cb_data.converter;
  MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;

  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;
      }

      int edge_vertices[2];
      converter->getEdgeVertices(converter, edge_index, edge_vertices);
      base_mesh_topology->setEdgeVertexIndices(edge_index, edge_vertices[0], edge_vertices[1]);
      base_mesh_topology->setEdgeSharpness(edge_index, sharpness);

      if (full_topology_specified) {
        setBaseEdgeSharpness(refiner, edge_index, sharpness);
      }
      else {
        // TODO(sergey): Should be a faster way to find reconstructed edge to
        // specify sharpness for (assuming, findBaseEdge has linear complexity).
        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)) {
      base_mesh_topology->setVertexSharpness(vertex_index, Crease::SHARPNESS_INFINITE);
      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);
      base_mesh_topology->setVertexSharpness(vertex_index, sharpness);
    }

    // If its 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 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;

  blender::opensubdiv::MeshTopology base_mesh_topology;

  TopologyRefinerData cb_data;
  cb_data.converter = converter;
  cb_data.base_mesh_topology = &base_mesh_topology;

  // 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;
  topology_refiner_impl->base_mesh_topology = move(base_mesh_topology);

  return topology_refiner_impl;
}

}  // namespace opensubdiv
}  // namespace blender