OpenSubdiv: Compare sharpness based on converter

This change starts the transition of topology refiner comparison
to compare actual values given by the converter, which will not be
affected by the refinement or face winding synchronization steps.

Currently is only implemented for vertex sharpness, but will be
extended further as followup development.

Fixes T71908: Subdiv: Incorrect topology comparison, leading to poor performance
This commit is contained in:
Sergey Sharybin 2020-05-19 11:43:58 +02:00
parent 717d968fb9
commit 614d70a87d
3 changed files with 68 additions and 53 deletions

@ -22,6 +22,7 @@
#include "internal/base/edge_map.h" #include "internal/base/edge_map.h"
#include "internal/base/type.h" #include "internal/base/type.h"
#include "internal/base/type_convert.h" #include "internal/base/type_convert.h"
#include "internal/topology/mesh_topology.h"
#include "internal/topology/topology_refiner_impl.h" #include "internal/topology/topology_refiner_impl.h"
#include "opensubdiv_converter_capi.h" #include "opensubdiv_converter_capi.h"
@ -553,55 +554,32 @@ bool checkEdgeTagsMatch(const OpenSubdiv_TopologyRefiner *topology_refiner,
} }
} }
bool checkvertexSharpnessMatch(const OpenSubdiv_TopologyRefiner *topology_refiner, float getEffectiveVertexSharpness(const OpenSubdiv_Converter *converter, const int vertex_index)
{
if (converter->isInfiniteSharpVertex != nullptr &&
converter->isInfiniteSharpVertex(converter, vertex_index)) {
return OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE;
}
if (converter->getVertexSharpness != nullptr) {
return converter->getVertexSharpness(converter, vertex_index);
}
return 0.0f;
}
bool checkVertexSharpnessMatch(const OpenSubdiv_TopologyRefiner *topology_refiner,
const OpenSubdiv_Converter *converter) const OpenSubdiv_Converter *converter)
{ {
using OpenSubdiv::Far::ConstIndexArray; const MeshTopology &base_mesh_topology = topology_refiner->impl->base_mesh_topology;
using OpenSubdiv::Far::TopologyLevel;
using OpenSubdiv::Sdc::Crease; const int num_vertices = base_mesh_topology.getNumVertices();
const TopologyLevel &base_level = getOSDTopologyRefiner(topology_refiner)->GetLevel(0);
// Create mapping for quick lookup of edge index from its vertices indices.
//
// TODO(sergey): Consider caching it in some sort of wrapper around topology
// refiner.
const int num_edges = base_level.GetNumEdges();
EdgeTagMap<int> edge_map;
for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
int edge_vertices[2];
converter->getEdgeVertices(converter, edge_index, edge_vertices);
edge_map.insert(edge_vertices[0], edge_vertices[1], edge_index);
}
const int num_vertices = base_level.GetNumVertices();
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) { for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
const float current_sharpness = base_level.GetVertexSharpness(vertex_index); const float current_sharpness = base_mesh_topology.vertices[vertex_index].sharpness;
if (converter->isInfiniteSharpVertex(converter, vertex_index)) { const float requested_sharpness = getEffectiveVertexSharpness(converter, vertex_index);
if (current_sharpness != Crease::SHARPNESS_INFINITE) {
return false; if (current_sharpness != requested_sharpness) {
} return false;
}
else {
ConstIndexArray vertex_edges = base_level.GetVertexEdges(vertex_index);
float sharpness = converter->getVertexSharpness(converter, vertex_index);
if (vertex_edges.size() == 2) {
const int edge0 = vertex_edges[0], edge1 = vertex_edges[1];
// Construct keys for lookup.
ConstIndexArray edge0_vertices = base_level.GetEdgeVertices(edge0);
ConstIndexArray edge1_vertices = base_level.GetEdgeVertices(edge1);
EdgeKey edge0_key(edge0_vertices[0], edge0_vertices[1]);
EdgeKey edge1_key(edge1_vertices[0], edge1_vertices[1]);
// Lookup edge indices in the converter.
const int edge0_converter_index = edge_map[edge0_key];
const int edge1_converter_index = edge_map[edge1_key];
// Lookup sharpness.
const float sharpness0 = converter->getEdgeSharpness(converter, edge0_converter_index);
const float sharpness1 = converter->getEdgeSharpness(converter, edge1_converter_index);
// TODO(sergey): Find a better mixing between edge and vertex sharpness.
sharpness += min(sharpness0, sharpness1);
sharpness = min(sharpness, 10.0f);
}
if (sharpness != current_sharpness) {
return false;
}
} }
} }
return true; return true;
@ -652,7 +630,7 @@ bool checkTopologyAttributesMatch(const OpenSubdiv_TopologyRefiner *topology_ref
const OpenSubdiv_Converter *converter) const OpenSubdiv_Converter *converter)
{ {
return checkEdgeTagsMatch(topology_refiner, converter) && return checkEdgeTagsMatch(topology_refiner, converter) &&
checkvertexSharpnessMatch(topology_refiner, converter) && checkVertexSharpnessMatch(topology_refiner, converter) &&
checkUVLayersMatch(topology_refiner, converter); checkUVLayersMatch(topology_refiner, converter);
} }

@ -29,6 +29,8 @@
#include "internal/base/type.h" #include "internal/base/type.h"
#include "internal/base/type_convert.h" #include "internal/base/type_convert.h"
#include "internal/topology/mesh_topology.h"
#include "opensubdiv_converter_capi.h" #include "opensubdiv_converter_capi.h"
using blender::opensubdiv::min; using blender::opensubdiv::min;
@ -37,6 +39,7 @@ using blender::opensubdiv::vector;
struct TopologyRefinerData { struct TopologyRefinerData {
const OpenSubdiv_Converter *converter; const OpenSubdiv_Converter *converter;
blender::opensubdiv::MeshTopology *base_mesh_topology;
}; };
typedef OpenSubdiv::Far::TopologyRefinerFactory<TopologyRefinerData> TopologyRefinerFactoryType; typedef OpenSubdiv::Far::TopologyRefinerFactory<TopologyRefinerData> TopologyRefinerFactoryType;
@ -49,7 +52,11 @@ template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology( inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology(
TopologyRefiner &refiner, const TopologyRefinerData &cb_data) TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{ {
using blender::opensubdiv::MeshTopology;
const OpenSubdiv_Converter *converter = cb_data.converter; const OpenSubdiv_Converter *converter = cb_data.converter;
MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;
// Faces and face-vertices. // Faces and face-vertices.
const int num_faces = converter->getNumFaces(converter); const int num_faces = converter->getNumFaces(converter);
setNumBaseFaces(refiner, num_faces); setNumBaseFaces(refiner, num_faces);
@ -57,13 +64,17 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology
const int num_face_vertices = converter->getNumFaceVertices(converter, face_index); const int num_face_vertices = converter->getNumFaceVertices(converter, face_index);
setNumBaseFaceVertices(refiner, face_index, num_face_vertices); setNumBaseFaceVertices(refiner, face_index, num_face_vertices);
} }
// Vertices. // Vertices.
const int num_vertices = converter->getNumVertices(converter); const int num_vertices = converter->getNumVertices(converter);
base_mesh_topology->setNumVertices(num_vertices);
setNumBaseVertices(refiner, num_vertices); setNumBaseVertices(refiner, num_vertices);
// If converter does not provide full topology, we are done. // If converter does not provide full topology, we are done.
if (!converter->specifiesFullTopology(converter)) { if (!converter->specifiesFullTopology(converter)) {
return true; return true;
} }
// Edges and edge-faces. // Edges and edge-faces.
const int num_edges = converter->getNumEdges(converter); const int num_edges = converter->getNumEdges(converter);
setNumBaseEdges(refiner, num_edges); setNumBaseEdges(refiner, num_edges);
@ -71,6 +82,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology
const int num_edge_faces = converter->getNumEdgeFaces(converter, edge_index); const int num_edge_faces = converter->getNumEdgeFaces(converter, edge_index);
setNumBaseEdgeFaces(refiner, edge_index, num_edge_faces); setNumBaseEdgeFaces(refiner, edge_index, num_edge_faces);
} }
// Vertex-faces and vertex-edges. // Vertex-faces and vertex-edges.
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) { 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_edges = converter->getNumVertexEdges(converter, vertex_index);
@ -78,6 +90,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology
setNumBaseVertexEdges(refiner, vertex_index, num_vert_edges); setNumBaseVertexEdges(refiner, vertex_index, num_vert_edges);
setNumBaseVertexFaces(refiner, vertex_index, num_vert_faces); setNumBaseVertexFaces(refiner, vertex_index, num_vert_faces);
} }
return true; return true;
} }
@ -137,8 +150,12 @@ template<>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags( inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
TopologyRefiner &refiner, const TopologyRefinerData &cb_data) TopologyRefiner &refiner, const TopologyRefinerData &cb_data)
{ {
using blender::opensubdiv::MeshTopology;
using OpenSubdiv::Sdc::Crease; using OpenSubdiv::Sdc::Crease;
const OpenSubdiv_Converter *converter = cb_data.converter; const OpenSubdiv_Converter *converter = cb_data.converter;
MeshTopology *base_mesh_topology = cb_data.base_mesh_topology;
const bool full_topology_specified = converter->specifiesFullTopology(converter); const bool full_topology_specified = converter->specifiesFullTopology(converter);
if (full_topology_specified || converter->getEdgeVertices != NULL) { if (full_topology_specified || converter->getEdgeVertices != NULL) {
const int num_edges = converter->getNumEdges(converter); const int num_edges = converter->getNumEdges(converter);
@ -151,6 +168,8 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
setBaseEdgeSharpness(refiner, edge_index, sharpness); setBaseEdgeSharpness(refiner, edge_index, sharpness);
} }
else { else {
// TODO(sergey): Should be a faster way to find reconstructed edge to
// specify sharpness for (assuming, findBaseEdge has linear complexity).
int edge_vertices[2]; int edge_vertices[2];
converter->getEdgeVertices(converter, edge_index, edge_vertices); converter->getEdgeVertices(converter, edge_index, edge_vertices);
const int base_edge_index = findBaseEdge(refiner, edge_vertices[0], edge_vertices[1]); const int base_edge_index = findBaseEdge(refiner, edge_vertices[0], edge_vertices[1]);
@ -162,6 +181,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
} }
} }
} }
// OpenSubdiv expects non-manifold vertices to be sharp but at the time it // 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 // 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 // vertices which are adjacent to a loose edge as sharp, but this decision
@ -170,6 +190,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) { for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
ConstIndexArray vertex_edges = getBaseVertexEdges(refiner, vertex_index); ConstIndexArray vertex_edges = getBaseVertexEdges(refiner, vertex_index);
if (converter->isInfiniteSharpVertex(converter, vertex_index)) { if (converter->isInfiniteSharpVertex(converter, vertex_index)) {
base_mesh_topology->setVertexSharpness(vertex_index, Crease::SHARPNESS_INFINITE);
setBaseVertexSharpness(refiner, vertex_index, Crease::SHARPNESS_INFINITE); setBaseVertexSharpness(refiner, vertex_index, Crease::SHARPNESS_INFINITE);
continue; continue;
} }
@ -178,6 +199,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
float sharpness = 0.0f; float sharpness = 0.0f;
if (converter->getVertexSharpness != NULL) { if (converter->getVertexSharpness != NULL) {
sharpness = converter->getVertexSharpness(converter, vertex_index); sharpness = converter->getVertexSharpness(converter, vertex_index);
base_mesh_topology->setVertexSharpness(vertex_index, sharpness);
} }
// If it's vertex where 2 non-manifold edges meet adjust vertex sharpness to // If it's vertex where 2 non-manifold edges meet adjust vertex sharpness to
@ -291,8 +313,11 @@ TopologyRefinerImpl *TopologyRefinerImpl::createFromConverter(
{ {
using OpenSubdiv::Far::TopologyRefiner; using OpenSubdiv::Far::TopologyRefiner;
blender::opensubdiv::MeshTopology base_mesh_topology;
TopologyRefinerData cb_data; TopologyRefinerData cb_data;
cb_data.converter = converter; cb_data.converter = converter;
cb_data.base_mesh_topology = &base_mesh_topology;
// Create OpenSubdiv descriptor for the topology refiner. // Create OpenSubdiv descriptor for the topology refiner.
TopologyRefinerFactoryType::Options topology_refiner_options = getTopologyRefinerOptions( TopologyRefinerFactoryType::Options topology_refiner_options = getTopologyRefinerOptions(
@ -307,6 +332,7 @@ TopologyRefinerImpl *TopologyRefinerImpl::createFromConverter(
TopologyRefinerImpl *topology_refiner_impl = new TopologyRefinerImpl(); TopologyRefinerImpl *topology_refiner_impl = new TopologyRefinerImpl();
topology_refiner_impl->topology_refiner = topology_refiner; topology_refiner_impl->topology_refiner = topology_refiner;
topology_refiner_impl->settings = settings; topology_refiner_impl->settings = settings;
topology_refiner_impl->base_mesh_topology = move(base_mesh_topology);
return topology_refiner_impl; return topology_refiner_impl;
} }

@ -26,6 +26,7 @@
#include <opensubdiv/far/topologyRefiner.h> #include <opensubdiv/far/topologyRefiner.h>
#include "internal/base/memory.h" #include "internal/base/memory.h"
#include "internal/topology/mesh_topology.h"
#include "opensubdiv_topology_refiner_capi.h" #include "opensubdiv_topology_refiner_capi.h"
struct OpenSubdiv_Converter; struct OpenSubdiv_Converter;
@ -46,14 +47,24 @@ class TopologyRefinerImpl {
OpenSubdiv::Far::TopologyRefiner *topology_refiner; OpenSubdiv::Far::TopologyRefiner *topology_refiner;
// Subdivision settingsa this refiner is created for. // Subdivision settingsa this refiner is created for.
//
// We store it here since OpenSubdiv's refiner will only know about level and
// "adaptivity" after performing actual "refine" step.
//
// Ideally, we would also support refining topology without re-importing it
// from external world, but that is for later.
OpenSubdiv_TopologyRefinerSettings settings; OpenSubdiv_TopologyRefinerSettings settings;
// Topology of the mesh which corresponds to the base level.
//
// All the indices and values are kept exactly the same as user-defined
// converter provided them. This allows to easily compare values which might
// be touched by the refinement process.
//
// On a more technical note this allows to easier/faster to compare following
// things:
//
// - Face vertices, where OpenSubdiv could re-arrange them to keep winding
// uniform.
//
// - Vertex crease where OpenSubdiv will force crease for non-manifold or
// corner vertices.
MeshTopology base_mesh_topology;
MEM_CXX_CLASS_ALLOC_FUNCS("TopologyRefinerImpl"); MEM_CXX_CLASS_ALLOC_FUNCS("TopologyRefinerImpl");
}; };