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
Notes: blender-bot 2023-02-14 03:00:45 +01:00
Referenced by issue #76855, Subdiv: Incorrect topology comparison, leading to poor performance
Referenced by issue #71908, Keyframed parameters are not preserved when frame_change_post handler is used
3 changed files with 68 additions and 53 deletions

View File

@ -22,6 +22,7 @@
#include "internal/base/edge_map.h"
#include "internal/base/type.h"
#include "internal/base/type_convert.h"
#include "internal/topology/mesh_topology.h"
#include "internal/topology/topology_refiner_impl.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)
{
using OpenSubdiv::Far::ConstIndexArray;
using OpenSubdiv::Far::TopologyLevel;
using OpenSubdiv::Sdc::Crease;
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();
const MeshTopology &base_mesh_topology = topology_refiner->impl->base_mesh_topology;
const int num_vertices = base_mesh_topology.getNumVertices();
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
const float current_sharpness = base_level.GetVertexSharpness(vertex_index);
if (converter->isInfiniteSharpVertex(converter, vertex_index)) {
if (current_sharpness != Crease::SHARPNESS_INFINITE) {
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;
}
const float current_sharpness = base_mesh_topology.vertices[vertex_index].sharpness;
const float requested_sharpness = getEffectiveVertexSharpness(converter, vertex_index);
if (current_sharpness != requested_sharpness) {
return false;
}
}
return true;
@ -652,7 +630,7 @@ bool checkTopologyAttributesMatch(const OpenSubdiv_TopologyRefiner *topology_ref
const OpenSubdiv_Converter *converter)
{
return checkEdgeTagsMatch(topology_refiner, converter) &&
checkvertexSharpnessMatch(topology_refiner, converter) &&
checkVertexSharpnessMatch(topology_refiner, converter) &&
checkUVLayersMatch(topology_refiner, converter);
}

View File

@ -29,6 +29,8 @@
#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;
@ -37,6 +39,7 @@ using blender::opensubdiv::vector;
struct TopologyRefinerData {
const OpenSubdiv_Converter *converter;
blender::opensubdiv::MeshTopology *base_mesh_topology;
};
typedef OpenSubdiv::Far::TopologyRefinerFactory<TopologyRefinerData> TopologyRefinerFactoryType;
@ -49,7 +52,11 @@ 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;
// Faces and face-vertices.
const int num_faces = converter->getNumFaces(converter);
setNumBaseFaces(refiner, num_faces);
@ -57,13 +64,17 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology
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);
base_mesh_topology->setNumVertices(num_vertices);
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);
@ -71,6 +82,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology
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);
@ -78,6 +90,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology
setNumBaseVertexEdges(refiner, vertex_index, num_vert_edges);
setNumBaseVertexFaces(refiner, vertex_index, num_vert_faces);
}
return true;
}
@ -137,8 +150,12 @@ 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);
@ -151,6 +168,8 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
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).
int edge_vertices[2];
converter->getEdgeVertices(converter, edge_index, edge_vertices);
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
// 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
@ -170,6 +190,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
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;
}
@ -178,6 +199,7 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
float sharpness = 0.0f;
if (converter->getVertexSharpness != NULL) {
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
@ -291,8 +313,11 @@ TopologyRefinerImpl *TopologyRefinerImpl::createFromConverter(
{
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(
@ -307,6 +332,7 @@ TopologyRefinerImpl *TopologyRefinerImpl::createFromConverter(
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;
}

View File

@ -26,6 +26,7 @@
#include <opensubdiv/far/topologyRefiner.h>
#include "internal/base/memory.h"
#include "internal/topology/mesh_topology.h"
#include "opensubdiv_topology_refiner_capi.h"
struct OpenSubdiv_Converter;
@ -46,14 +47,24 @@ class TopologyRefinerImpl {
OpenSubdiv::Far::TopologyRefiner *topology_refiner;
// 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;
// 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");
};