2015-07-20 13:18:35 +00:00
|
|
|
/*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2015 Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s): Sergey Sharybin.
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
# include "iso646.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <opensubdiv/far/topologyRefinerFactory.h>
|
|
|
|
|
2016-07-19 14:12:18 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
#include "opensubdiv_converter_capi.h"
|
|
|
|
#include "opensubdiv_intern.h"
|
2016-07-19 14:12:18 +00:00
|
|
|
#include "opensubdiv_topology_refiner.h"
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
|
2015-07-29 15:10:13 +00:00
|
|
|
#include <stack>
|
|
|
|
|
2015-08-06 07:49:13 +00:00
|
|
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
inline void reverse_face_verts(int *face_verts, int num_verts)
|
|
|
|
{
|
|
|
|
int last_vert = face_verts[num_verts - 1];
|
|
|
|
for (int i = num_verts - 1; i > 0; --i) {
|
|
|
|
face_verts[i] = face_verts[i - 1];
|
|
|
|
}
|
|
|
|
face_verts[0] = last_vert;
|
|
|
|
}
|
|
|
|
|
2016-07-21 10:33:11 +00:00
|
|
|
struct TopologyRefinerData {
|
|
|
|
const OpenSubdiv_Converter& conv;
|
|
|
|
std::vector<float> *uvs;
|
|
|
|
};
|
|
|
|
|
2015-08-06 07:49:13 +00:00
|
|
|
} /* namespace */
|
|
|
|
#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
namespace OpenSubdiv {
|
|
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
namespace Far {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline int findInArray(T array, int value)
|
|
|
|
{
|
|
|
|
return (int)(std::find(array.begin(), array.end(), value) - array.begin());
|
|
|
|
}
|
|
|
|
|
2015-08-03 11:20:41 +00:00
|
|
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
2015-08-06 07:49:13 +00:00
|
|
|
inline int get_loop_winding(int vert0_of_face, int vert1_of_face)
|
|
|
|
{
|
|
|
|
int delta_face = vert1_of_face - vert0_of_face;
|
|
|
|
if (abs(delta_face) != 1) {
|
|
|
|
if (delta_face > 0) {
|
|
|
|
delta_face = -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
delta_face = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return delta_face;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void reverse_face_loops(IndexArray face_verts, IndexArray face_edges)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < face_verts.size() / 2; ++i) {
|
|
|
|
int j = face_verts.size() - i - 1;
|
|
|
|
if (i != j) {
|
|
|
|
std::swap(face_verts[i], face_verts[j]);
|
|
|
|
std::swap(face_edges[i], face_edges[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reverse_face_verts(&face_verts[0], face_verts.size());
|
|
|
|
}
|
|
|
|
|
2015-08-03 11:20:41 +00:00
|
|
|
inline void check_oriented_vert_connectivity(const int num_vert_edges,
|
|
|
|
const int num_vert_faces,
|
|
|
|
const int *vert_edges,
|
|
|
|
const int *vert_faces,
|
|
|
|
const int *dst_vert_edges,
|
|
|
|
const int *dst_vert_faces)
|
|
|
|
{
|
|
|
|
# ifndef NDEBUG
|
|
|
|
for (int i = 0; i < num_vert_faces; ++i) {
|
|
|
|
bool found = false;
|
|
|
|
for (int j = 0; j < num_vert_faces; ++j) {
|
|
|
|
if (vert_faces[i] == dst_vert_faces[j]) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
assert(!"vert-faces connectivity ruined");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < num_vert_edges; ++i) {
|
|
|
|
bool found = false;
|
|
|
|
for (int j = 0; j < num_vert_edges; ++j) {
|
|
|
|
if (vert_edges[i] == dst_vert_edges[j]) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
assert(!"vert-edges connectivity ruined");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# else
|
|
|
|
(void)num_vert_edges;
|
|
|
|
(void)num_vert_faces;
|
|
|
|
(void)vert_edges;
|
|
|
|
(void)vert_faces;
|
|
|
|
(void)dst_vert_edges;
|
|
|
|
(void)dst_vert_faces;
|
|
|
|
# endif
|
|
|
|
}
|
|
|
|
#endif
|
2015-07-20 13:18:35 +00:00
|
|
|
|
2015-08-03 11:20:41 +00:00
|
|
|
} /* namespace */
|
2015-07-29 15:10:13 +00:00
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
template <>
|
2016-07-21 10:33:11 +00:00
|
|
|
inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology(
|
2015-07-20 13:18:35 +00:00
|
|
|
TopologyRefiner& refiner,
|
2016-07-21 10:33:11 +00:00
|
|
|
const TopologyRefinerData& cb_data)
|
2015-07-20 13:18:35 +00:00
|
|
|
{
|
2016-07-21 10:33:11 +00:00
|
|
|
const OpenSubdiv_Converter& conv = cb_data.conv;
|
2015-07-20 13:18:35 +00:00
|
|
|
/* Faces and face-verts */
|
|
|
|
const int num_faces = conv.get_num_faces(&conv);
|
|
|
|
setNumBaseFaces(refiner, num_faces);
|
|
|
|
for (int face = 0; face < num_faces; ++face) {
|
|
|
|
const int num_verts = conv.get_num_face_verts(&conv, face);
|
|
|
|
setNumBaseFaceVertices(refiner, face, num_verts);
|
|
|
|
}
|
|
|
|
/* Edges and edge-faces. */
|
|
|
|
const int num_edges = conv.get_num_edges(&conv);
|
|
|
|
setNumBaseEdges(refiner, num_edges);
|
|
|
|
for (int edge = 0; edge < num_edges; ++edge) {
|
|
|
|
const int num_edge_faces = conv.get_num_edge_faces(&conv, edge);
|
|
|
|
setNumBaseEdgeFaces(refiner, edge, num_edge_faces);
|
|
|
|
}
|
|
|
|
/* Vertices and vert-faces and vert-edges/ */
|
|
|
|
const int num_verts = conv.get_num_verts(&conv);
|
|
|
|
setNumBaseVertices(refiner, num_verts);
|
|
|
|
for (int vert = 0; vert < num_verts; ++vert) {
|
|
|
|
const int num_vert_edges = conv.get_num_vert_edges(&conv, vert),
|
|
|
|
num_vert_faces = conv.get_num_vert_faces(&conv, vert);
|
|
|
|
setNumBaseVertexEdges(refiner, vert, num_vert_edges);
|
|
|
|
setNumBaseVertexFaces(refiner, vert, num_vert_faces);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
2016-07-21 10:33:11 +00:00
|
|
|
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
|
2015-07-20 13:18:35 +00:00
|
|
|
TopologyRefiner& refiner,
|
2016-07-21 10:33:11 +00:00
|
|
|
const TopologyRefinerData &cb_data)
|
2015-07-20 13:18:35 +00:00
|
|
|
{
|
2016-07-21 10:33:11 +00:00
|
|
|
const OpenSubdiv_Converter& conv = cb_data.conv;
|
2015-07-20 13:18:35 +00:00
|
|
|
using Far::IndexArray;
|
|
|
|
/* Face relations. */
|
|
|
|
const int num_faces = conv.get_num_faces(&conv);
|
|
|
|
for (int face = 0; face < num_faces; ++face) {
|
|
|
|
IndexArray dst_face_verts = getBaseFaceVertices(refiner, face);
|
|
|
|
conv.get_face_verts(&conv, face, &dst_face_verts[0]);
|
|
|
|
IndexArray dst_face_edges = getBaseFaceEdges(refiner, face);
|
|
|
|
conv.get_face_edges(&conv, face, &dst_face_edges[0]);
|
|
|
|
}
|
|
|
|
/* Edge relations. */
|
|
|
|
const int num_edges = conv.get_num_edges(&conv);
|
|
|
|
for (int edge = 0; edge < num_edges; ++edge) {
|
|
|
|
/* Edge-vertices */
|
|
|
|
IndexArray dst_edge_verts = getBaseEdgeVertices(refiner, edge);
|
|
|
|
conv.get_edge_verts(&conv, edge, &dst_edge_verts[0]);
|
|
|
|
/* Edge-faces */
|
|
|
|
IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge);
|
|
|
|
conv.get_edge_faces(&conv, edge, &dst_edge_faces[0]);
|
|
|
|
}
|
2015-08-03 11:20:41 +00:00
|
|
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
2015-08-06 07:49:13 +00:00
|
|
|
/* Make face normals consistent. */
|
2015-08-03 11:20:41 +00:00
|
|
|
bool *face_used = new bool[num_faces];
|
2015-08-06 07:49:13 +00:00
|
|
|
memset(face_used, 0, sizeof(bool) * num_faces);
|
|
|
|
std::stack<int> traverse_stack;
|
|
|
|
int face_start = 0, num_traversed_faces = 0;
|
|
|
|
/* Traverse all islands. */
|
|
|
|
while (num_traversed_faces != num_faces) {
|
|
|
|
/* Find first face of any untraversed islands. */
|
|
|
|
while (face_used[face_start]) {
|
|
|
|
++face_start;
|
|
|
|
}
|
|
|
|
/* Add first face to the stack. */
|
|
|
|
traverse_stack.push(face_start);
|
|
|
|
face_used[face_start] = true;
|
|
|
|
/* Go over whole connected component. */
|
|
|
|
while (!traverse_stack.empty()) {
|
|
|
|
int face = traverse_stack.top();
|
|
|
|
traverse_stack.pop();
|
|
|
|
IndexArray face_edges = getBaseFaceEdges(refiner, face);
|
|
|
|
ConstIndexArray face_verts = getBaseFaceVertices(refiner, face);
|
|
|
|
for (int edge_index = 0; edge_index < face_edges.size(); ++edge_index) {
|
|
|
|
const int edge = face_edges[edge_index];
|
|
|
|
ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
|
|
|
|
if (edge_faces.size() != 2) {
|
|
|
|
/* Can't make consistent normals for non-manifolds. */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ConstIndexArray edge_verts = getBaseEdgeVertices(refiner, edge);
|
|
|
|
/* Get winding of the reference face. */
|
|
|
|
int vert0_of_face = findInArray(face_verts, edge_verts[0]),
|
|
|
|
vert1_of_face = findInArray(face_verts, edge_verts[1]);
|
|
|
|
int delta_face = get_loop_winding(vert0_of_face, vert1_of_face);
|
|
|
|
for (int edge_face = 0; edge_face < edge_faces.size(); ++edge_face) {
|
|
|
|
int other_face = edge_faces[edge_face];
|
|
|
|
/* Never re-traverse faces, only move forward. */
|
|
|
|
if (face_used[other_face]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
IndexArray other_face_verts = getBaseFaceVertices(refiner,
|
|
|
|
other_face);
|
|
|
|
int vert0_of_other_face = findInArray(other_face_verts,
|
|
|
|
edge_verts[0]),
|
|
|
|
vert1_of_other_face = findInArray(other_face_verts,
|
|
|
|
edge_verts[1]);
|
|
|
|
int delta_other_face = get_loop_winding(vert0_of_other_face,
|
|
|
|
vert1_of_other_face);
|
|
|
|
if (delta_face * delta_other_face > 0) {
|
|
|
|
IndexArray other_face_verts = getBaseFaceVertices(refiner,
|
|
|
|
other_face),
|
|
|
|
other_face_edges = getBaseFaceEdges(refiner,
|
|
|
|
other_face);
|
|
|
|
reverse_face_loops(other_face_verts,
|
|
|
|
other_face_edges);
|
|
|
|
}
|
|
|
|
traverse_stack.push(other_face);
|
|
|
|
face_used[other_face] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++num_traversed_faces;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
|
|
|
/* Vertex relations */
|
|
|
|
const int num_verts = conv.get_num_verts(&conv);
|
2015-07-20 13:18:35 +00:00
|
|
|
for (int vert = 0; vert < num_verts; ++vert) {
|
2015-08-03 11:20:41 +00:00
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
/* Vert-Faces */
|
|
|
|
IndexArray dst_vert_faces = getBaseVertexFaces(refiner, vert);
|
|
|
|
int num_vert_faces = conv.get_num_vert_faces(&conv, vert);
|
|
|
|
int *vert_faces = new int[num_vert_faces];
|
|
|
|
conv.get_vert_faces(&conv, vert, vert_faces);
|
2015-07-29 11:54:07 +00:00
|
|
|
/* Vert-Edges */
|
|
|
|
IndexArray dst_vert_edges = getBaseVertexEdges(refiner, vert);
|
|
|
|
int num_vert_edges = conv.get_num_vert_edges(&conv, vert);
|
|
|
|
int *vert_edges = new int[num_vert_edges];
|
|
|
|
conv.get_vert_edges(&conv, vert, vert_edges);
|
2015-07-29 15:41:57 +00:00
|
|
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
2015-08-03 11:20:41 +00:00
|
|
|
/* ** Order vertex edges and faces in a CCW order. ** */
|
2015-07-29 15:10:13 +00:00
|
|
|
memset(face_used, 0, sizeof(bool) * num_faces);
|
2015-08-03 11:20:41 +00:00
|
|
|
/* Number of edges and faces added to the ordered array. */
|
2015-07-29 15:10:13 +00:00
|
|
|
int edge_count_ordered = 0, face_count_ordered = 0;
|
2015-08-03 11:20:41 +00:00
|
|
|
/* Add loose edges straight into the edges array. */
|
|
|
|
bool has_fan_connections = false;
|
|
|
|
for (int i = 0; i < num_vert_edges; ++i) {
|
|
|
|
IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]);
|
|
|
|
if (edge_faces.size() == 0) {
|
|
|
|
dst_vert_edges[edge_count_ordered++] = vert_edges[i];
|
|
|
|
}
|
|
|
|
else if (edge_faces.size() > 2) {
|
|
|
|
has_fan_connections = true;
|
|
|
|
}
|
2015-07-29 15:10:13 +00:00
|
|
|
}
|
2015-08-03 11:20:41 +00:00
|
|
|
if (has_fan_connections) {
|
|
|
|
/* OpenSubdiv currently doesn't give us clues how to handle
|
|
|
|
* fan face connections. and since handling such connections
|
|
|
|
* complicates the loop below we simply don't do special
|
|
|
|
* orientation for them.
|
|
|
|
*/
|
|
|
|
memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
|
|
|
|
memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
|
|
|
|
delete [] vert_edges;
|
|
|
|
delete [] vert_faces;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Perform at max numbder of vert-edges iteration and try to avoid
|
|
|
|
* deadlock here for malformed mesh.
|
|
|
|
*/
|
|
|
|
for (int global_iter = 0; global_iter < num_vert_edges; ++global_iter) {
|
|
|
|
/* Numbr of edges and faces which are still to be ordered. */
|
|
|
|
int num_vert_edges_remained = num_vert_edges - edge_count_ordered,
|
|
|
|
num_vert_faces_remained = num_vert_faces - face_count_ordered;
|
|
|
|
if (num_vert_edges_remained == 0 && num_vert_faces_remained == 0) {
|
|
|
|
/* All done, nothing to do anymore. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Face, edge and face-vertex inndex to start traversal from. */
|
|
|
|
int face_start = -1, edge_start = -1, face_vert_start = -1;
|
|
|
|
if (num_vert_edges_remained == num_vert_faces_remained) {
|
|
|
|
/* Vertex is eitehr complete manifold or is connected to seevral
|
|
|
|
* manifold islands (hourglass-like configuration), can pick up
|
|
|
|
* random edge unused and start from it.
|
|
|
|
*/
|
|
|
|
/* TODO(sergey): Start from previous edge from which traversal
|
|
|
|
* began at previous iteration.
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < num_vert_edges; ++i) {
|
|
|
|
face_start = vert_faces[i];
|
|
|
|
if (!face_used[face_start]) {
|
|
|
|
ConstIndexArray
|
|
|
|
face_verts = getBaseFaceVertices(refiner, face_start),
|
|
|
|
face_edges = getBaseFaceEdges(refiner, face_start);
|
|
|
|
face_vert_start = findInArray(face_verts, vert);
|
|
|
|
edge_start = face_edges[face_vert_start];
|
|
|
|
break;
|
|
|
|
}
|
2015-07-29 15:10:13 +00:00
|
|
|
}
|
2015-08-03 11:20:41 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Special handle of non-manifold vertex. */
|
|
|
|
for (int i = 0; i < num_vert_edges; ++i) {
|
|
|
|
edge_start = vert_edges[i];
|
|
|
|
IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_start);
|
2015-08-03 12:26:31 +00:00
|
|
|
if (edge_faces.size() == 1) {
|
|
|
|
face_start = edge_faces[0];
|
2015-08-03 11:20:41 +00:00
|
|
|
if (!face_used[face_start]) {
|
|
|
|
ConstIndexArray
|
|
|
|
face_verts = getBaseFaceVertices(refiner, face_start),
|
|
|
|
face_edges = getBaseFaceEdges(refiner, face_start);
|
|
|
|
face_vert_start = findInArray(face_verts, vert);
|
|
|
|
if (edge_start == face_edges[face_vert_start]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Reset indices for sanity check below. */
|
|
|
|
face_start = edge_start = face_vert_start = -1;
|
2015-07-29 15:10:13 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-03 11:20:41 +00:00
|
|
|
/* Sanity check. */
|
|
|
|
assert(face_start != -1 &&
|
|
|
|
edge_start != -1 &&
|
|
|
|
face_vert_start != -1);
|
|
|
|
/* Traverse faces starting from the current one. */
|
|
|
|
int edge_first = edge_start;
|
2015-07-29 15:10:13 +00:00
|
|
|
dst_vert_faces[face_count_ordered++] = face_start;
|
2015-08-03 11:20:41 +00:00
|
|
|
dst_vert_edges[edge_count_ordered++] = edge_start;
|
2015-07-29 16:41:05 +00:00
|
|
|
face_used[face_start] = true;
|
2015-07-29 15:10:13 +00:00
|
|
|
while (edge_count_ordered < num_vert_edges) {
|
|
|
|
IndexArray face_verts = getBaseFaceVertices(refiner, face_start);
|
|
|
|
IndexArray face_edges = getBaseFaceEdges(refiner, face_start);
|
|
|
|
int face_edge_start = face_vert_start;
|
|
|
|
int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) : (face_verts.size() - 1);
|
|
|
|
Index edge_next = face_edges[face_edge_next];
|
|
|
|
if (edge_next == edge_first) {
|
2015-08-03 11:20:41 +00:00
|
|
|
/* Multiple manifolds found, stop for now and handle rest
|
|
|
|
* in the next iteration.
|
2015-07-29 16:41:05 +00:00
|
|
|
*/
|
2015-07-29 15:10:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
dst_vert_edges[edge_count_ordered++] = edge_next;
|
|
|
|
if (face_count_ordered < num_vert_faces) {
|
|
|
|
IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_next);
|
|
|
|
assert(edge_faces.size() != 0);
|
|
|
|
if (edge_faces.size() == 1) {
|
|
|
|
assert(edge_faces[0] == face_start);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (edge_faces.size() != 2) {
|
2015-07-20 13:18:35 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-07-29 15:10:13 +00:00
|
|
|
assert(edge_faces.size() == 2);
|
|
|
|
face_start = edge_faces[(edge_faces[0] == face_start) ? 1 : 0];
|
|
|
|
face_vert_start = findInArray(getBaseFaceEdges(refiner, face_start), edge_next);
|
|
|
|
dst_vert_faces[face_count_ordered++] = face_start;
|
2015-07-29 16:41:05 +00:00
|
|
|
face_used[face_start] = true;
|
2015-07-20 13:18:35 +00:00
|
|
|
}
|
2015-07-29 15:10:13 +00:00
|
|
|
edge_start = edge_next;
|
2015-07-20 13:18:35 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-29 15:10:13 +00:00
|
|
|
/* Verify ordering doesn't ruin connectivity information. */
|
|
|
|
assert(face_count_ordered == num_vert_faces);
|
|
|
|
assert(edge_count_ordered == num_vert_edges);
|
2015-08-03 11:20:41 +00:00
|
|
|
check_oriented_vert_connectivity(num_vert_edges,
|
|
|
|
num_vert_faces,
|
|
|
|
vert_edges,
|
|
|
|
vert_faces,
|
|
|
|
&dst_vert_edges[0],
|
|
|
|
&dst_vert_faces[0]);
|
|
|
|
/* For the release builds we're failing mesh construction so instead
|
|
|
|
* of nasty bugs the unsupported mesh will simply disappear from the
|
|
|
|
* viewport.
|
|
|
|
*/
|
|
|
|
if (face_count_ordered != num_vert_faces ||
|
|
|
|
edge_count_ordered != num_vert_edges)
|
|
|
|
{
|
|
|
|
delete [] vert_edges;
|
|
|
|
delete [] vert_faces;
|
|
|
|
return false;
|
2015-07-29 15:10:13 +00:00
|
|
|
}
|
2015-07-29 15:41:57 +00:00
|
|
|
#else /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
|
|
|
memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
|
|
|
|
memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
|
|
|
|
#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
|
2015-07-20 13:18:35 +00:00
|
|
|
delete [] vert_edges;
|
|
|
|
delete [] vert_faces;
|
|
|
|
}
|
2015-08-03 11:20:41 +00:00
|
|
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
|
|
|
delete [] face_used;
|
|
|
|
#endif
|
2015-07-20 13:18:35 +00:00
|
|
|
populateBaseLocalIndices(refiner);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
2016-07-21 10:33:11 +00:00
|
|
|
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
|
2015-07-20 13:18:35 +00:00
|
|
|
TopologyRefiner& refiner,
|
2016-07-21 10:33:11 +00:00
|
|
|
const TopologyRefinerData& cb_data)
|
2015-07-20 13:18:35 +00:00
|
|
|
{
|
2016-07-21 10:33:11 +00:00
|
|
|
const OpenSubdiv_Converter& conv = cb_data.conv;
|
2015-07-29 15:38:06 +00:00
|
|
|
typedef OpenSubdiv::Sdc::Crease Crease;
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
int num_edges = conv.get_num_edges(&conv);
|
|
|
|
for (int edge = 0; edge < num_edges; ++edge) {
|
2015-07-29 15:38:06 +00:00
|
|
|
float sharpness;
|
|
|
|
ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
|
|
|
|
if (edge_faces.size() == 2) {
|
|
|
|
sharpness = conv.get_edge_sharpness(&conv, edge);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Non-manifold edges must be sharp. */
|
|
|
|
sharpness = Crease::SHARPNESS_INFINITE;
|
|
|
|
}
|
2015-07-20 13:18:35 +00:00
|
|
|
setBaseEdgeSharpness(refiner, edge, sharpness);
|
|
|
|
}
|
2015-07-29 15:38:06 +00:00
|
|
|
|
2015-08-03 11:44:41 +00:00
|
|
|
/* 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 verts which are adjacent to a loose edge as sharp,
|
|
|
|
* but this decision needs some more investigation.
|
2015-07-29 15:38:06 +00:00
|
|
|
*/
|
|
|
|
int num_vert = conv.get_num_verts(&conv);
|
|
|
|
for (int vert = 0; vert < num_vert; ++vert) {
|
2015-08-03 11:44:41 +00:00
|
|
|
ConstIndexArray vert_edges = getBaseVertexEdges(refiner, vert);
|
|
|
|
for (int edge_index = 0; edge_index < vert_edges.size(); ++edge_index) {
|
|
|
|
int edge = vert_edges[edge_index];
|
|
|
|
ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
|
|
|
|
if (edge_faces.size() == 0) {
|
|
|
|
setBaseVertexSharpness(refiner, vert, Crease::SHARPNESS_INFINITE);
|
|
|
|
break;
|
|
|
|
}
|
2015-07-29 15:38:06 +00:00
|
|
|
}
|
2015-09-21 12:53:21 +00:00
|
|
|
if (vert_edges.size() == 2) {
|
|
|
|
int edge0 = vert_edges[0],
|
|
|
|
edge1 = vert_edges[1];
|
|
|
|
float sharpness0 = conv.get_edge_sharpness(&conv, edge0),
|
|
|
|
sharpness1 = conv.get_edge_sharpness(&conv, edge1);
|
|
|
|
float sharpness = std::min(sharpness0, sharpness1);
|
|
|
|
setBaseVertexSharpness(refiner, vert, sharpness);
|
|
|
|
}
|
2015-07-29 15:38:06 +00:00
|
|
|
}
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
2016-07-21 10:33:11 +00:00
|
|
|
inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology(
|
2015-07-20 13:18:35 +00:00
|
|
|
TopologyError /*errCode*/,
|
|
|
|
const char *msg,
|
2016-07-21 10:33:11 +00:00
|
|
|
const TopologyRefinerData& /*mesh*/)
|
2015-07-20 13:18:35 +00:00
|
|
|
{
|
|
|
|
printf("OpenSubdiv Error: %s\n", msg);
|
|
|
|
}
|
|
|
|
|
2016-07-19 07:28:54 +00:00
|
|
|
template <>
|
2016-07-21 10:33:11 +00:00
|
|
|
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology(
|
2016-07-19 07:28:54 +00:00
|
|
|
TopologyRefiner& refiner,
|
2016-07-21 10:33:11 +00:00
|
|
|
const TopologyRefinerData& cb_data)
|
2016-07-19 07:28:54 +00:00
|
|
|
{
|
2016-07-21 10:33:11 +00:00
|
|
|
const OpenSubdiv_Converter& conv = cb_data.conv;
|
|
|
|
const int num_layers = conv.get_num_uv_layers(&conv);
|
|
|
|
if (num_layers <= 0) {
|
2016-07-19 07:28:54 +00:00
|
|
|
/* No UV maps, we can skip any face-varying data. */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const int num_faces = getNumBaseFaces(refiner);
|
2016-07-21 10:33:11 +00:00
|
|
|
for (int layer = 0; layer < num_layers; ++layer) {
|
|
|
|
conv.precalc_uv_layer(&conv, layer);
|
|
|
|
const int num_uvs = conv.get_num_uvs(&conv);
|
|
|
|
/* Fill in UV coordinates. */
|
|
|
|
cb_data.uvs->resize(num_uvs * 2);
|
|
|
|
conv.get_uvs(&conv, &cb_data.uvs->at(0));
|
|
|
|
/* Fill in per-corner index of the UV. */
|
|
|
|
const int channel = createBaseFVarChannel(refiner, num_uvs);
|
|
|
|
for (int face = 0; face < num_faces; ++face) {
|
|
|
|
Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner,
|
|
|
|
face,
|
|
|
|
channel);
|
|
|
|
for (int corner = 0; corner < dst_face_uvs.size(); ++corner) {
|
|
|
|
const int uv_index = conv.get_face_corner_uv_index(&conv,
|
|
|
|
face,
|
|
|
|
corner);
|
|
|
|
dst_face_uvs[corner] = uv_index;
|
|
|
|
}
|
2016-07-19 07:28:54 +00:00
|
|
|
}
|
2016-07-21 10:33:11 +00:00
|
|
|
conv.finish_uv_layer(&conv);
|
|
|
|
/* TODO(sergey): Single layer only for now. */
|
|
|
|
break;
|
2016-07-19 07:28:54 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
} /* namespace Far */
|
|
|
|
} /* namespace OPENSUBDIV_VERSION */
|
|
|
|
} /* namespace OpenSubdiv */
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
OpenSubdiv::Sdc::SchemeType get_capi_scheme_type(OpenSubdiv_SchemeType type)
|
|
|
|
{
|
|
|
|
switch(type) {
|
|
|
|
case OSD_SCHEME_BILINEAR:
|
|
|
|
return OpenSubdiv::Sdc::SCHEME_BILINEAR;
|
|
|
|
case OSD_SCHEME_CATMARK:
|
|
|
|
return OpenSubdiv::Sdc::SCHEME_CATMARK;
|
|
|
|
case OSD_SCHEME_LOOP:
|
|
|
|
return OpenSubdiv::Sdc::SCHEME_LOOP;
|
|
|
|
}
|
|
|
|
assert(!"Unknown sceme type passed via C-API");
|
|
|
|
return OpenSubdiv::Sdc::SCHEME_CATMARK;
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* namespace */
|
|
|
|
|
|
|
|
struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr(
|
|
|
|
OpenSubdiv_Converter *converter)
|
|
|
|
{
|
2015-07-29 15:38:06 +00:00
|
|
|
typedef OpenSubdiv::Sdc::Options Options;
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
using OpenSubdiv::Far::TopologyRefinerFactory;
|
|
|
|
OpenSubdiv::Sdc::SchemeType scheme_type =
|
|
|
|
get_capi_scheme_type(converter->get_type(converter));
|
2015-07-29 15:38:06 +00:00
|
|
|
Options options;
|
|
|
|
options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY);
|
|
|
|
options.SetCreasingMethod(Options::CREASE_UNIFORM);
|
2016-07-21 10:33:11 +00:00
|
|
|
/* TODO(sergey): Get proper UV subdivide flag. */
|
|
|
|
// options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_ALL);
|
|
|
|
options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_CORNERS_ONLY);
|
2015-07-20 13:18:35 +00:00
|
|
|
|
2016-07-21 10:33:11 +00:00
|
|
|
TopologyRefinerFactory<TopologyRefinerData>::Options
|
2015-07-20 13:18:35 +00:00
|
|
|
topology_options(scheme_type, options);
|
|
|
|
#ifdef OPENSUBDIV_VALIDATE_TOPOLOGY
|
|
|
|
topology_options.validateFullTopology = true;
|
|
|
|
#endif
|
2016-07-19 14:12:18 +00:00
|
|
|
OpenSubdiv_TopologyRefinerDescr *result = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerDescr);
|
2016-07-21 10:33:11 +00:00
|
|
|
TopologyRefinerData cb_data = {*converter, &result->uvs};
|
2015-07-20 13:18:35 +00:00
|
|
|
/* We don't use guarded allocation here so we can re-use the refiner
|
|
|
|
* for GL mesh creation directly.
|
|
|
|
*/
|
2016-07-19 14:12:18 +00:00
|
|
|
result->osd_refiner =
|
2016-07-21 10:33:11 +00:00
|
|
|
TopologyRefinerFactory<TopologyRefinerData>::Create(
|
|
|
|
cb_data,
|
2015-07-20 13:18:35 +00:00
|
|
|
topology_options);
|
2016-07-19 07:28:54 +00:00
|
|
|
|
2016-07-19 14:12:18 +00:00
|
|
|
return result;
|
2015-07-20 13:18:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void openSubdiv_deleteTopologyRefinerDescr(
|
|
|
|
OpenSubdiv_TopologyRefinerDescr *topology_refiner)
|
|
|
|
{
|
2016-07-19 14:12:18 +00:00
|
|
|
delete topology_refiner->osd_refiner;
|
|
|
|
OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefinerDescr);
|
2015-07-20 13:18:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int openSubdiv_topologyRefinerGetSubdivLevel(
|
|
|
|
const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
|
|
|
|
{
|
|
|
|
using OpenSubdiv::Far::TopologyRefiner;
|
2016-07-19 14:12:18 +00:00
|
|
|
const TopologyRefiner *refiner = topology_refiner->osd_refiner;
|
2015-07-20 13:18:35 +00:00
|
|
|
return refiner->GetMaxLevel();
|
|
|
|
}
|
|
|
|
|
|
|
|
int openSubdiv_topologyRefinerGetNumVerts(
|
|
|
|
const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
|
|
|
|
{
|
|
|
|
using OpenSubdiv::Far::TopologyLevel;
|
|
|
|
using OpenSubdiv::Far::TopologyRefiner;
|
2016-07-19 14:12:18 +00:00
|
|
|
const TopologyRefiner *refiner = topology_refiner->osd_refiner;
|
2015-07-20 13:18:35 +00:00
|
|
|
const TopologyLevel &base_level = refiner->GetLevel(0);
|
|
|
|
return base_level.GetNumVertices();
|
|
|
|
}
|
|
|
|
|
|
|
|
int openSubdiv_topologyRefinerGetNumEdges(
|
|
|
|
const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
|
|
|
|
{
|
|
|
|
using OpenSubdiv::Far::TopologyLevel;
|
|
|
|
using OpenSubdiv::Far::TopologyRefiner;
|
2016-07-19 14:12:18 +00:00
|
|
|
const TopologyRefiner *refiner = topology_refiner->osd_refiner;
|
2015-07-20 13:18:35 +00:00
|
|
|
const TopologyLevel &base_level = refiner->GetLevel(0);
|
|
|
|
return base_level.GetNumEdges();
|
|
|
|
}
|
|
|
|
|
|
|
|
int openSubdiv_topologyRefinerGetNumFaces(
|
|
|
|
const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
|
|
|
|
{
|
|
|
|
using OpenSubdiv::Far::TopologyLevel;
|
|
|
|
using OpenSubdiv::Far::TopologyRefiner;
|
2016-07-19 14:12:18 +00:00
|
|
|
const TopologyRefiner *refiner = topology_refiner->osd_refiner;
|
2015-07-20 13:18:35 +00:00
|
|
|
const TopologyLevel &base_level = refiner->GetLevel(0);
|
|
|
|
return base_level.GetNumFaces();
|
|
|
|
}
|
|
|
|
|
2015-08-25 13:05:28 +00:00
|
|
|
int openSubdiv_topologyRefinerGetNumFaceVerts(
|
|
|
|
const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
|
|
|
|
int face)
|
|
|
|
{
|
|
|
|
using OpenSubdiv::Far::TopologyLevel;
|
|
|
|
using OpenSubdiv::Far::TopologyRefiner;
|
2016-07-19 14:12:18 +00:00
|
|
|
const TopologyRefiner *refiner = topology_refiner->osd_refiner;
|
2015-08-25 13:05:28 +00:00
|
|
|
const TopologyLevel &base_level = refiner->GetLevel(0);
|
|
|
|
return base_level.GetFaceVertices(face).size();
|
|
|
|
}
|
|
|
|
|
2015-07-20 13:18:35 +00:00
|
|
|
int openSubdiv_topologyRefnerCompareConverter(
|
|
|
|
const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
|
|
|
|
OpenSubdiv_Converter *converter)
|
|
|
|
{
|
|
|
|
using OpenSubdiv::Far::ConstIndexArray;
|
|
|
|
using OpenSubdiv::Far::TopologyRefiner;
|
|
|
|
using OpenSubdiv::Far::TopologyLevel;
|
2016-07-19 14:12:18 +00:00
|
|
|
const TopologyRefiner *refiner = topology_refiner->osd_refiner;
|
2015-07-20 13:18:35 +00:00
|
|
|
const TopologyLevel &base_level = refiner->GetLevel(0);
|
|
|
|
const int num_verts = base_level.GetNumVertices();
|
|
|
|
const int num_edges = base_level.GetNumEdges();
|
|
|
|
const int num_faces = base_level.GetNumFaces();
|
|
|
|
/* Quick preliminary check. */
|
|
|
|
OpenSubdiv::Sdc::SchemeType scheme_type =
|
|
|
|
get_capi_scheme_type(converter->get_type(converter));
|
|
|
|
if (scheme_type != refiner->GetSchemeType()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (converter->get_num_verts(converter) != num_verts ||
|
|
|
|
converter->get_num_edges(converter) != num_edges ||
|
|
|
|
converter->get_num_faces(converter) != num_faces)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* Compare all edges. */
|
|
|
|
for (int edge = 0; edge < num_edges; ++edge) {
|
|
|
|
ConstIndexArray edge_verts = base_level.GetEdgeVertices(edge);
|
|
|
|
int conv_edge_verts[2];
|
|
|
|
converter->get_edge_verts(converter, edge, conv_edge_verts);
|
|
|
|
if (conv_edge_verts[0] != edge_verts[0] ||
|
|
|
|
conv_edge_verts[1] != edge_verts[1])
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Compare all faces. */
|
|
|
|
std::vector<int> conv_face_verts;
|
|
|
|
for (int face = 0; face < num_faces; ++face) {
|
|
|
|
ConstIndexArray face_verts = base_level.GetFaceVertices(face);
|
|
|
|
if (face_verts.size() != converter->get_num_face_verts(converter,
|
|
|
|
face))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
conv_face_verts.resize(face_verts.size());
|
|
|
|
converter->get_face_verts(converter, face, &conv_face_verts[0]);
|
2015-08-06 07:49:13 +00:00
|
|
|
bool direct_match = true;
|
2015-07-20 13:18:35 +00:00
|
|
|
for (int i = 0; i < face_verts.size(); ++i) {
|
|
|
|
if (conv_face_verts[i] != face_verts[i]) {
|
2015-08-06 07:49:13 +00:00
|
|
|
direct_match = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!direct_match) {
|
|
|
|
/* If face didn't match in direct direction we also test if it
|
|
|
|
* matches in reversed direction. This is because conversion might
|
|
|
|
* reverse loops to make normals consistent.
|
|
|
|
*/
|
|
|
|
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
|
|
|
|
reverse_face_verts(&conv_face_verts[0], conv_face_verts.size());
|
|
|
|
for (int i = 0; i < face_verts.size(); ++i) {
|
|
|
|
if (conv_face_verts[i] != face_verts[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-07-20 13:18:35 +00:00
|
|
|
}
|
2015-08-06 07:49:13 +00:00
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
2015-07-20 13:18:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Compare sharpness. */
|
|
|
|
for (int edge = 0; edge < num_edges; ++edge) {
|
2015-07-29 16:05:29 +00:00
|
|
|
ConstIndexArray edge_faces = base_level.GetEdgeFaces(edge);
|
2015-07-20 13:18:35 +00:00
|
|
|
float sharpness = base_level.GetEdgeSharpness(edge);
|
2015-07-29 16:05:29 +00:00
|
|
|
float conv_sharpness;
|
|
|
|
if (edge_faces.size() == 2) {
|
|
|
|
conv_sharpness = converter->get_edge_sharpness(converter, edge);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
conv_sharpness = OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE;
|
|
|
|
}
|
2015-07-20 13:18:35 +00:00
|
|
|
if (sharpness != conv_sharpness) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|