Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
/* SPDX-FileCopyrightText: 2020 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#include "internal/topology/mesh_topology.h"
#include <cassert>
namespace blender::opensubdiv {
MeshTopology::MeshTopology() : num_vertices_(0), num_edges_(0), num_faces_(0) {}
MeshTopology::~MeshTopology() = default;
////////////////////////////////////////////////////////////////////////////////
// Vertices.
void MeshTopology::setNumVertices(int num_vertices)
{
num_vertices_ = num_vertices;
}
int MeshTopology::getNumVertices() const
{
return num_vertices_;
}
void MeshTopology::setVertexSharpness(int vertex_index, float sharpness)
{
assert(vertex_index >= 0);
assert(vertex_index < getNumVertices());
ensureVertexTagsSize(vertex_index + 1);
vertex_tags_[vertex_index].sharpness = sharpness;
}
float MeshTopology::getVertexSharpness(int vertex_index) const
{
assert(vertex_index >= 0);
assert(vertex_index < getNumVertices());
if (vertex_index >= vertex_tags_.size()) {
// Sharpness for the vertex was never provided.
return 0.0f;
}
return vertex_tags_[vertex_index].sharpness;
}
void MeshTopology::ensureVertexTagsSize(int num_vertices)
{
if (vertex_tags_.size() < num_vertices) {
vertex_tags_.resize(num_vertices);
}
}
////////////////////////////////////////////////////////////////////////////////
// Edges.
void MeshTopology::setNumEdges(int num_edges)
{
num_edges_ = num_edges;
}
int MeshTopology::getNumEdges() const
{
return num_edges_;
}
void MeshTopology::setEdgeVertexIndices(int edge_index, int v1, int v2)
{
assert(edge_index >= 0);
assert(edge_index < getNumEdges());
assert(v1 >= 0);
assert(v1 < getNumVertices());
assert(v2 >= 0);
assert(v2 < getNumVertices());
ensureNumEdgesAtLeast(edge_index + 1);
Edge &edge = edges_[edge_index];
edge.v1 = v1;
edge.v2 = v2;
}
void MeshTopology::getEdgeVertexIndices(int edge_index, int *v1, int *v2) const
{
assert(edge_index >= 0);
assert(edge_index < getNumEdges());
if (edge_index >= edges_.size()) {
*v1 = -1;
*v2 = -1;
return;
}
const Edge &edge = edges_[edge_index];
*v1 = edge.v1;
*v2 = edge.v2;
}
bool MeshTopology::isEdgeEqual(int edge_index, int expected_v1, int expected_v2) const
{
assert(edge_index >= 0);
assert(edge_index < getNumEdges());
if (edge_index >= edges_.size()) {
return false;
}
const Edge &edge = edges_[edge_index];
return edge.v1 == expected_v1 && edge.v2 == expected_v2;
}
void MeshTopology::setEdgeSharpness(int edge_index, float sharpness)
{
assert(edge_index >= 0);
assert(edge_index < getNumEdges());
if (sharpness < 1e-6f) {
return;
}
ensureEdgeTagsSize(edge_index + 1);
edge_tags_[edge_index].sharpness = sharpness;
}
float MeshTopology::getEdgeSharpness(int edge_index) const
{
assert(edge_index >= 0);
if (edge_index >= edge_tags_.size()) {
// NOTE: It's possible that full topology is not known and that there was
// never sharpness assigned to any of the edges.
return 0.0f;
}
return edge_tags_[edge_index].sharpness;
}
void MeshTopology::ensureNumEdgesAtLeast(int num_edges)
{
if (edges_.size() < num_edges) {
edges_.resize(num_edges);
}
}
void MeshTopology::ensureEdgeTagsSize(int num_edges)
{
if (edge_tags_.size() < num_edges) {
edge_tags_.resize(num_edges);
}
}
////////////////////////////////////////////////////////////////////////////////
// Faces.
void MeshTopology::setNumFaces(int num_faces)
{
num_faces_ = num_faces;
// NOTE: Extra element to store fake face past the last real one to make it
// possible to calculate number of vertices in the last face.
faces_first_vertex_index_.resize(num_faces + 1, 0);
}
int MeshTopology::getNumFaces() const
{
return num_faces_;
}
void MeshTopology::setNumFaceVertices(int face_index, int num_face_vertices)
{
assert(face_index >= 0);
assert(face_index < getNumFaces());
faces_first_vertex_index_[face_index + 1] = faces_first_vertex_index_[face_index] +
num_face_vertices;
}
int MeshTopology::getNumFaceVertices(int face_index) const
{
assert(face_index >= 0);
assert(face_index < getNumFaces());
return faces_first_vertex_index_[face_index + 1] - faces_first_vertex_index_[face_index];
}
void MeshTopology::setFaceVertexIndices(int face_index,
int num_face_vertex_indices,
const int *face_vertex_indices)
{
assert(face_index >= 0);
assert(face_index < getNumFaces());
assert(num_face_vertex_indices == getNumFaceVertices(face_index));
int *face_vertex_indices_storage = getFaceVertexIndicesStorage(face_index);
memcpy(face_vertex_indices_storage, face_vertex_indices, sizeof(int) * num_face_vertex_indices);
}
bool MeshTopology::isFaceVertexIndicesEqual(int face_index,
int num_expected_face_vertex_indices,
const int *expected_face_vertex_indices) const
{
assert(face_index >= 0);
assert(face_index < getNumFaces());
if (getNumFaceVertices(face_index) != num_expected_face_vertex_indices) {
return false;
}
const int *face_vertex_indices_storage = getFaceVertexIndicesStorage(face_index);
return memcmp(face_vertex_indices_storage,
expected_face_vertex_indices,
sizeof(int) * num_expected_face_vertex_indices) == 0;
}
bool MeshTopology::isFaceVertexIndicesEqual(
int face_index, const std::vector<int> &expected_face_vertex_indices) const
{
return isFaceVertexIndicesEqual(
face_index, expected_face_vertex_indices.size(), expected_face_vertex_indices.data());
}
int *MeshTopology::getFaceVertexIndicesStorage(int face_index)
{
const MeshTopology *const_this = this;
return const_cast<int *>(const_this->getFaceVertexIndicesStorage(face_index));
}
const int *MeshTopology::getFaceVertexIndicesStorage(int face_index) const
{
assert(face_index >= 0);
assert(face_index < getNumFaces());
const int offset = faces_first_vertex_index_[face_index];
return face_vertex_indices_.data() + offset;
}
////////////////////////////////////////////////////////////////////////////////
// Pipeline related.
void MeshTopology::finishResizeTopology()
{
if (!faces_first_vertex_index_.empty()) {
face_vertex_indices_.resize(faces_first_vertex_index_.back());
}
}
} // namespace blender::opensubdiv

View File

@@ -0,0 +1,168 @@
/* SPDX-FileCopyrightText: 2020 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#ifndef OPENSUBDIV_MESH_TOPOLOGY_H_
#define OPENSUBDIV_MESH_TOPOLOGY_H_
#include <vector>
#include "internal/base/memory.h"
struct OpenSubdiv_Converter;
namespace blender::opensubdiv {
// Simplified representation of mesh topology.
// Only includes parts of actual mesh topology which is needed to perform
// comparison between Application side and OpenSubddiv side.
//
// NOTE: It is an optimized storage which requires special order of topology
// specification. Basically, counters is to be set prior to anything else, in
// the following manner:
//
// MeshTopology mesh_topology;
//
// mesh_topology.setNumVertices(...);
// mesh_topology.setNumEdges(...);
// mesh_topology.setNumFaces(...);
//
// for (...) {
// mesh_topology.setNumFaceVertices(...);
// }
//
// mesh_topology.finishResizeTopology();
//
// /* it is now possible to set vertices of edge, vertices of face, and
// * sharpness. */
class MeshTopology {
public:
MeshTopology();
MeshTopology(const MeshTopology &other) = default;
MeshTopology(MeshTopology &&other) noexcept = default;
~MeshTopology();
MeshTopology &operator=(const MeshTopology &other) = default;
MeshTopology &operator=(MeshTopology &&other) = default;
//////////////////////////////////////////////////////////////////////////////
// Vertices.
void setNumVertices(int num_vertices);
int getNumVertices() const;
void setVertexSharpness(int vertex_index, float sharpness);
float getVertexSharpness(int vertex_index) const;
//////////////////////////////////////////////////////////////////////////////
// Edges.
void setNumEdges(int num_edges);
// NOTE: Unless full topology was specified will return number of edges based
// on last edge index for which topology tag was specified.
int getNumEdges() const;
void setEdgeVertexIndices(int edge_index, int v1, int v2);
void getEdgeVertexIndices(int edge_index, int *v1, int *v2) const;
bool isEdgeEqual(int edge_index, int expected_v1, int expected_v2) const;
void setEdgeSharpness(int edge_index, float sharpness);
float getEdgeSharpness(int edge_index) const;
//////////////////////////////////////////////////////////////////////////////
// Faces.
void setNumFaces(int num_faces);
int getNumFaces() const;
void setNumFaceVertices(int face_index, int num_face_vertices);
int getNumFaceVertices(int face_index) const;
void setFaceVertexIndices(int face_index,
int num_face_vertex_indices,
const int *face_vertex_indices);
bool isFaceVertexIndicesEqual(int face_index,
int num_expected_face_vertex_indices,
const int *expected_face_vertex_indices) const;
bool isFaceVertexIndicesEqual(int face_index,
const std::vector<int> &expected_face_vertex_indices) const;
//////////////////////////////////////////////////////////////////////////////
// Pipeline related.
// This function is to be called when number of vertices, edges, faces, and
// face-vertices are known.
//
// Usually is called from the end of topology refiner factory's
// resizeComponentTopology().
void finishResizeTopology();
//////////////////////////////////////////////////////////////////////////////
// Comparison.
// Compare given topology with converter. Returns truth if topology
// matches given converter, false otherwise.
//
// This allows users to construct converter (which is supposed to be cheap)
// and compare with existing topology before going into more computationally
// complicated parts of subdivision process.
bool isEqualToConverter(const OpenSubdiv_Converter *converter) const;
protected:
// Edges are allowed to be stored sparsly, to save memory used by
// non-semi-sharp edges.
void ensureNumEdgesAtLeast(int num_edges);
// Geometry tags are stored sparsly.
//
// These functions ensures that the storage can be addressed by an index which
// corresponds to the given size.
void ensureVertexTagsSize(int num_vertices);
void ensureEdgeTagsSize(int num_edges);
// Get pointer to the memory where face vertex indices are stored.
int *getFaceVertexIndicesStorage(int face_index);
const int *getFaceVertexIndicesStorage(int face_index) const;
struct VertexTag {
float sharpness = 0.0f;
};
struct Edge {
int v1 = -1;
int v2 = -1;
};
struct EdgeTag {
float sharpness = 0.0f;
};
int num_vertices_;
std::vector<VertexTag> vertex_tags_;
int num_edges_;
std::vector<Edge> edges_;
std::vector<EdgeTag> edge_tags_;
int num_faces_;
// Continuous array of all vertices of all faces:
// [vertex indices of face 0][vertex indices of face 1] .. [vertex indices of face n].
std::vector<int> face_vertex_indices_;
// Indexed by face contains index within face_vertex_indices_ which corresponds
// to the element which contains first vertex of the face.
std::vector<int> faces_first_vertex_index_;
MEM_CXX_CLASS_ALLOC_FUNCS("MeshTopology");
};
} // namespace blender::opensubdiv
#endif // OPENSUBDIV_MESH_TOPOLOGY_H_

View File

@@ -0,0 +1,229 @@
/* SPDX-FileCopyrightText: 2020 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#include "internal/topology/mesh_topology.h"
#include <cassert>
#include <cstring>
#include <opensubdiv/sdc/crease.h>
#include <vector>
#include "opensubdiv_converter_capi.hh"
namespace blender::opensubdiv {
////////////////////////////////////////////////////////////////////////////////
// Quick preliminary checks.
static int getEffectiveNumEdges(const OpenSubdiv_Converter *converter)
{
if (converter->getNumEdges == nullptr) {
return 0;
}
return converter->getNumEdges(converter);
}
static bool isEqualGeometryCounters(const MeshTopology &mesh_topology,
const OpenSubdiv_Converter *converter)
{
if (converter->getNumVertices(converter) != mesh_topology.getNumVertices()) {
return false;
}
if (converter->faces.size() != mesh_topology.getNumFaces()) {
return false;
}
if (getEffectiveNumEdges(converter) != mesh_topology.getNumEdges()) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Geometry.
// Edges.
static bool isEqualGeometryEdge(const MeshTopology &mesh_topology,
const OpenSubdiv_Converter *converter)
{
const int num_requested_edges = getEffectiveNumEdges(converter);
if (num_requested_edges != mesh_topology.getNumEdges()) {
return false;
}
// NOTE: Ignoring the sharpness we don't really care of the content of the
// edges, they should be in the consistent state with faces and face-vertices.
// If that's not the case the mesh is invalid and comparison can not happen
// reliably.
//
// For sharpness it is important to know that edges are connecting same pair
// of vertices. But since sharpness is stored sparesly the connectivity will
// be checked when comparing edge sharpness.
return true;
}
// Faces.
static bool isEqualGeometryFace(const MeshTopology &mesh_topology,
const OpenSubdiv_Converter *converter)
{
const int num_requested_faces = converter->faces.size();
if (num_requested_faces != mesh_topology.getNumFaces()) {
return false;
}
std::vector<int> vertices_of_face;
for (int face_index = 0; face_index < num_requested_faces; ++face_index) {
int num_face_vertices = converter->faces[face_index].size();
if (mesh_topology.getNumFaceVertices(face_index) != num_face_vertices) {
return false;
}
vertices_of_face.resize(num_face_vertices);
converter->getFaceVertices(converter, face_index, vertices_of_face.data());
if (!mesh_topology.isFaceVertexIndicesEqual(face_index, vertices_of_face)) {
return false;
}
}
return true;
}
// Geometry comparison entry point.
static bool isEqualGeometry(const MeshTopology &mesh_topology,
const OpenSubdiv_Converter *converter)
{
if (!isEqualGeometryEdge(mesh_topology, converter)) {
return false;
}
if (!isEqualGeometryFace(mesh_topology, converter)) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Geometry tags.
// Vertices.
// TODO(sergey): Make this function usable by factory as well.
static 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;
}
static bool isEqualVertexTags(const MeshTopology &mesh_topology,
const OpenSubdiv_Converter *converter)
{
const int num_vertices = mesh_topology.getNumVertices();
for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
const float current_sharpness = mesh_topology.getVertexSharpness(vertex_index);
const float requested_sharpness = getEffectiveVertexSharpness(converter, vertex_index);
if (current_sharpness != requested_sharpness) {
return false;
}
}
return true;
}
// Edges.
// TODO(sergey): Make this function usable by factory as well.
static float getEffectiveEdgeSharpness(const OpenSubdiv_Converter *converter, const int edge_index)
{
if (converter->getEdgeSharpness != nullptr) {
return converter->getEdgeSharpness(converter, edge_index);
}
return 0.0f;
}
static bool isEqualEdgeTags(const MeshTopology &mesh_topology,
const OpenSubdiv_Converter *converter)
{
const int num_edges = mesh_topology.getNumEdges();
for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
const float current_sharpness = mesh_topology.getEdgeSharpness(edge_index);
const float requested_sharpness = getEffectiveEdgeSharpness(converter, edge_index);
if (current_sharpness != requested_sharpness) {
return false;
}
if (current_sharpness < 1e-6f) {
continue;
}
int requested_edge_vertices[2];
converter->getEdgeVertices(converter, edge_index, requested_edge_vertices);
if (!mesh_topology.isEdgeEqual(
edge_index, requested_edge_vertices[0], requested_edge_vertices[1]))
{
return false;
}
}
return true;
}
// Tags comparison entry point.
static bool isEqualTags(const MeshTopology &mesh_topology, const OpenSubdiv_Converter *converter)
{
if (!isEqualVertexTags(mesh_topology, converter)) {
return false;
}
if (!isEqualEdgeTags(mesh_topology, converter)) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Entry point.
bool MeshTopology::isEqualToConverter(const OpenSubdiv_Converter *converter) const
{
// Preliminary checks.
if (!isEqualGeometryCounters(*this, converter)) {
return false;
}
// Geometry.
if (!isEqualGeometry(*this, converter)) {
return false;
}
// Tags.
if (!isEqualTags(*this, converter)) {
return false;
}
return true;
}
} // namespace blender::opensubdiv

View File

@@ -0,0 +1,84 @@
/* SPDX-FileCopyrightText: 2020 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#include "internal/topology/mesh_topology.h"
#include "testing/testing.h"
namespace blender::opensubdiv {
TEST(MeshTopology, TrivialVertexSharpness)
{
MeshTopology mesh_topology;
mesh_topology.setNumVertices(3);
mesh_topology.finishResizeTopology();
mesh_topology.setVertexSharpness(0, 0.1f);
mesh_topology.setVertexSharpness(1, 0.2f);
EXPECT_EQ(mesh_topology.getVertexSharpness(0), 0.1f);
EXPECT_EQ(mesh_topology.getVertexSharpness(1), 0.2f);
EXPECT_EQ(mesh_topology.getVertexSharpness(2), 0.0f);
}
TEST(MeshTopology, TrivialEdgeSharpness)
{
MeshTopology mesh_topology;
mesh_topology.setNumVertices(8);
mesh_topology.setNumEdges(3);
mesh_topology.finishResizeTopology();
mesh_topology.setEdgeVertexIndices(0, 0, 1);
mesh_topology.setEdgeVertexIndices(1, 1, 2);
mesh_topology.setEdgeVertexIndices(2, 2, 3);
mesh_topology.setEdgeSharpness(0, 0.1f);
mesh_topology.setEdgeSharpness(2, 0.2f);
EXPECT_EQ(mesh_topology.getEdgeSharpness(0), 0.1f);
EXPECT_EQ(mesh_topology.getEdgeSharpness(1), 0.0f);
EXPECT_EQ(mesh_topology.getEdgeSharpness(2), 0.2f);
}
TEST(MeshTopology, TrivialFaceTopology)
{
MeshTopology mesh_topology;
mesh_topology.setNumFaces(3);
mesh_topology.setNumFaceVertices(0, 4);
mesh_topology.setNumFaceVertices(1, 3);
mesh_topology.setNumFaceVertices(2, 5);
mesh_topology.finishResizeTopology();
EXPECT_EQ(mesh_topology.getNumFaceVertices(0), 4);
EXPECT_EQ(mesh_topology.getNumFaceVertices(1), 3);
EXPECT_EQ(mesh_topology.getNumFaceVertices(2), 5);
{
const int vertex_indices[] = {0, 1, 2, 3};
mesh_topology.setFaceVertexIndices(0, 4, vertex_indices);
}
{
const int vertex_indices[] = {4, 5, 6};
mesh_topology.setFaceVertexIndices(1, 3, vertex_indices);
}
{
const int vertex_indices[] = {7, 8, 9, 10, 11};
mesh_topology.setFaceVertexIndices(2, 5, vertex_indices);
}
EXPECT_TRUE(mesh_topology.isFaceVertexIndicesEqual(0, {{0, 1, 2, 3}}));
EXPECT_FALSE(mesh_topology.isFaceVertexIndicesEqual(0, {{10, 1, 2, 3}}));
EXPECT_FALSE(mesh_topology.isFaceVertexIndicesEqual(0, {{0, 1, 2}}));
EXPECT_TRUE(mesh_topology.isFaceVertexIndicesEqual(1, {{4, 5, 6}}));
EXPECT_TRUE(mesh_topology.isFaceVertexIndicesEqual(2, {{7, 8, 9, 10, 11}}));
}
} // namespace blender::opensubdiv

View File

@@ -0,0 +1,363 @@
/* SPDX-FileCopyrightText: 2015 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#ifdef _MSC_VER
# include <iso646.h>
#endif
#include <cassert>
#include <cstdio>
#include <opensubdiv/far/topologyRefinerFactory.h>
#include "internal/base/type_convert.h"
#include "internal/topology/mesh_topology.h"
#include "opensubdiv_converter_capi.hh"
#include "opensubdiv_topology_refiner.hh"
struct TopologyRefinerData {
const OpenSubdiv_Converter *converter;
blender::opensubdiv::MeshTopology *base_mesh_topology;
};
using TopologyRefinerFactoryType = OpenSubdiv::Far::TopologyRefinerFactory<TopologyRefinerData>;
namespace OpenSubdiv::OPENSUBDIV_VERSION::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 blender::OffsetIndices<int> src_faces = converter->faces;
base_mesh_topology->setNumFaces(src_faces.size());
setNumBaseFaces(refiner, src_faces.size());
for (const int face_index : src_faces.index_range()) {
const int num_face_vertices = src_faces[face_index].size();
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);
const blender::OffsetIndices<int> src_faces = converter->faces;
// Vertices of face.
for (const int face_index : src_faces.index_range()) {
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);
std::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.data());
// 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.data());
memcpy(&dst_vertex_edges[0], vertex_edges.data(), sizeof(int) * num_vertex_edges);
memcpy(&dst_vertex_faces[0], vertex_faces.data(), 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 (const int face_index : src_faces.index_range()) {
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 != nullptr) {
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 != nullptr) {
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 += std::min(sharpness0, sharpness1);
sharpness = std::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 == nullptr) {
assert(converter->precalcUVLayer == nullptr);
assert(converter->getNumUVCoordinates == nullptr);
assert(converter->getFaceCornerUVIndex == nullptr);
assert(converter->finishUVLayer == nullptr);
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 OpenSubdiv::OPENSUBDIV_VERSION::Far
namespace blender::opensubdiv {
static 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;
}
static 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;
}
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 = std::move(base_mesh_topology);
return topology_refiner_impl;
}
} // namespace blender::opensubdiv

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2016 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#include "opensubdiv_topology_refiner.hh"
namespace blender::opensubdiv {
TopologyRefinerImpl::TopologyRefinerImpl() : topology_refiner(nullptr) {}
TopologyRefinerImpl::~TopologyRefinerImpl()
{
delete topology_refiner;
}
} // namespace blender::opensubdiv

View File

@@ -0,0 +1,133 @@
/* SPDX-FileCopyrightText: 2018 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#include "internal/base/type_convert.h"
#include "internal/topology/mesh_topology.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->getSchemeType(converter));
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->getFVarLinearInterpolation(converter));
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);
}
////////////////////////////////////////////////////////////////////////////////
// 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 checkSingleUVLayerMatch(const OpenSubdiv::Far::TopologyLevel &base_level,
const OpenSubdiv_Converter *converter,
const int layer_index)
{
converter->precalcUVLayer(converter, layer_index);
const int num_faces = base_level.GetNumFaces();
// 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) {
OpenSubdiv::Far::ConstIndexArray base_level_face_uvs = base_level.GetFaceFVarValues(
face_index, layer_index);
for (int corner = 0; corner < base_level_face_uvs.size(); ++corner) {
const int uv_index = converter->getFaceCornerUVIndex(converter, face_index, corner);
if (base_level_face_uvs[corner] != uv_index) {
converter->finishUVLayer(converter);
return false;
}
}
}
converter->finishUVLayer(converter);
return true;
}
static bool checkUVLayersMatch(const TopologyRefinerImpl *topology_refiner_impl,
const OpenSubdiv_Converter *converter)
{
using OpenSubdiv::Far::TopologyLevel;
const int num_layers = converter->getNumUVLayers(converter);
const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner_impl);
// Number of UV layers should match.
if (base_level.GetNumFVarChannels() != num_layers) {
return false;
}
for (int layer_index = 0; layer_index < num_layers; ++layer_index) {
if (!checkSingleUVLayerMatch(base_level, converter, layer_index)) {
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 (!base_mesh_topology.isEqualToConverter(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