Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user