blender/intern/opensubdiv/internal/topology/topology_refiner_impl_compare.cc
Hans Goudey eeb7c04995 Subdiv: Simplify converter UV map API
Instead of callbacks to retrieve information about specific UV layers,
pre-calculate data and store it in a more "raw" form in the converter
struct. This is the last place the callbacks are actually used.

Part of #130917.

Pull Request: https://projects.blender.org/blender/blender/pulls/164916
2026-10-09 02:49:25 +02:00

167 lines
6.3 KiB
C++

/* SPDX-FileCopyrightText: 2018 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#include <algorithm>
#include <utility>
#include <vector>
#include "internal/base/type_convert.h"
#include "opensubdiv_converter_capi.hh"
#include "opensubdiv_topology_refiner.hh"
namespace blender::opensubdiv {
static const OpenSubdiv::Far::TopologyRefiner *getOSDTopologyRefiner(
const TopologyRefinerImpl *topology_refiner_impl)
{
return topology_refiner_impl->topology_refiner;
}
static const OpenSubdiv::Far::TopologyLevel &getOSDTopologyBaseLevel(
const TopologyRefinerImpl *topology_refiner_impl)
{
return getOSDTopologyRefiner(topology_refiner_impl)->GetLevel(0);
}
////////////////////////////////////////////////////////////////////////////////
// Quick preliminary checks.
static bool checkSchemeTypeMatches(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
const OpenSubdiv::Sdc::SchemeType converter_scheme_type =
blender::opensubdiv::getSchemeTypeFromCAPI(converter->scheme_type);
return (converter_scheme_type == getOSDTopologyRefiner(topology_refiner_impl)->GetSchemeType());
}
static bool checkOptionsMatches(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
using Options = OpenSubdiv::Sdc::Options;
const Options options = getOSDTopologyRefiner(topology_refiner_impl)->GetSchemeOptions();
const Options::FVarLinearInterpolation fvar_interpolation = options.GetFVarLinearInterpolation();
const Options::FVarLinearInterpolation converter_fvar_interpolation =
blender::opensubdiv::getFVarLinearInterpolationFromCAPI(
converter->fvar_linear_interpolation);
if (fvar_interpolation != converter_fvar_interpolation) {
return false;
}
return true;
}
static bool checkPreliminaryMatches(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
return checkSchemeTypeMatches(topology_refiner_impl, converter) &&
checkOptionsMatches(topology_refiner_impl, converter);
}
////////////////////////////////////////////////////////////////////////////////
// Base mesh topology.
static bool checkBaseMeshTopologyMatches(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
if (topology_refiner_impl->base_verts_num != converter->verts_num) {
return false;
}
if (!std::ranges::equal(converter->face_offsets, topology_refiner_impl->base_face_offsets)) {
return false;
}
if (!std::ranges::equal(converter->corner_verts, topology_refiner_impl->base_corner_verts)) {
return false;
}
if (!std::ranges::equal(converter->edge_sharpness, topology_refiner_impl->base_edge_sharpness)) {
return false;
}
if (!std::ranges::equal(converter->vert_sharpness, topology_refiner_impl->base_vert_sharpness)) {
return false;
}
// NOTE: Ignoring the sharpness we don't really care about the content of the edges, they should
// be in the consistent state with the faces and face-vertices compared above. If that's not the
// case the mesh is invalid and comparison can not happen reliably. For sharpness it is
// important to know that the edges still connect the same pair of vertices though.
const std::vector<std::pair<int, int>> &base_edges = topology_refiner_impl->base_edges_sparse;
for (size_t edge_index = 0; edge_index < base_edges.size(); edge_index++) {
if (topology_refiner_impl->base_edge_sharpness[edge_index] < 1e-6f) {
continue;
}
if (base_edges[edge_index] != converter->edges[edge_index]) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Compare attributes which affects on topology.
//
// TODO(sergey): Need to look into how auto-winding affects on face-varying
// indexing and, possibly, move to mesh topology as well if winding affects
// face-varyign as well.
static bool checkUVLayersMatch(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
using OpenSubdiv::Far::ConstIndexArray;
using OpenSubdiv::Far::TopologyLevel;
const std::span<const OpenSubdiv_Converter::UVLayer> uv_layers = converter->uv_layers;
const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner_impl);
// Number of UV layers should match.
if (base_level.GetNumFVarChannels() != int(uv_layers.size())) {
return false;
}
const std::span<const int> face_offsets = converter->face_offsets;
const int num_faces = base_level.GetNumFaces();
for (int channel = 0; channel < int(uv_layers.size()); ++channel) {
const OpenSubdiv_Converter::UVLayer &uv_layer = uv_layers[channel];
if (base_level.GetNumFVarValues(channel) != uv_layer.uvs_num) {
return false;
}
// 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) {
const ConstIndexArray base_level_face_uvs = base_level.GetFaceFVarValues(face_index,
channel);
const std::span<const int> face_uvs = uv_layer.corner_uv_indices.subspan(
face_offsets[face_index], base_level_face_uvs.size());
if (!std::ranges::equal(base_level_face_uvs, face_uvs)) {
return false;
}
}
}
return true;
}
static bool checkTopologyAttributesMatch(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
return checkUVLayersMatch(topology_refiner_impl, converter);
}
bool TopologyRefinerImpl::isEqualToConverter(const OpenSubdiv_Converter *converter) const
{
if (!blender::opensubdiv::checkPreliminaryMatches(this, converter)) {
return false;
}
if (!checkBaseMeshTopologyMatches(this, converter)) {
return false;
}
// NOTE: Do after geometry check, to be sure topology does match and all
// indexing will go fine.
if (!blender::opensubdiv::checkTopologyAttributesMatch(this, converter)) {
return false;
}
return true;
}
} // namespace blender::opensubdiv