diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2018-07-16 10:28:05 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2018-07-16 10:52:37 +0300 |
commit | d9203820467665ba19e1956e035de663849e695c (patch) | |
tree | 62d01753cafe31ac739c27f45272e2e4c0938c80 /intern | |
parent | b29b73a67eca3e5ac71c4b7c89f25bf3fb694cc3 (diff) |
OpenSubdiv: Re-work C-API integration
Main goal is to make API simpler to follow (at least ion terms what
is defined/declared where, as opposite of handful big headers which
includes all the declarations), and also avoid a big set of long and
obscure functions.
Now C-API files are split into smaller ones, following OpenSubdiv
behavior more closely, and also function pointers in structures
used a lot more, which shortens functions names,
UV integration part in GL Mesh is mainly stripped away, it needs
to be done differently. On a related topic, UV coordinates API in
converter needs to be removed as well, we do not need coordinates,
only island connectivity information there.
Additional changes:
- Varying interpolation in evaluator API are temporarily disabled,
need to extend API somewhere (probably, evaluator's API) to inform
layout information of vertex data (whether it contains varying
data, width, stride and such).
- Evaluator now can interpolate face-varying data.
Only works for adaptive refiner, since some issues in OpenSubdiv
itself.
Planned changes:
- Remove uv coordinates from TopologyConverter.
- Support evaluation of patches (as opposite to individual coordinates
as it happens currently).
- Support more flexible layout of varying and face-varying data.
It is stupid to assume varying is 3 floats and face-varying 2 floats.
- Support of second order derivatives.
- Everything else what i'm missing in this list.
Diffstat (limited to 'intern')
50 files changed, 4800 insertions, 2789 deletions
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt index 876b5c0181f..887eb399224 100644 --- a/intern/opensubdiv/CMakeLists.txt +++ b/intern/opensubdiv/CMakeLists.txt @@ -34,20 +34,40 @@ set(INC_SYS ) set(SRC - opensubdiv_capi.cc - opensubdiv_converter.cc - opensubdiv_device_context_cuda.cc - opensubdiv_device_context_opencl.cc - opensubdiv_evaluator_capi.cc - opensubdiv_gpu_capi.cc - opensubdiv_utils_capi.cc + internal/opensubdiv.cc + internal/opensubdiv_converter_factory.cc + internal/opensubdiv_converter_internal.cc + internal/opensubdiv_converter_orient.cc + internal/opensubdiv_device_context_cuda.cc + internal/opensubdiv_device_context_opencl.cc + internal/opensubdiv_evaluator.cc + internal/opensubdiv_evaluator_internal.cc + internal/opensubdiv_gl_mesh.cc + internal/opensubdiv_gl_mesh_draw.cc + internal/opensubdiv_gl_mesh_fvar.cc + internal/opensubdiv_gl_mesh_internal.cc + internal/opensubdiv_topology_refiner.cc + internal/opensubdiv_topology_refiner_internal.cc + internal/opensubdiv_util.cc + + internal/opensubdiv_converter_internal.h + internal/opensubdiv_converter_orient.h + internal/opensubdiv_converter_orient_impl.h + internal/opensubdiv_device_context_cuda.h + internal/opensubdiv_device_context_opencl.h + internal/opensubdiv_evaluator_internal.h + internal/opensubdiv_gl_mesh_fvar.h + internal/opensubdiv_gl_mesh_internal.h + internal/opensubdiv_internal.h + internal/opensubdiv_topology_refiner_internal.h + internal/opensubdiv_util.h opensubdiv_capi.h + opensubdiv_capi_type.h opensubdiv_converter_capi.h - opensubdiv_device_context_cuda.h - opensubdiv_device_context_opencl.h - opensubdiv_intern.h - opensubdiv_topology_refiner.h + opensubdiv_evaluator_capi.h + opensubdiv_gl_mesh_capi.h + opensubdiv_topology_refiner_capi.h ) macro(OPENSUBDIV_DEFINE_COMPONENT component) @@ -64,9 +84,9 @@ OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_OPENMP) OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK) OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_GLSL_COMPUTE) -data_to_c_simple(gpu_shader_opensubdiv_vertex.glsl SRC) -data_to_c_simple(gpu_shader_opensubdiv_geometry.glsl SRC) -data_to_c_simple(gpu_shader_opensubdiv_fragment.glsl SRC) +data_to_c_simple(shader/gpu_shader_opensubdiv_vertex.glsl SRC) +data_to_c_simple(shader/gpu_shader_opensubdiv_geometry.glsl SRC) +data_to_c_simple(shader/gpu_shader_opensubdiv_fragment.glsl SRC) add_definitions(-DGLEW_STATIC) diff --git a/intern/opensubdiv/internal/opensubdiv.cc b/intern/opensubdiv/internal/opensubdiv.cc new file mode 100644 index 00000000000..c2945ed25ab --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv.cc @@ -0,0 +1,99 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin +// Contributor(s): Brecht van Lommel + +#include "opensubdiv_capi.h" + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <GL/glew.h> + +#include "opensubdiv_device_context_opencl.h" +#include "opensubdiv_device_context_cuda.h" +#include "opensubdiv_gl_mesh_capi.h" + +void openSubdiv_init(void) { + // Ensure all OpenGL strings are cached. + openSubdiv_getAvailableEvaluators(); +} + +void openSubdiv_cleanup(void) { + openSubdiv_deinitGLMeshDrawingResources(); +} + +int openSubdiv_getAvailableEvaluators(void) { + int flags = OPENSUBDIV_EVALUATOR_CPU; + +#ifdef OPENSUBDIV_HAS_OPENMP + flags |= OPENSUBDIV_EVALUATOR_OPENMP; +#endif + +#ifdef OPENSUBDIV_HAS_OPENCL + if (CLDeviceContext::HAS_CL_VERSION_1_1()) { + flags |= OPENSUBDIV_EVALUATOR_OPENCL; + } +#endif + +#ifdef OPENSUBDIV_HAS_CUDA + if (CudaDeviceContext::HAS_CUDA_VERSION_4_0()) { + flags |= OPENSUBDIV_EVALUATOR_CUDA; + } +#endif + +#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK + if (GLEW_VERSION_4_1) { + flags |= OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK; + } +#endif + +#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE + if (GLEW_VERSION_4_3 || GLEW_ARB_compute_shader) { + flags |= OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; + } +#endif + + return flags; +} + +int openSubdiv_getVersionHex(void) { +#if defined(OPENSUBDIV_VERSION_NUMBER) + return OPENSUBDIV_VERSION_NUMBER; +#elif defined(OPENSUBDIV_VERSION_MAJOR) + return OPENSUBDIV_VERSION_MAJOR * 10000 + + OPENSUBDIV_VERSION_MINOR * 100 + + OPENSUBDIV_VERSION_PATCH; +#elif defined(OPENSUBDIV_VERSION) + const char* version = STRINGIFY(OPENSUBDIV_VERSION); + if (version[0] == 'v') { + version += 1; + } + int major = 0, minor = 0, patch = 0; + vector<string> tokens; + opensubdiv_capi::stringSplit(&tokens, version, "_", true); + if (tokens.size() == 3) { + major = atoi(tokens[0].c_str()); + minor = atoi(tokens[1].c_str()); + patch = atoi(tokens[2].c_str()); + } + return major * 10000 + minor * 100 + patch; +#else + return 0; +#endif +} diff --git a/intern/opensubdiv/internal/opensubdiv_converter_factory.cc b/intern/opensubdiv/internal/opensubdiv_converter_factory.cc new file mode 100644 index 00000000000..bd6edbb9648 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_factory.cc @@ -0,0 +1,438 @@ +// Copyright 2015 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include "internal/opensubdiv_converter_factory.h" + +#include <cassert> +#include <cstdio> +#include <stack> +#include <vector> + +#include <opensubdiv/far/topologyRefinerFactory.h> + +#include "internal/opensubdiv_converter_internal.h" +#include "internal/opensubdiv_converter_orient.h" +#include "internal/opensubdiv_internal.h" +#include "opensubdiv_converter_capi.h" + +struct TopologyRefinerData { + const OpenSubdiv_Converter* converter; +}; + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { +namespace Far { + +template <> +inline bool +TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology( + TopologyRefiner& refiner, + const TopologyRefinerData& cb_data) { + const OpenSubdiv_Converter* converter = cb_data.converter; + /// Faces and face-vertices. + const int num_faces = converter->getNumFaces(converter); + setNumBaseFaces(refiner, num_faces); + for (int face_index = 0; face_index < num_faces; ++face_index) { + const int num_face_vertices = + converter->getNumFaceVertices(converter, face_index); + setNumBaseFaceVertices(refiner, face_index, num_face_vertices); + } + // 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); + } + // Vertices and vertex-faces and vertex-edges. + const int num_vertices = converter->getNumVertices(converter); + setNumBaseVertices(refiner, num_vertices); + 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); + } + return true; +} + +template <> +inline bool +TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology( + TopologyRefiner& refiner, + const TopologyRefinerData& cb_data) { + using Far::IndexArray; + const OpenSubdiv_Converter* converter = cb_data.converter; + // Face relations. + const int num_faces = converter->getNumFaces(converter); + for (int face_index = 0; face_index < num_faces; ++face_index) { + IndexArray dst_face_verts = getBaseFaceVertices(refiner, face_index); + converter->getFaceVertices(converter, face_index, &dst_face_verts[0]); + IndexArray dst_face_edges = getBaseFaceEdges(refiner, face_index); + converter->getFaceEdges(converter, face_index, &dst_face_edges[0]); + } + // Edge relations. + const int num_edges = converter->getNumEdges(converter); + for (int edge_index = 0; edge_index < num_edges; ++edge_index) { + // Edge-vertices. + IndexArray dst_edge_vertices = getBaseEdgeVertices(refiner, edge_index); + converter->getEdgeVertices(converter, edge_index, &dst_edge_vertices[0]); + // Edge-faces. + IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge_index); + converter->getEdgeFaces(converter, edge_index, &dst_edge_faces[0]); + } +// TODO(sergey): Find a way to move this to an utility function. +#ifdef OPENSUBDIV_ORIENT_TOPOLOGY + // Make face normals consistent. + std::vector<bool> face_used(num_faces, false); + 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_vertices = getBaseFaceVertices(refiner, face); + for (int i = 0; i < face_edges.size(); ++i) { + const int edge = face_edges[i]; + ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge); + if (edge_faces.size() != 2) { + /* Can't make consistent normals for non-manifolds. */ + continue; + } + ConstIndexArray edge_vertices = getBaseEdgeVertices(refiner, edge); + // Get winding of the reference face. + const int vert0_of_face = face_vertices.FindIndex(edge_vertices[0]); + const int vert1_of_face = face_vertices.FindIndex(edge_vertices[1]); + const int delta_face = + opensubdiv_capi::getLoopWinding(vert0_of_face, vert1_of_face); + for (int edge_face = 0; edge_face < edge_faces.size(); ++edge_face) { + const int other_face_index = edge_faces[edge_face]; + // Never re-traverse faces, only move forward. + if (face_used[other_face_index]) { + continue; + } + IndexArray other_face_vertics = + getBaseFaceVertices(refiner, other_face_index); + const int vert0_of_other_face = + other_face_vertics.FindIndex(edge_vertices[0]); + const int vert1_of_other_face = + other_face_vertics.FindIndex(edge_vertices[1]); + const int delta_other_face = opensubdiv_capi::getLoopWinding( + vert0_of_other_face, vert1_of_other_face); + if (delta_face * delta_other_face > 0) { + IndexArray other_face_vertices = + getBaseFaceVertices(refiner, other_face_index); + IndexArray other_face_edges = + getBaseFaceEdges(refiner, other_face_index); + opensubdiv_capi::reverseFaceLoops(&other_face_vertices, + &other_face_edges); + } + traverse_stack.push(other_face_index); + face_used[other_face_index] = true; + } + } + ++num_traversed_faces; + } + } +#endif // OPENSUBDIV_ORIENT_TOPOLOGY + // 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[0]); + // 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[0]); +// TODO(sergey): Find a way to move this to an utility function. +#ifdef OPENSUBDIV_ORIENT_TOPOLOGY + // Order vertex edges and faces to be in a CCW order. + std::fill(face_used.begin(), face_used.end(), false); + // Number of edges and faces added to the ordered array. + int edge_count_ordered = 0, face_count_ordered = 0; + // Add loose edges straight into the edges array. + bool has_fan_connections = false; + for (int i = 0; i < num_vertex_edges; ++i) { + IndexArray edge_faces = getBaseEdgeFaces(refiner, vertex_edges[i]); + if (edge_faces.size() == 0) { + dst_vertex_edges[edge_count_ordered++] = vertex_edges[i]; + } else if (edge_faces.size() > 2) { + has_fan_connections = true; + } + } + 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_vertex_edges[0], &vertex_edges[0], + sizeof(int) * num_vertex_edges); + memcpy(&dst_vertex_faces[0], &vertex_faces[0], + sizeof(int) * num_vertex_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_vertex_edges; ++global_iter) { + // Number of edges and faces which are still to be ordered. + const int num_vertex_edges_remained = + num_vertex_edges - edge_count_ordered; + const int num_vertex_faces_remained = + num_vertex_faces - face_count_ordered; + if (num_vertex_edges_remained == 0 && num_vertex_faces_remained == 0) { + // All done, nothing to do anymore. + break; + } + // Face, edge and face-vertex index to start traversal from. + int face_start = -1, edge_start = -1, face_vertex_start = -1; + if (num_vertex_edges_remained == num_vertex_faces_remained) { + // Vertex is either complete manifold or is connected to several + // 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_vertex_edges; ++i) { + face_start = vertex_faces[i]; + if (!face_used[face_start]) { + ConstIndexArray face_vertices = + getBaseFaceVertices(refiner, face_start); + ConstIndexArray face_edges = getBaseFaceEdges(refiner, face_start); + face_vertex_start = face_vertices.FindIndex(vertex_index); + edge_start = face_edges[face_vertex_start]; + break; + } + } + } else { + // Special handle of non-manifold vertex. + for (int i = 0; i < num_vertex_edges; ++i) { + edge_start = vertex_edges[i]; + IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_start); + if (edge_faces.size() == 1) { + face_start = edge_faces[0]; + if (!face_used[face_start]) { + ConstIndexArray face_vertices = + getBaseFaceVertices(refiner, face_start); + ConstIndexArray face_edges = + getBaseFaceEdges(refiner, face_start); + face_vertex_start = face_vertices.FindIndex(vertex_index); + if (edge_start == face_edges[face_vertex_start]) { + break; + } + } + } + // Reset indices for sanity check below. + face_start = edge_start = face_vertex_start = -1; + } + } + // Sanity check. + assert(face_start != -1); + assert(edge_start != -1); + assert(face_vertex_start != -1); + // Traverse faces starting from the current one. */ + int edge_first = edge_start; + dst_vertex_faces[face_count_ordered++] = face_start; + dst_vertex_edges[edge_count_ordered++] = edge_start; + face_used[face_start] = true; + while (edge_count_ordered < num_vertex_edges) { + IndexArray face_vertices = getBaseFaceVertices(refiner, face_start); + IndexArray face_edges = getBaseFaceEdges(refiner, face_start); + int face_edge_start = face_vertex_start; + int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) + : (face_vertices.size() - 1); + Index edge_next = face_edges[face_edge_next]; + if (edge_next == edge_first) { + // Multiple manifolds found, stop for now and handle rest + // in the next iteration. + break; + } + dst_vertex_edges[edge_count_ordered++] = edge_next; + if (face_count_ordered < num_vertex_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) { + break; + } + assert(edge_faces.size() == 2); + face_start = edge_faces[(edge_faces[0] == face_start) ? 1 : 0]; + face_vertex_start = + getBaseFaceEdges(refiner, face_start).FindIndex(edge_next); + dst_vertex_faces[face_count_ordered++] = face_start; + face_used[face_start] = true; + } + edge_start = edge_next; + } + } + // Verify ordering doesn't ruin connectivity information. + assert(face_count_ordered == num_vertex_faces); + assert(edge_count_ordered == num_vertex_edges); + opensubdiv_capi::checkOrientedVertexConnectivity( + num_vertex_edges, num_vertex_faces, &vertex_edges[0], &vertex_faces[0], + &dst_vertex_edges[0], &dst_vertex_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_vertex_faces || + edge_count_ordered != num_vertex_edges) { + return false; + } +#else // OPENSUBDIV_ORIENT_TOPOLOGY + memcpy(&dst_vertex_edges[0], &vertex_edges[0], + sizeof(int) * num_vertex_edges); + memcpy(&dst_vertex_faces[0], &vertex_faces[0], + sizeof(int) * num_vertex_faces); +#endif // OPENSUBDIV_ORIENT_TOPOLOGY + } + populateBaseLocalIndices(refiner); + return true; +} + +template <> +inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags( + TopologyRefiner& refiner, + const TopologyRefinerData& cb_data) { + using OpenSubdiv::Sdc::Crease; + const OpenSubdiv_Converter* converter = cb_data.converter; + const int num_edges = converter->getNumEdges(converter); + for (int edge_index = 0; edge_index < num_edges; ++edge_index) { + const float sharpness = + opensubdiv_capi::getCompatibleEdgeSharpness(converter, edge_index); + setBaseEdgeSharpness(refiner, 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); + for (int i = 0; i < vertex_edges.size(); ++i) { + const int edge_index = vertex_edges[i]; + ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge_index); + if (edge_faces.size() == 0) { + setBaseVertexSharpness(refiner, vertex_index, + Crease::SHARPNESS_INFINITE); + break; + } + } + if (vertex_edges.size() == 2) { + const int edge0 = vertex_edges[0], edge1 = vertex_edges[1]; + const float sharpness0 = converter->getEdgeSharpness(converter, edge0); + const float sharpness1 = converter->getEdgeSharpness(converter, edge1); + const float sharpness = std::min(sharpness0, sharpness1); + 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; + 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); + 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 Far */ +} /* namespace OPENSUBDIV_VERSION */ +} /* namespace OpenSubdiv */ + +namespace opensubdiv_capi { + +OpenSubdiv::Far::TopologyRefiner* createOSDTopologyRefinerFromConverter( + OpenSubdiv_Converter* converter) { + using OpenSubdiv::Sdc::Options; + using OpenSubdiv::Far::TopologyRefinerFactory; + const OpenSubdiv::Sdc::SchemeType scheme_type = + getSchemeTypeFromCAPI(converter->getSchemeType(converter)); + const Options::FVarLinearInterpolation linear_interpolation = + getFVarLinearInterpolationFromCAPI( + converter->getFVarLinearInterpolation(converter)); + Options options; + options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY); + options.SetCreasingMethod(Options::CREASE_UNIFORM); + options.SetFVarLinearInterpolation(linear_interpolation); + + TopologyRefinerFactory<TopologyRefinerData>::Options topology_options( + scheme_type, options); +#ifdef OPENSUBDIV_VALIDATE_TOPOLOGY + topology_options.validateFullTopology = true; +#endif + TopologyRefinerData cb_data; + cb_data.converter = converter; + return TopologyRefinerFactory<TopologyRefinerData>::Create( + cb_data, topology_options); +} + +} // namespace opensubdiv_capi diff --git a/intern/opensubdiv/internal/opensubdiv_converter_factory.h b/intern/opensubdiv/internal/opensubdiv_converter_factory.h new file mode 100644 index 00000000000..451418813e1 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_factory.h @@ -0,0 +1,37 @@ +// Copyright 2015 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CONVERTER_FACTORY_H_ +#define OPENSUBDIV_CONVERTER_FACTORY_H_ + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/far/topologyRefiner.h> + +struct OpenSubdiv_Converter; + +namespace opensubdiv_capi { + +OpenSubdiv::Far::TopologyRefiner* createOSDTopologyRefinerFromConverter( + struct OpenSubdiv_Converter* converter); + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_CONVERTER_FACTORY_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_converter_internal.cc b/intern/opensubdiv/internal/opensubdiv_converter_internal.cc new file mode 100644 index 00000000000..56b5657121d --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_internal.cc @@ -0,0 +1,70 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include "internal/opensubdiv_converter_internal.h" + +#include <opensubdiv/sdc/crease.h> +#include <cassert> + +namespace opensubdiv_capi { + +OpenSubdiv::Sdc::SchemeType getSchemeTypeFromCAPI(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 scheme type passed via C-API"); + return OpenSubdiv::Sdc::SCHEME_CATMARK; +} + +OpenSubdiv::Sdc::Options::FVarLinearInterpolation +getFVarLinearInterpolationFromCAPI( + OpenSubdiv_FVarLinearInterpolation linear_interpolation) { + typedef OpenSubdiv::Sdc::Options Options; + switch (linear_interpolation) { + case OSD_FVAR_LINEAR_INTERPOLATION_NONE: + return Options::FVAR_LINEAR_NONE; + case OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY: + return Options::FVAR_LINEAR_CORNERS_ONLY; + case OSD_FVAR_LINEAR_INTERPOLATION_BOUNDARIES: + return Options::FVAR_LINEAR_BOUNDARIES; + case OSD_FVAR_LINEAR_INTERPOLATION_ALL: + return Options::FVAR_LINEAR_ALL; + } + assert(!"Unknown fvar linear interpolation passed via C-API"); + return Options::FVAR_LINEAR_NONE; +} + +float getCompatibleEdgeSharpness(const OpenSubdiv_Converter* converter, + int edge_index) { + if (converter->getNumEdgeFaces(converter, edge_index) == 2) { + return converter->getEdgeSharpness(converter, edge_index); + } else { + return OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE; + } +} + +} // namespace opensubdiv_capi diff --git a/intern/opensubdiv/internal/opensubdiv_converter_internal.h b/intern/opensubdiv/internal/opensubdiv_converter_internal.h new file mode 100644 index 00000000000..68518d67884 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_internal.h @@ -0,0 +1,50 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CONVERTER_INTERNAL_H_ +#define OPENSUBDIV_CONVERTER_INTERNAL_H_ + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/sdc/options.h> +#include <opensubdiv/sdc/types.h> + +#include "opensubdiv_converter_capi.h" + +struct OpenSubdiv_Converter; + +namespace opensubdiv_capi { + +// Convert scheme type from C-API enum to an OpenSubdiv native enum. +OpenSubdiv::Sdc::SchemeType getSchemeTypeFromCAPI(OpenSubdiv_SchemeType type); + +// Convert face-varying interpolation type from C-API to an OpenSubdiv +// native enum. +OpenSubdiv::Sdc::Options::FVarLinearInterpolation +getFVarLinearInterpolationFromCAPI( + OpenSubdiv_FVarLinearInterpolation linear_interpolation); + +// Get edge sharpness in a way which makes OpenSubdiv happy. +float getCompatibleEdgeSharpness(const OpenSubdiv_Converter* converter, + int edge_index); + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_CONVERTER_INTERNAL_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_converter_orient.cc b/intern/opensubdiv/internal/opensubdiv_converter_orient.cc new file mode 100644 index 00000000000..449e9028180 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_orient.cc @@ -0,0 +1,66 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/opensubdiv_converter_orient.h" + +#include "internal/opensubdiv_internal.h" + +namespace opensubdiv_capi { + +void checkOrientedVertexConnectivity(const int num_vertex_edges, + const int num_vertex_faces, + const int* vertex_edges, + const int* vertex_faces, + const int* dst_vertex_edges, + const int* dst_vertex_faces) { +#ifndef NDEBUG + for (int i = 0; i < num_vertex_faces; ++i) { + bool found = false; + for (int j = 0; j < num_vertex_faces; ++j) { + if (vertex_faces[i] == dst_vertex_faces[j]) { + found = true; + break; + } + } + if (!found) { + assert(!"vert-faces connectivity ruined"); + } + } + for (int i = 0; i < num_vertex_edges; ++i) { + bool found = false; + for (int j = 0; j < num_vertex_edges; ++j) { + if (vertex_edges[i] == dst_vertex_edges[j]) { + found = true; + break; + } + } + if (!found) { + assert(!"vert-edges connectivity ruined"); + } + } +#else + (void) num_vertex_edges; + (void) num_vertex_faces; + (void) vertex_edges; + (void) vertex_faces; + (void) dst_vertex_edges; + (void) dst_vertex_faces; +#endif +} + +} // namespace opensubdiv_capi diff --git a/intern/opensubdiv/internal/opensubdiv_converter_orient.h b/intern/opensubdiv/internal/opensubdiv_converter_orient.h new file mode 100644 index 00000000000..b783007a0cb --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_orient.h @@ -0,0 +1,50 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CONVERTER_ORIENT_H_ +#define OPENSUBDIV_CONVERTER_ORIENT_H_ + +#include <opensubdiv/far/types.h> + +// Set of utility functions which are needed to bring topology to an orientation +// (or, winding, if you wish) which OpenSubdiv expects. + +namespace opensubdiv_capi { + +inline void reverseFaceVertices(int* face_vertices, const int num_vertices); + +// TODO(sergey): Document which value corresponds to which winding. +inline int getLoopWinding(int vert0_of_face, int vert1_of_face); + +inline void reverseFaceLoops( + OpenSubdiv::Far::IndexArray* face_vertices, + OpenSubdiv::Far::IndexArray* face_edges); + +// Used for debugging, checks whether orientation happened correct. +void checkOrientedVertexConnectivity(const int num_vertex_edges, + const int num_vertex_faces, + const int* vertex_edges, + const int* vertex_faces, + const int* dst_vertex_edges, + const int* dst_vertex_faces); + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_CONVERTER_ORIENT_H_ + +#include "internal/opensubdiv_converter_orient_impl.h" diff --git a/intern/opensubdiv/internal/opensubdiv_converter_orient_impl.h b/intern/opensubdiv/internal/opensubdiv_converter_orient_impl.h new file mode 100644 index 00000000000..3125bc600e5 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_converter_orient_impl.h @@ -0,0 +1,66 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CONVERTER_ORIENT_IMPL_H_ +#define OPENSUBDIV_CONVERTER_ORIENT_IMPL_H_ + +#include "internal/opensubdiv_converter_orient.h" + +#include <algorithm> +#include <cmath> +#include <utility> + +namespace opensubdiv_capi { + +inline void reverseFaceVertices(int* face_vertices, const int num_vertices) { + int last_vert = face_vertices[num_vertices - 1]; + for (int i = num_vertices - 1; i > 0; --i) { + face_vertices[i] = face_vertices[i - 1]; + } + face_vertices[0] = last_vert; +} + +inline int getLoopWinding(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 reverseFaceLoops( + OpenSubdiv::Far::IndexArray* face_vertices, + OpenSubdiv::Far::IndexArray* face_edges) { + const int num_face_vertices = face_vertices->size(); + for (int i = 0; i < num_face_vertices / 2; ++i) { + const int j = num_face_vertices - i - 1; + if (i != j) { + std::swap((*face_vertices)[i], (*face_vertices)[j]); + std::swap((*face_edges)[i], (*face_edges)[j]); + } + } + reverseFaceVertices(&(*face_vertices)[0], num_face_vertices); +} + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_CONVERTER_ORIENT_IMPL_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_device_context_cuda.cc b/intern/opensubdiv/internal/opensubdiv_device_context_cuda.cc new file mode 100644 index 00000000000..875f503b9ab --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_device_context_cuda.cc @@ -0,0 +1,226 @@ +// Adopted from OpenSubdiv with the following license: +// +// Copyright 2015 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. + +#ifdef OPENSUBDIV_HAS_CUDA + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include "opensubdiv_device_context_cuda.h" + +#if defined(_WIN32) +# include <windows.h> +#elif defined(__APPLE__) +# include <OpenGL/OpenGL.h> +#else +# include <GL/glx.h> +# include <X11/Xlib.h> +#endif + +#include <cuda.h> +#include <cuda_gl_interop.h> +#include <cuda_runtime_api.h> +#include <algorithm> +#include <cstdio> + +#define message(fmt, ...) +// #define message(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) +#define error(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) + +namespace { + +int getCudaDeviceForCurrentGLContext() { + // Find and use the CUDA device for the current GL context + unsigned int interop_device_count = 0; + int interopDevices[1]; + cudaError_t status = cudaGLGetDevices(&interop_device_count, + interopDevices, + 1, + cudaGLDeviceListCurrentFrame); + if (status == cudaErrorNoDevice || interop_device_count != 1) { + message("CUDA no interop devices found.\n"); + return 0; + } + int device = interopDevices[0]; +#if defined(_WIN32) + return device; +#elif defined(__APPLE__) + return device; +#else // X11 + Display* display = glXGetCurrentDisplay(); + int screen = DefaultScreen(display); + if (device != screen) { + error("The CUDA interop device (%d) does not match " + "the screen used by the current GL context (%d), " + "which may cause slow performance on systems " + "with multiple GPU devices.", + device, screen); + } + message("CUDA init using device for current GL context: %d\n", device); + return device; +#endif +} + +// Beginning of GPU Architecture definitions. +int convertSMVer2Cores_local(int major, int minor) { + // Defines for GPU Architecture types (using the SM version to determine + // the # of cores per SM + typedef struct { + int SM; // 0xMm (hexidecimal notation), + // M = SM Major version, + // and m = SM minor version + int Cores; + } sSMtoCores; + + sSMtoCores nGpuArchCoresPerSM[] = { + {0x10, 8}, // Tesla Generation (SM 1.0) G80 class. + {0x11, 8}, // Tesla Generation (SM 1.1) G8x class. + {0x12, 8}, // Tesla Generation (SM 1.2) G9x class. + {0x13, 8}, // Tesla Generation (SM 1.3) GT200 class. + {0x20, 32}, // Fermi Generation (SM 2.0) GF100 class. + {0x21, 48}, // Fermi Generation (SM 2.1) GF10x class. + {0x30, 192}, // Fermi Generation (SM 3.0) GK10x class. + {-1, -1}}; + int index = 0; + while (nGpuArchCoresPerSM[index].SM != -1) { + if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) { + return nGpuArchCoresPerSM[index].Cores; + } + index++; + } + printf("MapSMtoCores undefined SMversion %d.%d!\n", major, minor); + return -1; +} + +// This function returns the best GPU (with maximum GFLOPS). +int cutGetMaxGflopsDeviceId() { + int current_device = 0, sm_per_multiproc = 0; + int max_compute_perf = 0, max_perf_device = -1; + int device_count = 0, best_SM_arch = 0; + int compat_major, compat_minor; + cuDeviceGetCount(&device_count); + // Find the best major SM Architecture GPU device. + while (current_device < device_count) { + cuDeviceComputeCapability(&compat_major, &compat_minor, current_device); + if (compat_major > 0 && compat_major < 9999) { + best_SM_arch = std::max(best_SM_arch, compat_major); + } + current_device++; + } + // Find the best CUDA capable GPU device. + current_device = 0; + while (current_device < device_count) { + cuDeviceComputeCapability(&compat_major, &compat_minor, current_device); + if (compat_major == 9999 && compat_minor == 9999) { + sm_per_multiproc = 1; + } else { + sm_per_multiproc = convertSMVer2Cores_local(compat_major, compat_minor); + } + int multi_processor_count; + cuDeviceGetAttribute(&multi_processor_count, + CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, + current_device); + int clock_rate; + cuDeviceGetAttribute(&clock_rate, CU_DEVICE_ATTRIBUTE_CLOCK_RATE, + current_device); + int compute_perf = multi_processor_count * sm_per_multiproc * clock_rate; + if (compute_perf > max_compute_perf) { + /* If we find GPU with SM major > 2, search only these */ + if (best_SM_arch > 2) { + /* If our device==dest_SM_arch, choose this, or else pass. */ + if (compat_major == best_SM_arch) { + max_compute_perf = compute_perf; + max_perf_device = current_device; + } + } else { + max_compute_perf = compute_perf; + max_perf_device = current_device; + } + } + ++current_device; + } + return max_perf_device; +} + +} // namespace + +bool CudaDeviceContext::HAS_CUDA_VERSION_4_0() { +#ifdef OPENSUBDIV_HAS_CUDA + static bool cuda_initialized = false; + static bool cuda_load_success = true; + if (!cuda_initialized) { + cuda_initialized = true; + +#ifdef OPENSUBDIV_HAS_CUEW + cuda_load_success = cuewInit(CUEW_INIT_CUDA) == CUEW_SUCCESS; + if (!cuda_load_success) { + fprintf(stderr, "Loading CUDA failed.\n"); + } +#endif + // Need to initialize CUDA here so getting device + // with the maximum FPLOS works fine. + if (cuInit(0) == CUDA_SUCCESS) { + // This is to deal with cases like NVidia Optimus, + // when there might be CUDA library installed but + // NVidia card is not being active. + if (cutGetMaxGflopsDeviceId() < 0) { + cuda_load_success = false; + } + } else { + cuda_load_success = false; + } + } + return cuda_load_success; +#else + return false; +#endif +} + +CudaDeviceContext::CudaDeviceContext() + : initialized_(false) { +} + +CudaDeviceContext::~CudaDeviceContext() { + cudaDeviceReset(); +} + +bool CudaDeviceContext::Initialize() { + // See if any cuda device is available. + int device_count = 0; + cudaGetDeviceCount(&device_count); + message("CUDA device count: %d\n", device_count); + if (device_count <= 0) { + return false; + } + cudaGLSetGLDevice(getCudaDeviceForCurrentGLContext()); + initialized_ = true; + return true; +} + +bool CudaDeviceContext::IsInitialized() const { + return initialized_; +} + +#endif // OPENSUBDIV_HAS_CUDA diff --git a/intern/opensubdiv/internal/opensubdiv_device_context_cuda.h b/intern/opensubdiv/internal/opensubdiv_device_context_cuda.h new file mode 100644 index 00000000000..ef212df10f0 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_device_context_cuda.h @@ -0,0 +1,54 @@ +// Adopted from OpenSubdiv with the following license: +// +// Copyright 2013 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http: //www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. + +#ifndef OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ +#define OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ + +#ifdef OPENSUBDIV_HAS_CUDA + +struct ID3D11Device; + +class CudaDeviceContext { + public: + CudaDeviceContext(); + ~CudaDeviceContext(); + + static bool HAS_CUDA_VERSION_4_0(); + + // Initialze cuda device from the current GL context. + bool Initialize(); + + // Initialze cuda device from the ID3D11Device. + bool Initialize(ID3D11Device* device); + + // Returns true if the cuda device has already been initialized. + bool IsInitialized() const; + + private: + bool initialized_; +}; + +#endif // OPENSUBDIV_HAS_CUDA + +#endif // _OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_device_context_opencl.cc b/intern/opensubdiv/internal/opensubdiv_device_context_opencl.cc new file mode 100644 index 00000000000..00f58af894a --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_device_context_opencl.cc @@ -0,0 +1,269 @@ +// Adopted from OpenSubdiv with the following license: +// +// Copyright 2015 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. + +#include "opensubdiv_device_context_opencl.h" + +#ifdef OPENSUBDIV_HAS_OPENCL + +#if defined(_WIN32) +# include <windows.h> +#elif defined(__APPLE__) +# include <OpenGL/OpenGL.h> +#else +# include <GL/glx.h> +#endif + +#include <cstdio> +#include <cstring> +#include <string> +#include <vector> + +#define message(...) // fprintf(stderr, __VA_ARGS__) +#define error(...) fprintf(stderr, __VA_ARGS__) + +namespace { + +// Returns the first found platform. +cl_platform_id findPlatform() { + cl_uint num_platforms; + cl_int ci_error_number = clGetPlatformIDs(0, NULL, &num_platforms); + if (ci_error_number != CL_SUCCESS) { + error("Error %d in clGetPlatformIDs call.\n", ci_error_number); + return NULL; + } + if (num_platforms == 0) { + error("No OpenCL platform found.\n"); + return NULL; + } + std::vector<cl_platform_id> cl_platform_ids(num_platforms); + ci_error_number = clGetPlatformIDs(num_platforms, &cl_platform_ids[0], NULL); + char ch_buffer[1024]; + for (cl_uint i = 0; i < num_platforms; ++i) { + ci_error_number = clGetPlatformInfo(cl_platform_ids[i], + CL_PLATFORM_NAME, + sizeof(ch_buffer), + ch_buffer, + NULL); + if (ci_error_number == CL_SUCCESS) { + cl_platform_id platform_id = cl_platform_ids[i]; + return platform_id; + } + } + return NULL; +} + +// Return the device in cl_devices which supports the extension. +int findExtensionSupportedDevice(cl_device_id* cl_devices, + int num_devices, + const char* extension_name) { + // Find a device that supports sharing with GL/D3D11 + // (SLI / X-fire configurations) + cl_int cl_error_number; + for (int i = 0; i < num_devices; ++i) { + // Get extensions string size. + size_t extensions_size; + cl_error_number = clGetDeviceInfo(cl_devices[i], + CL_DEVICE_EXTENSIONS, + 0, + NULL, + &extensions_size); + if (cl_error_number != CL_SUCCESS) { + error("Error %d in clGetDeviceInfo\n", cl_error_number); + return -1; + } + if (extensions_size > 0) { + // Get extensions string. + std::string extensions('\0', extensions_size); + cl_error_number = clGetDeviceInfo(cl_devices[i], + CL_DEVICE_EXTENSIONS, + extensions_size, + &extensions[0], + &extensions_size); + if (cl_error_number != CL_SUCCESS) { + error("Error %d in clGetDeviceInfo\n", cl_error_number); + continue; + } + // Parse string. This is bit deficient since the extentions + // is space separated. + // + // The actual string would be "cl_khr_d3d11_sharing" + // or "cl_nv_d3d11_sharing" + if (extensions.find(extension_name) != std::string::npos) { + return i; + } + } + } + return -1; +} + +} // namespace + +CLDeviceContext::CLDeviceContext() + : cl_context_(NULL), + cl_command_queue_(NULL) { +} + +CLDeviceContext::~CLDeviceContext() { + if (cl_command_queue_) { + clReleaseCommandQueue(cl_command_queue_); + } + if (cl_context_) { + clReleaseContext(cl_context_); + } +} + +bool CLDeviceContext::HAS_CL_VERSION_1_1() { +#ifdef OPENSUBDIV_HAS_CLEW + static bool clew_initialized = false; + static bool clew_load_success; + if (!clew_initialized) { + clew_initialized = true; + clew_load_success = clewInit() == CLEW_SUCCESS; + if (!clew_load_success) { + error("Loading OpenCL failed.\n"); + } + } + return clew_load_success; +#endif + return true; +} + +bool CLDeviceContext::Initialize() { +#ifdef OPENSUBDIV_HAS_CLEW + if (!clGetPlatformIDs) { + error("Error clGetPlatformIDs function not bound.\n"); + return false; + } +#endif + cl_int cl_error_number; + cl_platform_id cp_platform = findPlatform(); + +#if defined(_WIN32) + cl_context_properties props[] = { + CL_GL_CONTEXT_KHR, + (cl_context_properties)wglGetCurrentContext(), + CL_WGL_HDC_KHR, + (cl_context_properties)wglGetCurrentDC(), + CL_CONTEXT_PLATFORM, + (cl_context_properties)cp_platform, + 0}; +#elif defined(__APPLE__) + CGLContextObj kCGLContext = CGLGetCurrentContext(); + CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext); + cl_context_properties props[] = {CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, + (cl_context_properties)kCGLShareGroup, + 0}; +#else + cl_context_properties props[] = { + CL_GL_CONTEXT_KHR, + (cl_context_properties)glXGetCurrentContext(), + CL_GLX_DISPLAY_KHR, + (cl_context_properties)glXGetCurrentDisplay(), + CL_CONTEXT_PLATFORM, + (cl_context_properties)cp_platform, + 0}; +#endif + +#if defined(__APPLE__) + _clContext = clCreateContext(props, 0, NULL, clLogMessagesToStdoutAPPLE, NULL, + &cl_error_number); + if (cl_error_number != CL_SUCCESS) { + error("Error %d in clCreateContext\n", cl_error_number); + return false; + } + size_t devices_size = 0; + clGetGLContextInfoAPPLE(_clContext, kCGLContext, + CL_CGL_DEVICES_FOR_SUPPORTED_VIRTUAL_SCREENS_APPLE, + 0, + NULL, + &devices_size); + const int num_devices = devices_size / sizeof(cl_device_id); + if (num_devices == 0) { + error("No sharable devices.\n"); + return false; + } + std::vector<cl_device_id> cl_devices(num_devices); + clGetGLContextInfoAPPLE(_clContext, kCGLContext, + CL_CGL_DEVICES_FOR_SUPPORTED_VIRTUAL_SCREENS_APPLE, + num_devices * sizeof(cl_device_id), + &cl_devices[0], + NULL); + int cl_device_used = 0; +#else // not __APPLE__ + // Get the number of GPU devices available to the platform. + cl_uint num_devices = 0; + clGetDeviceIDs(cp_platform, CL_DEVICE_TYPE_GPU, 0, NULL, &num_devices); + if (num_devices == 0) { + error("No CL GPU device found.\n"); + return false; + } + // Create the device list. + std::vector<cl_device_id> cl_devices(num_devices); + clGetDeviceIDs(cp_platform, + CL_DEVICE_TYPE_GPU, + num_devices, + &cl_devices[0], + NULL); + const char* extension = "cl_khr_gl_sharing"; + int cl_device_used = findExtensionSupportedDevice(&cl_devices[0], + num_devices, + extension); + if (cl_device_used < 0) { + error("No device found that supports CL/GL context sharing\n"); + return false; + } + cl_context_ = clCreateContext(props, + 1, + &cl_devices[cl_device_used], + NULL, NULL, + &cl_error_number); +#endif // not __APPLE__ + if (cl_error_number != CL_SUCCESS) { + error("Error %d in clCreateContext\n", cl_error_number); + return false; + } + cl_command_queue_ = clCreateCommandQueue(cl_context_, + cl_devices[cl_device_used], + 0, + &cl_error_number); + if (cl_error_number != CL_SUCCESS) { + error("Error %d in clCreateCommandQueue\n", cl_error_number); + return false; + } + return true; +} + +bool CLDeviceContext::IsInitialized() const { + return (cl_context_ != NULL); +} + +cl_context CLDeviceContext::GetContext() const { + return cl_context_; +} + +cl_command_queue CLDeviceContext::GetCommandQueue() const { + return cl_command_queue_; +} + +#endif // OPENSUBDIV_HAS_OPENCL diff --git a/intern/opensubdiv/internal/opensubdiv_device_context_opencl.h b/intern/opensubdiv/internal/opensubdiv_device_context_opencl.h new file mode 100644 index 00000000000..bc751cb6954 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_device_context_opencl.h @@ -0,0 +1,52 @@ +// Adopted from OpenSubdiv with the following license: +// +// Copyright 2015 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. + +#ifndef OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ +#define OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ + +#ifdef OPENSUBDIV_HAS_OPENCL +#include <opensubdiv/osd/opencl.h> + +class CLDeviceContext { + public: + static bool HAS_CL_VERSION_1_1(); + + CLDeviceContext(); + ~CLDeviceContext(); + + bool Initialize(); + + bool IsInitialized() const; + + cl_context GetContext() const; + cl_command_queue GetCommandQueue() const; + + protected: + cl_context cl_context_; + cl_command_queue cl_command_queue_; +}; + +#endif // OPENSUBDIV_HAS_OPENCL + +#endif // _OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator.cc b/intern/opensubdiv/internal/opensubdiv_evaluator.cc new file mode 100644 index 00000000000..29d42a903ba --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_evaluator.cc @@ -0,0 +1,154 @@ +// Copyright 2015 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "opensubdiv_evaluator_capi.h" + +#include <new> +#include "MEM_guardedalloc.h" + +#include "internal/opensubdiv_evaluator_internal.h" + +namespace { + +void setCoarsePositions(OpenSubdiv_Evaluator* evaluator, + const float* positions, + const int start_vertex_index, const int num_vertices) { + evaluator->internal->eval_output->setCoarsePositions(positions, + start_vertex_index, + num_vertices); +} + +void setVaryingData(OpenSubdiv_Evaluator* evaluator, + const float* varying_data, + const int start_vertex_index, const int num_vertices) { + evaluator->internal->eval_output->setVaryingData(varying_data, + start_vertex_index, + num_vertices); +} + +void setFaceVaryingData(OpenSubdiv_Evaluator* evaluator, + const float* face_varying_data, + const int start_vertex_index, const int num_vertices) { + evaluator->internal->eval_output->setFaceVaryingData(face_varying_data, + start_vertex_index, + num_vertices); +} + +void setCoarsePositionsFromBuffer(OpenSubdiv_Evaluator* evaluator, + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { + evaluator->internal->eval_output->setCoarsePositionsFromBuffer( + buffer, + start_offset, + stride, + start_vertex_index, + num_vertices); +} + +void setVaryingDataFromBuffer(OpenSubdiv_Evaluator* evaluator, + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { + evaluator->internal->eval_output->setVaryingDataFromBuffer( + buffer, + start_offset, + stride, + start_vertex_index, + num_vertices); +} + +void setFaceVaryingDataFromBuffer(OpenSubdiv_Evaluator* evaluator, + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { + evaluator->internal->eval_output->setFaceVaryingDataFromBuffer( + buffer, + start_offset, + stride, + start_vertex_index, + num_vertices); +} + +void refine(OpenSubdiv_Evaluator* evaluator) { + evaluator->internal->eval_output->refine(); +} + +void evaluateLimit(OpenSubdiv_Evaluator* evaluator, + const int ptex_face_index, + const float face_u, const float face_v, + float P[3], float dPdu[3], float dPdv[3]) { + evaluator->internal->eval_output->evaluateLimit(ptex_face_index, + face_u, face_v, + P, dPdu, dPdv); +} + +void evaluateVarying(OpenSubdiv_Evaluator* evaluator, + const int ptex_face_index, + float face_u, float face_v, + float varying[3]) { + evaluator->internal->eval_output->evaluateVarying(ptex_face_index, + face_u, face_v, + varying); +} + +void evaluateFaceVarying(OpenSubdiv_Evaluator* evaluator, + const int ptex_face_index, + float face_u, float face_v, + float face_varying[2]) { + evaluator->internal->eval_output->evaluateFaceVarying(ptex_face_index, + face_u, face_v, + face_varying); +} + +void assignFunctionPointers(OpenSubdiv_Evaluator* evaluator) { + evaluator->setCoarsePositions = setCoarsePositions; + evaluator->setVaryingData = setVaryingData; + evaluator->setFaceVaryingData = setFaceVaryingData; + + evaluator->setCoarsePositionsFromBuffer = setCoarsePositionsFromBuffer; + evaluator->setVaryingDataFromBuffer = setVaryingDataFromBuffer; + evaluator->setFaceVaryingDataFromBuffer = setFaceVaryingDataFromBuffer; + + evaluator->refine = refine; + + evaluator->evaluateLimit = evaluateLimit; + evaluator->evaluateVarying = evaluateVarying; + evaluator->evaluateFaceVarying = evaluateFaceVarying; +} + +} // namespace + +OpenSubdiv_Evaluator* openSubdiv_createEvaluatorFromTopologyRefiner( + OpenSubdiv_TopologyRefiner* topology_refiner) { + OpenSubdiv_Evaluator* evaluator = OBJECT_GUARDED_NEW(OpenSubdiv_Evaluator); + assignFunctionPointers(evaluator); + evaluator->internal = openSubdiv_createEvaluatorInternal(topology_refiner); + return evaluator; +} + +void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator* evaluator) { + openSubdiv_deleteEvaluatorInternal(evaluator->internal); + OBJECT_GUARDED_DELETE(evaluator, OpenSubdiv_Evaluator); +} diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc new file mode 100644 index 00000000000..595df3eaa75 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc @@ -0,0 +1,734 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/opensubdiv_evaluator_internal.h" + +#include <cassert> +#include <cstdio> +#include <vector> + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/far/patchMap.h> +#include <opensubdiv/far/patchTable.h> +#include <opensubdiv/far/patchTableFactory.h> +#include <opensubdiv/osd/cpuEvaluator.h> +#include <opensubdiv/osd/cpuPatchTable.h> +#include <opensubdiv/osd/cpuVertexBuffer.h> +#include <opensubdiv/osd/mesh.h> +#include <opensubdiv/osd/types.h> + +#include "MEM_guardedalloc.h" + +#include "internal/opensubdiv_topology_refiner_internal.h" +#include "opensubdiv_topology_refiner_capi.h" + +using OpenSubdiv::Osd::BufferDescriptor; +using OpenSubdiv::Osd::CpuEvaluator; +using OpenSubdiv::Osd::CpuPatchTable; +using OpenSubdiv::Osd::CpuVertexBuffer; +using OpenSubdiv::Osd::PatchCoord; +using OpenSubdiv::Far::PatchMap; +using OpenSubdiv::Far::PatchTable; +using OpenSubdiv::Far::PatchTableFactory; +using OpenSubdiv::Far::StencilTable; +using OpenSubdiv::Far::StencilTableFactory; +using OpenSubdiv::Far::TopologyRefiner; + +namespace opensubdiv_capi { + +namespace { + +// Helper class to wrap numerous of patch coordinates into a buffer. +// Used to pass coordinates to the CPU evaluator. Other evaluators are not +// supported. +class PatchCoordBuffer : public std::vector<PatchCoord> { + public: + static PatchCoordBuffer* Create(int size) { + PatchCoordBuffer* buffer = new PatchCoordBuffer(); + buffer->resize(size); + return buffer; + } + + PatchCoord* BindCpuBuffer() { + return reinterpret_cast<PatchCoord*>(&(*this)[0]); + } + + int GetNumVertices() { + return size(); + } + + void UpdateData(const PatchCoord* patch_coords, int num_patch_coords) { + memcpy(&(*this)[0], + reinterpret_cast<const void*>(patch_coords), + sizeof(PatchCoord) * num_patch_coords); + } +}; + +// Helper class to wrap single of patch coord into a buffer. Used to pass +// coordinates to the CPU evaluator. Other evaluators are not supported. +class SinglePatchCoordBuffer { + public: + static SinglePatchCoordBuffer* Create() { + return new SinglePatchCoordBuffer(); + } + + SinglePatchCoordBuffer() { + } + + explicit SinglePatchCoordBuffer(const PatchCoord &patch_coord) + : patch_coord_(patch_coord) { + } + + PatchCoord* BindCpuBuffer() { + return &patch_coord_; + } + + int GetNumVertices() { + return 1; + } + + void UpdateData(const PatchCoord &patch_coord) { + patch_coord_ = patch_coord; + } + + protected: + PatchCoord patch_coord_; +}; + +// Helper class which is aimed to be used in cases when buffer is small enough +// and better to be allocated in stack rather than in heap. +// +// TODO(sergey): Check if bare arrays could be used by CPU evaluator. +template <int element_size, int num_vertices> +class StackAllocatedBuffer { + public: + static PatchCoordBuffer* Create(int /*size*/) { + // TODO(sergey): Validate that requested dize is smaller than static + // stack memory size. + return new StackAllocatedBuffer<element_size, num_vertices>(); + } + + float* BindCpuBuffer() { + return &data_[0]; + } + + int GetNumVertices() { + return num_vertices; + } + + // TODO(sergey): Support UpdateData(). + protected: + float data_[element_size * num_vertices]; +}; + +// Volatile evaluator which can be used from threads. +// +// TODO(sergey): Make it possible to evaluate coordinates in chunks. +// TODO(sergey): Make it possible to evaluate multiple face varying layers. +// (or maybe, it's cheap to create new evaluator for existing +// topology to evaluate all needed face varying layers?) +template <typename SRC_VERTEX_BUFFER, + typename EVAL_VERTEX_BUFFER, + typename STENCIL_TABLE, + typename PATCH_TABLE, + typename EVALUATOR, + typename DEVICE_CONTEXT = void> +class VolatileEvalOutput { + public: + typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache; + + VolatileEvalOutput(const StencilTable* vertex_stencils, + const StencilTable* varying_stencils, + const StencilTable* face_varying_stencils, + const int face_varying_channel, + const int face_varying_width, + const PatchTable* patch_table, + EvaluatorCache* evaluator_cache = NULL, + DEVICE_CONTEXT* device_context = NULL) + : src_desc_(0, 3, 3), + src_varying_desc_(0, 3, 3), + src_face_varying_desc_(0, face_varying_width, face_varying_width), + face_varying_channel_(face_varying_channel), + face_varying_width_(face_varying_width), + evaluator_cache_(evaluator_cache), + device_context_(device_context) { + // Total number of vertices = coarse points + refined points + local points. + int num_total_vertices = vertex_stencils->GetNumControlVertices() + + vertex_stencils->GetNumStencils(); + num_coarse_vertices_ = vertex_stencils->GetNumControlVertices(); + using OpenSubdiv::Osd::convertToCompatibleStencilTable; + src_data_ = + SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); + src_varying_data_ = + SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); + patch_table_ = PATCH_TABLE::Create(patch_table, device_context_); + patch_coords_ = NULL; + vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>( + vertex_stencils, device_context_); + varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>( + varying_stencils, device_context_); + if (face_varying_stencils != NULL) { + num_coarse_face_varying_vertices_ = + face_varying_stencils->GetNumControlVertices(); + const int num_total_face_varying_vertices = + face_varying_stencils->GetNumControlVertices() + + face_varying_stencils->GetNumStencils(); + src_face_varying_data_ = + EVAL_VERTEX_BUFFER::Create(2, + num_total_face_varying_vertices, + device_context_); + face_varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>( + face_varying_stencils, device_context_); + } else { + num_coarse_face_varying_vertices_ = 0; + src_face_varying_data_ = NULL; + face_varying_stencils_ = NULL; + } + } + + ~VolatileEvalOutput() { + delete src_data_; + delete src_varying_data_; + delete src_face_varying_data_; + delete patch_table_; + delete vertex_stencils_; + delete varying_stencils_; + delete face_varying_stencils_; + } + + // TODO(sergey): Implement binding API. + + void updateData(const float* src, int start_vertex, int num_vertices) { + src_data_->UpdateData(src, start_vertex, num_vertices, device_context_); + } + + void updateVaryingData(const float* src, int start_vertex, int num_vertices) { + src_varying_data_->UpdateData(src, + start_vertex, + num_vertices, + device_context_); + } + + void updateFaceVaryingData(const float* src, + int start_vertex, + int num_vertices) { + src_face_varying_data_->UpdateData(src, + start_vertex, + num_vertices, + device_context_); + } + + bool hasVaryingData() const { + // return varying_stencils_ != NULL; + // TODO(sergey): Check this based on actual topology. + return false; + } + + bool hasFaceVaryingData() const { + return face_varying_stencils_ != NULL; + } + + void refine() { + // Evaluate vertex positions. + BufferDescriptor dst_desc = src_desc_; + dst_desc.offset += num_coarse_vertices_ * src_desc_.stride; + const EVALUATOR* eval_instance = + OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, + src_desc_, + dst_desc, + device_context_); + EVALUATOR::EvalStencils(src_data_, src_desc_, + src_data_, dst_desc, + vertex_stencils_, + eval_instance, + device_context_); + // Evaluate varying data. + if (hasVaryingData()) { + BufferDescriptor dst_varying_desc = src_varying_desc_; + dst_varying_desc.offset += + num_coarse_vertices_ * src_varying_desc_.stride; + eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, + src_varying_desc_, + dst_varying_desc, + device_context_); + EVALUATOR::EvalStencils(src_varying_data_, src_varying_desc_, + src_varying_data_, dst_varying_desc, + varying_stencils_, + eval_instance, device_context_); + } + // Evaluate face-varying data. + if (hasFaceVaryingData()) { + BufferDescriptor dst_face_varying_desc = src_face_varying_desc_; + dst_face_varying_desc.offset += num_coarse_face_varying_vertices_ * + src_face_varying_desc_.stride; + eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, + src_face_varying_desc_, + dst_face_varying_desc, + device_context_); + EVALUATOR::EvalStencils(src_face_varying_data_, src_face_varying_desc_, + src_face_varying_data_, dst_face_varying_desc, + face_varying_stencils_, + eval_instance, + device_context_); + } + } + + void evalPatchCoord(const PatchCoord& patch_coord, float P[3]) { + StackAllocatedBuffer<6, 1> vertex_data; + // TODO(sergey): Varying data is interleaved in vertex array, so need to + // adjust stride if there is a varying data. + // BufferDescriptor vertex_desc(0, 3, 6); + BufferDescriptor vertex_desc(0, 3, 3); + SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + const EVALUATOR* eval_instance = + OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, + src_desc_, + vertex_desc, + device_context_); + EVALUATOR::EvalPatches(src_data_, + src_desc_, + &vertex_data, + vertex_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + eval_instance, + device_context_); + const float* refined_vertices = vertex_data.BindCpuBuffer(); + memcpy(P, refined_vertices, sizeof(float) * 3); + } + + void evalPatchesWithDerivatives(const PatchCoord& patch_coord, + float P[3], + float dPdu[3], float dPdv[3]) { + StackAllocatedBuffer<6, 1> vertex_data, derivatives; + // TODO(sergey): Varying data is interleaved in vertex array, so need to + // adjust stride if there is a varying data. + // BufferDescriptor vertex_desc(0, 3, 6); + BufferDescriptor vertex_desc(0, 3, 3); + BufferDescriptor du_desc(0, 3, 6), dv_desc(3, 3, 6); + SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + const EVALUATOR* eval_instance = + OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, + src_desc_, + vertex_desc, + du_desc, dv_desc, + device_context_); + EVALUATOR::EvalPatches(src_data_, src_desc_, + &vertex_data, vertex_desc, + &derivatives, du_desc, + &derivatives, dv_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + eval_instance, + device_context_); + const float* refined_vertices = vertex_data.BindCpuBuffer(); + memcpy(P, refined_vertices, sizeof(float) * 3); + if (dPdu != NULL || dPdv != NULL) { + const float* refined_derivatives = derivatives.BindCpuBuffer(); + if (dPdu != NULL) { + memcpy(dPdu, refined_derivatives, sizeof(float) * 3); + } + if (dPdv != NULL) { + memcpy(dPdv, refined_derivatives + 3, sizeof(float) * 3); + } + } + } + + void evalPatchVarying(const PatchCoord& patch_coord, float varying[3]) { + StackAllocatedBuffer<6, 1> varying_data; + BufferDescriptor varying_desc(3, 3, 6); + SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + const EVALUATOR* eval_instance = + OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, + src_varying_desc_, + varying_desc, + device_context_); + EVALUATOR::EvalPatchesVarying(src_varying_data_, src_varying_desc_, + &varying_data, varying_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + eval_instance, + device_context_); + const float* refined_varying = varying_data.BindCpuBuffer(); + memcpy(varying, refined_varying, sizeof(float) * 3); + } + + void evalPatchFaceVarying(const PatchCoord& patch_coord, + float face_varying[2]) { + StackAllocatedBuffer<2, 1> face_varying_data; + BufferDescriptor face_varying_desc(0, 2, 2); + SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + const EVALUATOR* eval_instance = + OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, + src_face_varying_desc_, + face_varying_desc, + device_context_); + EVALUATOR::EvalPatchesFaceVarying( + src_face_varying_data_, src_face_varying_desc_, + &face_varying_data, face_varying_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + face_varying_channel_, + eval_instance, + device_context_); + const float* refined_face_varying = face_varying_data.BindCpuBuffer(); + memcpy(face_varying, refined_face_varying, sizeof(float) * 2); + } + + private: + SRC_VERTEX_BUFFER* src_data_; + SRC_VERTEX_BUFFER* src_varying_data_; + EVAL_VERTEX_BUFFER* src_face_varying_data_; + PatchCoordBuffer* patch_coords_; + PATCH_TABLE* patch_table_; + BufferDescriptor src_desc_; + BufferDescriptor src_varying_desc_; + BufferDescriptor src_face_varying_desc_; + + int num_coarse_vertices_; + int face_varying_channel_; + int face_varying_width_; + int num_coarse_face_varying_vertices_; + + const STENCIL_TABLE* vertex_stencils_; + const STENCIL_TABLE* varying_stencils_; + const STENCIL_TABLE* face_varying_stencils_; + + EvaluatorCache* evaluator_cache_; + DEVICE_CONTEXT* device_context_; +}; + +} // namespace + +// Note: Define as a class instead of typedcef to make it possible +// to have anonymous class in opensubdiv_evaluator_internal.h +class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer, + CpuVertexBuffer, + StencilTable, + CpuPatchTable, + CpuEvaluator> { + public: + CpuEvalOutput(const StencilTable* vertex_stencils, + const StencilTable* varying_stencils, + const StencilTable* face_varying_stencils, + const int face_varying_channel, + const int face_varying_width, + const PatchTable* patch_table, + EvaluatorCache* evaluator_cache = NULL) + : VolatileEvalOutput<CpuVertexBuffer, + CpuVertexBuffer, + StencilTable, + CpuPatchTable, + CpuEvaluator> (vertex_stencils, + varying_stencils, + face_varying_stencils, + face_varying_channel, + face_varying_width, + patch_table, + evaluator_cache) { + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Evaluator wrapper for anonymous API. + +CpuEvalOutputAPI::CpuEvalOutputAPI(CpuEvalOutput* implementation, + OpenSubdiv::Far::PatchMap* patch_map) + : implementation_(implementation), + patch_map_(patch_map) { +} + +CpuEvalOutputAPI::~CpuEvalOutputAPI() { + delete implementation_; +} + +void CpuEvalOutputAPI::setCoarsePositions(const float* positions, + const int start_vertex_index, + const int num_vertices) { + // TODO(sergey): Add sanity check on indices. + implementation_->updateData(positions, start_vertex_index, num_vertices); +} + +void CpuEvalOutputAPI::setVaryingData(const float* varying_data, + const int start_vertex_index, + const int num_vertices) { + // TODO(sergey): Add sanity check on indices. + implementation_->updateVaryingData(varying_data, + start_vertex_index, + num_vertices); +} + +void CpuEvalOutputAPI::setFaceVaryingData(const float* face_varying_data, + const int start_vertex_index, + const int num_vertices) { + // TODO(sergey): Add sanity check on indices. + implementation_->updateFaceVaryingData(face_varying_data, + start_vertex_index, + num_vertices); +} + +void CpuEvalOutputAPI::setCoarsePositionsFromBuffer( + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { + // TODO(sergey): Add sanity check on indices. + const unsigned char* current_buffer = (unsigned char *)buffer; + current_buffer += start_offset; + for (int i = 0; i < num_vertices; ++i) { + const int current_vertex_index = start_vertex_index + i; + implementation_->updateData(reinterpret_cast<const float*>(current_buffer), + current_vertex_index, 1); + current_buffer += stride; + } +} + +void CpuEvalOutputAPI::setVaryingDataFromBuffer( + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { + // TODO(sergey): Add sanity check on indices. + const unsigned char* current_buffer = (unsigned char *)buffer; + current_buffer += start_offset; + for (int i = 0; i < num_vertices; ++i) { + const int current_vertex_index = start_vertex_index + i; + implementation_->updateVaryingData( + reinterpret_cast<const float*>(current_buffer), + current_vertex_index, 1); + current_buffer += stride; + } +} + +void CpuEvalOutputAPI::setFaceVaryingDataFromBuffer( + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { + // TODO(sergey): Add sanity check on indices. + const unsigned char* current_buffer = (unsigned char *)buffer; + current_buffer += start_offset; + for (int i = 0; i < num_vertices; ++i) { + const int current_vertex_index = start_vertex_index + i; + implementation_->updateFaceVaryingData( + reinterpret_cast<const float*>(current_buffer), + current_vertex_index, 1); + current_buffer += stride; + } +} + +void CpuEvalOutputAPI::refine() { + implementation_->refine(); +} + +void CpuEvalOutputAPI::evaluateLimit(const int ptex_face_index, + float face_u, float face_v, + float P[3], float dPdu[3], float dPdv[3]) { + assert(face_u >= 0.0f); + assert(face_u <= 1.0f); + assert(face_v >= 0.0f); + assert(face_v <= 1.0f); + const PatchTable::PatchHandle* handle = + patch_map_->FindPatch(ptex_face_index, face_u, face_v); + PatchCoord patch_coord(*handle, face_u, face_v); + if (dPdu != NULL || dPdv != NULL) { + implementation_->evalPatchesWithDerivatives(patch_coord, P, dPdu, dPdv); + } else { + implementation_->evalPatchCoord(patch_coord, P); + }} + +void CpuEvalOutputAPI::evaluateVarying(const int ptex_face_index, + float face_u, float face_v, + float varying[3]) { + assert(face_u >= 0.0f); + assert(face_u <= 1.0f); + assert(face_v >= 0.0f); + assert(face_v <= 1.0f); + const PatchTable::PatchHandle* handle = + patch_map_->FindPatch(ptex_face_index, face_u, face_v); + PatchCoord patch_coord(*handle, face_u, face_v); + implementation_->evalPatchVarying(patch_coord, varying); +} + +void CpuEvalOutputAPI::evaluateFaceVarying(const int ptex_face_index, + float face_u, float face_v, + float face_varying[2]) { + assert(face_u >= 0.0f); + assert(face_u <= 1.0f); + assert(face_v >= 0.0f); + assert(face_v <= 1.0f); + const PatchTable::PatchHandle* handle = + patch_map_->FindPatch(ptex_face_index, face_u, face_v); + PatchCoord patch_coord(*handle, face_u, face_v); + implementation_->evalPatchFaceVarying(patch_coord, face_varying); +} + +} // namespace opensubdiv_capi + +OpenSubdiv_EvaluatorInternal::OpenSubdiv_EvaluatorInternal() + : eval_output(NULL), + patch_map(NULL), + patch_table(NULL) { +} + +OpenSubdiv_EvaluatorInternal::~OpenSubdiv_EvaluatorInternal() { + delete eval_output; + delete patch_map; + delete patch_table; +} + +OpenSubdiv_EvaluatorInternal* openSubdiv_createEvaluatorInternal( + OpenSubdiv_TopologyRefiner* topology_refiner) { + TopologyRefiner* refiner = topology_refiner->internal->osd_topology_refiner; + if (refiner == NULL) { + // Happens on bad topology. + return NULL; + } + // TODO(sergey): Base this on actual topology. + // const bool bas_varying_data = false; + const bool has_face_varying_data = + (refiner->GetNumFVarChannels() != 0); + const int level = topology_refiner->getSubdivisionLevel(topology_refiner); + // TODO(sergey): Query from topology refiner. + const bool is_adaptive = topology_refiner->getIsAdaptive(topology_refiner); + // Refine the topology with given settings. + // TODO(sergey): What if topology is already refined? + if (is_adaptive) { + TopologyRefiner::AdaptiveOptions options(level); + options.considerFVarChannels = has_face_varying_data; + options.useInfSharpPatch = false; + refiner->RefineAdaptive(options); + } else { + TopologyRefiner::UniformOptions options(level); + refiner->RefineUniform(options); + } + // Generate stencil table to update the bi-cubic patches control vertices + // after they have been re-posed (both for vertex & varying interpolation). + // + // Vertex stencils. + StencilTableFactory::Options vertex_stencil_options; + vertex_stencil_options.generateOffsets = true; + vertex_stencil_options.generateIntermediateLevels = is_adaptive; + const StencilTable* vertex_stencils = + StencilTableFactory::Create(*refiner, vertex_stencil_options); + // Varying stencils. + // + // TODO(sergey): Seems currently varying stencils are always required in + // OpenSubdiv itself. + const StencilTable* varying_stencils = NULL; + StencilTableFactory::Options varying_stencil_options; + varying_stencil_options.generateOffsets = true; + varying_stencil_options.generateIntermediateLevels = is_adaptive; + varying_stencil_options.interpolationMode = + StencilTableFactory::INTERPOLATE_VARYING; + varying_stencils = + StencilTableFactory::Create(*refiner, varying_stencil_options); + // Face warying stencil. + const StencilTable* face_varying_stencils = NULL; + if (has_face_varying_data) { + StencilTableFactory::Options face_varying_stencil_options; + face_varying_stencil_options.generateOffsets = true; + face_varying_stencil_options.generateIntermediateLevels = is_adaptive; + face_varying_stencil_options.interpolationMode = + StencilTableFactory::INTERPOLATE_FACE_VARYING; + // TODO(sergey): Make it configurable which face varying channel is being + // interpolated. + face_varying_stencil_options.fvarChannel = 0; + face_varying_stencils = + StencilTableFactory::Create(*refiner, face_varying_stencil_options); + } + // Generate bi-cubic patch table for the limit surface. + // TODO(sergey): Ideally we would want to expose end-cap settings via + // C-API to make it more generic. Currently it matches old Blender's + // subsurf code. + PatchTableFactory::Options patch_options(level); + patch_options.SetEndCapType(PatchTableFactory::Options::ENDCAP_BSPLINE_BASIS); + patch_options.useInfSharpPatch = false; + patch_options.generateFVarTables = has_face_varying_data; + patch_options.generateFVarLegacyLinearPatches = false; + const PatchTable* patch_table = PatchTableFactory::Create( + *refiner, patch_options); + // Append local points stencils. + const StencilTable* local_point_stencil_table = + patch_table->GetLocalPointStencilTable(); + if (local_point_stencil_table != NULL) { + const StencilTable* table = + StencilTableFactory::AppendLocalPointStencilTable( + *refiner, vertex_stencils, local_point_stencil_table); + delete vertex_stencils; + vertex_stencils = table; + } + const StencilTable* local_point_varying_stencil_table = + patch_table->GetLocalPointVaryingStencilTable(); + if (local_point_varying_stencil_table != NULL) { + const StencilTable* table = + StencilTableFactory::AppendLocalPointStencilTable( + *refiner, varying_stencils, local_point_varying_stencil_table); + delete varying_stencils; + varying_stencils = table; + } + const StencilTable* local_point_face_varying_stencil_table = + patch_table->GetLocalPointFaceVaryingStencilTable(); + if (local_point_face_varying_stencil_table != NULL) { + const StencilTable* table = + StencilTableFactory::AppendLocalPointStencilTableFaceVarying( + *refiner, + face_varying_stencils, + local_point_face_varying_stencil_table); + delete face_varying_stencils; + face_varying_stencils = table; + } + // Create OpenSubdiv's CPU side evaluator. + // TODO(sergey): Make it possible to use different evaluators. + opensubdiv_capi::CpuEvalOutput* eval_output = + new opensubdiv_capi::CpuEvalOutput(vertex_stencils, + varying_stencils, + face_varying_stencils, + 0, 2, + patch_table); + OpenSubdiv::Far::PatchMap* patch_map = new PatchMap(*patch_table); + // Wrap everything we need into an object which we control from our side. + OpenSubdiv_EvaluatorInternal* evaluator_descr; + evaluator_descr = OBJECT_GUARDED_NEW(OpenSubdiv_EvaluatorInternal); + evaluator_descr->eval_output = + new opensubdiv_capi::CpuEvalOutputAPI(eval_output, patch_map); + evaluator_descr->patch_map = patch_map; + evaluator_descr->patch_table = patch_table; + // TOOD(sergey): Look into whether we've got duplicated stencils arrays. + delete vertex_stencils; + delete varying_stencils; + delete face_varying_stencils; + return evaluator_descr; +} + +void openSubdiv_deleteEvaluatorInternal( + OpenSubdiv_EvaluatorInternal* evaluator) { + OBJECT_GUARDED_DELETE(evaluator, OpenSubdiv_EvaluatorInternal); +} diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h new file mode 100644 index 00000000000..cc69db6abc6 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h @@ -0,0 +1,134 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_EVALUATOR_INTERNAL_H_ +#define OPENSUBDIV_EVALUATOR_INTERNAL_H_ + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/far/patchMap.h> +#include <opensubdiv/far/patchTable.h> + +struct OpenSubdiv_TopologyRefiner; + +namespace opensubdiv_capi { + +// Anonymous forward declaration of actual evaluator implementation. +class CpuEvalOutput; + +// Wrapper around implementaiton, which defines API which we are capable to +// provide over the implementation. +// +// TODO(sergey): It is almost the same as C-API object, so ideally need to +// merge them somehow, but how to do this and keep files with all the templates +// and such separate? +class CpuEvalOutputAPI { + public: + // NOTE: API object becomes an owner of evaluator. Patch we are referencing. + CpuEvalOutputAPI(CpuEvalOutput* implementation, + OpenSubdiv::Far::PatchMap* patch_map); + ~CpuEvalOutputAPI(); + + // Set coarse positions from a continuous array of coordinates. + void setCoarsePositions(const float* positions, + const int start_vertex_index, + const int num_vertices); + // Set varying data from a continuous array of data. + void setVaryingData(const float* varying_data, + const int start_vertex_index, const int num_vertices); + // Set face varying data from a continuous array of data. + // + // TODO(sergey): Find a better name for vertex here. It is not the vertex of + // geometry, but a vertex of UV map. + void setFaceVaryingData(const float* varying_data, + const int start_vertex_index, const int num_vertices); + + // Set coarse vertex position from a continuous memory buffer where + // first coordinate starts at offset of `start_offset` and there is `stride` + // bytes between adjacent vertex coordinates. + void setCoarsePositionsFromBuffer(const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices); + // Set varying data from a continuous memory buffer where + // first coordinate starts at offset of `start_offset` and there is `stride` + // bytes between adjacent vertex coordinates. + void setVaryingDataFromBuffer(const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices); + // Set face varying data from a continuous memory buffer where + // first coordinate starts at offset of `start_offset` and there is `stride` + // bytes between adjacent vertex coordinates. + // + // TODO(sergey): Find a better name for vertex here. It is not the vertex of + // geometry, but a vertex of UV map. + void setFaceVaryingDataFromBuffer(const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices); + + // Refine after coarse positions update. + void refine(); + + // Evaluate given ptex face at given bilinear coordinate. + // If derivatives are NULL, they will not be evaluated. + void evaluateLimit(const int ptex_face_index, + float face_u, float face_v, + float P[3], float dPdu[3], float dPdv[3]); + + // Evaluate varying data at a given bilinear coordinate of given ptex face. + void evaluateVarying(const int ptes_face_index, + float face_u, float face_v, + float varying[3]); + + // Evaluate facee-varying data at a given bilinear coordinate of given + // ptex face. + void evaluateFaceVarying(const int ptes_face_index, + float face_u, float face_v, + float face_varying[2]); + + protected: + CpuEvalOutput* implementation_; + OpenSubdiv::Far::PatchMap* patch_map_; +}; + +} // namespace opensubdiv_capi + +struct OpenSubdiv_EvaluatorInternal { + public: + OpenSubdiv_EvaluatorInternal(); + ~OpenSubdiv_EvaluatorInternal(); + + opensubdiv_capi::CpuEvalOutputAPI* eval_output; + const OpenSubdiv::Far::PatchMap* patch_map; + const OpenSubdiv::Far::PatchTable* patch_table; +}; + +OpenSubdiv_EvaluatorInternal* openSubdiv_createEvaluatorInternal( + struct OpenSubdiv_TopologyRefiner* topology_refiner); + +void openSubdiv_deleteEvaluatorInternal( + OpenSubdiv_EvaluatorInternal* evaluator); + +#endif // OPENSUBDIV_EVALUATOR_INTERNAL_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh.cc b/intern/opensubdiv/internal/opensubdiv_gl_mesh.cc new file mode 100644 index 00000000000..34d27591d0b --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh.cc @@ -0,0 +1,287 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin +// Contributor(s): Brecht van Lommel + +#include "opensubdiv_gl_mesh_capi.h" + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/far/stencilTable.h> +#include <opensubdiv/osd/glMesh.h> +#include <opensubdiv/osd/glPatchTable.h> + +using OpenSubdiv::Far::StencilTable; +using OpenSubdiv::Osd::GLMeshInterface; +using OpenSubdiv::Osd::GLPatchTable; +using OpenSubdiv::Osd::Mesh; +using OpenSubdiv::Osd::MeshBitset; + +// CPU backend. +#include <opensubdiv/osd/cpuEvaluator.h> +#include <opensubdiv/osd/cpuGLVertexBuffer.h> +using OpenSubdiv::Osd::CpuGLVertexBuffer; +using OpenSubdiv::Osd::CpuEvaluator; +typedef Mesh<CpuGLVertexBuffer, + StencilTable, + CpuEvaluator, + GLPatchTable> OsdCpuMesh; +// OpenMP backend. +#ifdef OPENSUBDIV_HAS_OPENMP +# include <opensubdiv/osd/ompEvaluator.h> +using OpenSubdiv::Osd::OmpEvaluator; +typedef Mesh<CpuGLVertexBuffer, + StencilTable, + OmpEvaluator, + GLPatchTable> OsdOmpMesh; +#endif +// OpenCL backend. +#ifdef OPENSUBDIV_HAS_OPENCL +# include <opensubdiv/osd/clEvaluator.h> +# include <opensubdiv/osd/clGLVertexBuffer.h> +# include "opensubdiv_device_context_opencl.h" +using OpenSubdiv::Osd::CLEvaluator; +using OpenSubdiv::Osd::CLGLVertexBuffer; +using OpenSubdiv::Osd::CLStencilTable; +/* TODO(sergey): Use CLDeviceContext similar to OSD examples? */ +typedef Mesh<CLGLVertexBuffer, + CLStencilTable, + CLEvaluator, + GLPatchTable, + CLDeviceContext> OsdCLMesh; +static CLDeviceContext g_cl_device_context; +#endif +// CUDA backend. +#ifdef OPENSUBDIV_HAS_CUDA +# include <opensubdiv/osd/cudaEvaluator.h> +# include <opensubdiv/osd/cudaGLVertexBuffer.h> +# include "opensubdiv_device_context_cuda.h" +using OpenSubdiv::Osd::CudaEvaluator; +using OpenSubdiv::Osd::CudaGLVertexBuffer; +using OpenSubdiv::Osd::CudaStencilTable; +typedef Mesh<CudaGLVertexBuffer, + CudaStencilTable, + CudaEvaluator, + GLPatchTable> OsdCudaMesh; +static CudaDeviceContext g_cuda_device_context; +#endif +// Transform feedback backend. +#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK +# include <opensubdiv/osd/glVertexBuffer.h> +# include <opensubdiv/osd/glXFBEvaluator.h> +using OpenSubdiv::Osd::GLXFBEvaluator; +using OpenSubdiv::Osd::GLStencilTableTBO; +using OpenSubdiv::Osd::GLVertexBuffer; +typedef Mesh<GLVertexBuffer, + GLStencilTableTBO, + GLXFBEvaluator, + GLPatchTable> OsdGLSLTransformFeedbackMesh; +#endif +// GLSL compute backend. +#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE +# include <opensubdiv/osd/glComputeEvaluator.h> +# include <opensubdiv/osd/glVertexBuffer.h> +using OpenSubdiv::Osd::GLComputeEvaluator; +using OpenSubdiv::Osd::GLStencilTableSSBO; +using OpenSubdiv::Osd::GLVertexBuffer; +typedef Mesh<GLVertexBuffer, + GLStencilTableSSBO, + GLComputeEvaluator, + GLPatchTable> OsdGLSLComputeMesh; +#endif + +#include <string> +#include <vector> + +#include "MEM_guardedalloc.h" + +#include "opensubdiv_topology_refiner_capi.h" +#include "internal/opensubdiv_gl_mesh_draw.h" +#include "internal/opensubdiv_gl_mesh_fvar.h" +#include "internal/opensubdiv_gl_mesh_internal.h" +#include "internal/opensubdiv_topology_refiner_internal.h" + +namespace { + +GLMeshInterface* createGLMeshInterface( + OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const MeshBitset& bits, + const int num_vertex_elements, + const int num_varying_elements, + const int level, + eOpenSubdivEvaluator evaluator_type) { + GLMeshInterface* mesh = NULL; + switch (evaluator_type) { +#define CHECK_EVALUATOR_TYPE(type, class) \ + case OPENSUBDIV_EVALUATOR_##type: \ + mesh = new class(topology_refiner, \ + num_vertex_elements, \ + num_varying_elements, \ + level, \ + bits); \ + break; + +#define CHECK_EVALUATOR_TYPE_STUB(type) \ + case OPENSUBDIV_EVALUATOR_##type: \ + mesh = NULL; \ + break; + + CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh) +#ifdef OPENSUBDIV_HAS_OPENMP + CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh) +#else + CHECK_EVALUATOR_TYPE_STUB(OPENMP) +#endif +#ifdef OPENSUBDIV_HAS_OPENCL + CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh) +#else + CHECK_EVALUATOR_TYPE_STUB(OPENCL) +#endif +#ifdef OPENSUBDIV_HAS_CUDA + CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh) +#else + CHECK_EVALUATOR_TYPE_STUB(CUDA) +#endif +#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK + CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK, OsdGLSLTransformFeedbackMesh) +#else + CHECK_EVALUATOR_TYPE_STUB(GLSL_TRANSFORM_FEEDBACK) +#endif +#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE + CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh) +#else + CHECK_EVALUATOR_TYPE_STUB(GLSL_COMPUTE) +#endif + +#undef CHECK_EVALUATOR_TYPE +#undef CHECK_EVALUATOR_TYPE_STUB + } + return mesh; +} + +//////////////////////////////////////////////////////////////////////////////// +// GLMesh structure "methods". + +opensubdiv_capi::GLMeshFVarData* createFVarData( + OpenSubdiv::Far::TopologyRefiner* topology_refiner, + GLMeshInterface* mesh, + const float *fvar_src_buffer) { + using opensubdiv_capi::GLMeshFVarData; + GLMeshFVarData* fvar_data = new GLMeshFVarData(); + fvar_data->create(topology_refiner, + mesh->GetFarPatchTable(), + 2, + fvar_src_buffer); + return fvar_data; +} + +unsigned int getPatchIndexBuffer(OpenSubdiv_GLMesh* gl_mesh) { + return gl_mesh->internal->mesh_interface + ->GetPatchTable() + ->GetPatchIndexBuffer(); +} + +void bindVertexBuffer(OpenSubdiv_GLMesh* gl_mesh) { + gl_mesh->internal->mesh_interface->BindVertexBuffer(); +} + +void setCoarsePositions(OpenSubdiv_GLMesh* gl_mesh, + const float* positions, + const int start_vertex, + const int num_vertices) { + gl_mesh->internal->mesh_interface->UpdateVertexBuffer(positions, + start_vertex, + num_vertices); +} + +void refine(OpenSubdiv_GLMesh* gl_mesh) { + gl_mesh->internal->mesh_interface->Refine(); +} + +void synchronize(struct OpenSubdiv_GLMesh* gl_mesh) { + gl_mesh->internal->mesh_interface->Synchronize(); +} + +void assignFunctionPointers(OpenSubdiv_GLMesh* gl_mesh) { + gl_mesh->getPatchIndexBuffer = getPatchIndexBuffer; + gl_mesh->bindVertexBuffer = bindVertexBuffer; + gl_mesh->setCoarsePositions = setCoarsePositions; + gl_mesh->refine = refine; + gl_mesh->synchronize = synchronize; + + gl_mesh->prepareDraw = opensubdiv_capi::GLMeshDisplayPrepare; + gl_mesh->drawPatches = opensubdiv_capi::GLMeshDisplayDrawPatches; +} + +} // namespace + +struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner( + OpenSubdiv_TopologyRefiner* topology_refiner, + eOpenSubdivEvaluator evaluator_type) { + using OpenSubdiv::Far::TopologyRefiner; + TopologyRefiner* osd_topology_refiner = + topology_refiner->internal->osd_topology_refiner; + // TODO(sergey): Query this from refiner. + const bool is_adaptive = false; + MeshBitset bits; + bits.set(OpenSubdiv::Osd::MeshAdaptive, is_adaptive); + bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, 0); + bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, 1); + bits.set(OpenSubdiv::Osd::MeshFVarData, 1); + bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1); + const int num_vertex_elements = 3; + const int num_varying_elements = 3; + GLMeshInterface* mesh = createGLMeshInterface( + osd_topology_refiner, + bits, + num_vertex_elements, + num_varying_elements, + osd_topology_refiner->GetMaxLevel(), + evaluator_type); + if (mesh == NULL) { + return NULL; + } + OpenSubdiv_GLMesh* gl_mesh = OBJECT_GUARDED_NEW(OpenSubdiv_GLMesh); + assignFunctionPointers(gl_mesh); + gl_mesh->internal = new OpenSubdiv_GLMeshInternal(); + gl_mesh->internal->evaluator_type = evaluator_type; + gl_mesh->internal->mesh_interface = mesh; + // Face-varying support. + // TODO(sergey): This part needs to be re-done. + if (osd_topology_refiner->GetNumFVarChannels() > 0) { + // TODO(sergey): This is a temporary stub to get things compiled. Need + // to store base level UVs somewhere else. + std::vector<float> uvs; + std::vector<float> fvar_data_buffer; + opensubdiv_capi::interpolateFVarData(*osd_topology_refiner, + uvs, + &fvar_data_buffer); + gl_mesh->internal->fvar_data = createFVarData(osd_topology_refiner, + mesh, + &fvar_data_buffer[0]); + } else { + gl_mesh->internal->fvar_data = NULL; + } + return gl_mesh; +} + +void openSubdiv_deleteOsdGLMesh(OpenSubdiv_GLMesh *gl_mesh) { + delete gl_mesh->internal; + OBJECT_GUARDED_DELETE(gl_mesh, OpenSubdiv_GLMesh); +} diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.cc b/intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.cc new file mode 100644 index 00000000000..0d2f8dc9842 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.cc @@ -0,0 +1,587 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/opensubdiv_gl_mesh_draw.h" + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <GL/glew.h> +#include <cmath> +#include <cstdio> + +#include <opensubdiv/osd/glMesh.h> + +#ifdef OPENSUBDIV_HAS_CUDA +# include <opensubdiv/osd/cudaGLVertexBuffer.h> +#endif // OPENSUBDIV_HAS_CUDA + +#include <opensubdiv/osd/cpuEvaluator.h> +#include <opensubdiv/osd/cpuGLVertexBuffer.h> + +#include "internal/opensubdiv_gl_mesh_fvar.h" +#include "internal/opensubdiv_gl_mesh_internal.h" +#include "opensubdiv_capi.h" +#include "opensubdiv_gl_mesh_capi.h" + +using OpenSubdiv::Osd::GLMeshInterface; + +extern "C" char datatoc_gpu_shader_opensubdiv_vertex_glsl[]; +extern "C" char datatoc_gpu_shader_opensubdiv_geometry_glsl[]; +extern "C" char datatoc_gpu_shader_opensubdiv_fragment_glsl[]; + +// TODO(sergey): Those are a bit of bad level calls :S +extern "C" { +void copy_m3_m3(float m1[3][3], float m2[3][3]); +void copy_m3_m4(float m1[3][3], float m2[4][4]); +void adjoint_m3_m3(float m1[3][3], float m[3][3]); +float determinant_m3_array(float m[3][3]); +bool invert_m3_m3(float m1[3][3], float m2[3][3]); +bool invert_m3(float m[3][3]); +void transpose_m3(float mat[3][3]); +} + +#define MAX_LIGHTS 8 +#define SUPPORT_COLOR_MATERIAL + +typedef struct Light { + float position[4]; + float ambient[4]; + float diffuse[4]; + float specular[4]; + float spot_direction[4]; +#ifdef SUPPORT_COLOR_MATERIAL + float constant_attenuation; + float linear_attenuation; + float quadratic_attenuation; + float spot_cutoff; + float spot_exponent; + float spot_cos_cutoff; + float pad, pad2; +#endif +} Light; + +typedef struct Lighting { + Light lights[MAX_LIGHTS]; + int num_enabled; +} Lighting; + +typedef struct Transform { + float projection_matrix[16]; + float model_view_matrix[16]; + float normal_matrix[9]; +} Transform; + +static bool g_use_osd_glsl = false; +static int g_active_uv_index = 0; + +static GLuint g_flat_fill_solid_program = 0; +static GLuint g_flat_fill_texture2d_program = 0; +static GLuint g_smooth_fill_solid_program = 0; +static GLuint g_smooth_fill_texture2d_program = 0; + +static GLuint g_flat_fill_solid_shadeless_program = 0; +static GLuint g_flat_fill_texture2d_shadeless_program = 0; +static GLuint g_smooth_fill_solid_shadeless_program = 0; +static GLuint g_smooth_fill_texture2d_shadeless_program = 0; + +static GLuint g_wireframe_program = 0; + +static GLuint g_lighting_ub = 0; +static Lighting g_lighting_data; +static Transform g_transform; + +namespace { + +GLuint compileShader(GLenum shaderType, + const char* version, + const char* define, + const char* source) { + const char* sources[] = { + version, define, +#ifdef SUPPORT_COLOR_MATERIAL + "#define SUPPORT_COLOR_MATERIAL\n", +#else + "", +#endif + source, + }; + + GLuint shader = glCreateShader(shaderType); + glShaderSource(shader, 4, sources, NULL); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + GLchar emsg[1024]; + glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg); + fprintf(stderr, "Error compiling GLSL: %s\n", emsg); + fprintf(stderr, "Version: %s\n", version); + fprintf(stderr, "Defines: %s\n", define); + fprintf(stderr, "Source: %s\n", source); + return 0; + } + + return shader; +} + +GLuint linkProgram(const char* version, const char* define) { + GLuint vertexShader = + compileShader(GL_VERTEX_SHADER, version, define, + datatoc_gpu_shader_opensubdiv_vertex_glsl); + if (vertexShader == 0) { + return 0; + } + GLuint geometryShader = + compileShader(GL_GEOMETRY_SHADER, version, define, + datatoc_gpu_shader_opensubdiv_geometry_glsl); + if (geometryShader == 0) { + return 0; + } + GLuint fragmentShader = + compileShader(GL_FRAGMENT_SHADER, version, define, + datatoc_gpu_shader_opensubdiv_fragment_glsl); + if (fragmentShader == 0) { + return 0; + } + + GLuint program = glCreateProgram(); + + glAttachShader(program, vertexShader); + glAttachShader(program, geometryShader); + glAttachShader(program, fragmentShader); + + glBindAttribLocation(program, 0, "position"); + glBindAttribLocation(program, 1, "normal"); + + glLinkProgram(program); + + glDeleteShader(vertexShader); + glDeleteShader(geometryShader); + glDeleteShader(fragmentShader); + + GLint status; + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + GLchar emsg[1024]; + glGetProgramInfoLog(program, sizeof(emsg), 0, emsg); + fprintf(stderr, "Error linking GLSL program : %s\n", emsg); + fprintf(stderr, "Defines: %s\n", define); + glDeleteProgram(program); + return 0; + } + + glUniformBlockBinding(program, glGetUniformBlockIndex(program, "Lighting"), + 0); + + if (GLEW_VERSION_4_1) { + glProgramUniform1i( + program, glGetUniformLocation(program, "texture_buffer"), 0); + glProgramUniform1i( + program, glGetUniformLocation(program, "FVarDataOffsetBuffer"), 30); + glProgramUniform1i( + program, glGetUniformLocation(program, "FVarDataBuffer"), 31); + } else { + glUseProgram(program); + glUniform1i(glGetUniformLocation(program, "texture_buffer"), 0); + glUniform1i(glGetUniformLocation(program, "FVarDataOffsetBuffer"), 30); + glUniform1i(glGetUniformLocation(program, "FVarDataBuffer"), 31); + glUseProgram(0); + } + + return program; +} + +void bindProgram(OpenSubdiv_GLMesh* gl_mesh, int program) { + glUseProgram(program); + // Matrices + glUniformMatrix4fv(glGetUniformLocation(program, "modelViewMatrix"), + 1, + false, + g_transform.model_view_matrix); + glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), + 1, + false, + g_transform.projection_matrix); + glUniformMatrix3fv(glGetUniformLocation(program, "normalMatrix"), + 1, + false, + g_transform.normal_matrix); + // Lighting. + glBindBuffer(GL_UNIFORM_BUFFER, g_lighting_ub); + glBufferSubData( + GL_UNIFORM_BUFFER, 0, sizeof(g_lighting_data), &g_lighting_data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_lighting_ub); + // Color. + { + // TODO(sergey): Stop using glGetMaterial. + float color[4]; + glGetMaterialfv(GL_FRONT, GL_DIFFUSE, color); + glUniform4fv(glGetUniformLocation(program, "diffuse"), 1, color); + glGetMaterialfv(GL_FRONT, GL_SPECULAR, color); + glUniform4fv(glGetUniformLocation(program, "specular"), 1, color); + glGetMaterialfv(GL_FRONT, GL_SHININESS, color); + glUniform1f(glGetUniformLocation(program, "shininess"), color[0]); + } + // Face-vertex data. + opensubdiv_capi::GLMeshFVarData* fvar_data = gl_mesh->internal->fvar_data; + if (fvar_data != NULL) { + if (fvar_data->texture_buffer) { + glActiveTexture(GL_TEXTURE31); + glBindTexture(GL_TEXTURE_BUFFER, fvar_data->texture_buffer); + glActiveTexture(GL_TEXTURE0); + } + if (fvar_data->offset_buffer) { + glActiveTexture(GL_TEXTURE30); + glBindTexture(GL_TEXTURE_BUFFER, fvar_data->offset_buffer); + glActiveTexture(GL_TEXTURE0); + } + glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), + fvar_data->fvar_width); + if (fvar_data->channel_offsets.size() > 0 && g_active_uv_index >= 0) { + glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), + fvar_data->channel_offsets[g_active_uv_index]); + } else { + glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0); + } + } else { + glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), 0); + glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0); + } +} + +} // namespace + +bool openSubdiv_initGLMeshDrawingResources(void) { + static bool need_init = true; + static bool init_success = false; + if (!need_init) { + return init_success; + } + // TODO(sergey): Update OSD drawing to OpenGL 3.3 core, + // then remove following line. + return false; + const char* version = ""; + if (GLEW_VERSION_3_2) { + version = "#version 150 compatibility\n"; + } else if (GLEW_VERSION_3_1) { + version = "#version 140\n" + "#extension GL_ARB_compatibility: enable\n"; + } else { + version = "#version 130\n"; + // Minimum supported for OpenSubdiv. + } + g_flat_fill_solid_program = linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define USE_LIGHTING\n" + "#define FLAT_SHADING\n"); + g_flat_fill_texture2d_program = linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define USE_LIGHTING\n" + "#define USE_TEXTURE_2D\n" + "#define FLAT_SHADING\n"); + g_smooth_fill_solid_program = linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define USE_LIGHTING\n" + "#define SMOOTH_SHADING\n"); + g_smooth_fill_texture2d_program = linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define USE_LIGHTING\n" + "#define USE_TEXTURE_2D\n" + "#define SMOOTH_SHADING\n"); + + g_flat_fill_solid_shadeless_program = + linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define FLAT_SHADING\n"); + g_flat_fill_texture2d_shadeless_program = + linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define USE_TEXTURE_2D\n" + "#define FLAT_SHADING\n"); + g_smooth_fill_solid_shadeless_program = + linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define SMOOTH_SHADING\n"); + g_smooth_fill_texture2d_shadeless_program = + linkProgram(version, + "#define USE_COLOR_MATERIAL\n" + "#define USE_TEXTURE_2D\n" + "#define SMOOTH_SHADING\n"); + g_wireframe_program = linkProgram(version, "#define WIREFRAME\n"); + + glGenBuffers(1, &g_lighting_ub); + glBindBuffer(GL_UNIFORM_BUFFER, g_lighting_ub); + glBufferData(GL_UNIFORM_BUFFER, + sizeof(g_lighting_data), + NULL, + GL_STATIC_DRAW); + need_init = false; + init_success = g_flat_fill_solid_program != 0 && + g_flat_fill_texture2d_program != 0 && + g_smooth_fill_solid_program != 0 && + g_smooth_fill_texture2d_program != 0 && g_wireframe_program; + return init_success; +} + +void openSubdiv_deinitGLMeshDrawingResources(void) { + if (g_lighting_ub != 0) { + glDeleteBuffers(1, &g_lighting_ub); + } +#define SAFE_DELETE_PROGRAM(program) \ + do { \ + if (program) { \ + glDeleteProgram(program); \ + } \ + } while (false) + + SAFE_DELETE_PROGRAM(g_flat_fill_solid_program); + SAFE_DELETE_PROGRAM(g_flat_fill_texture2d_program); + SAFE_DELETE_PROGRAM(g_smooth_fill_solid_program); + SAFE_DELETE_PROGRAM(g_smooth_fill_texture2d_program); + SAFE_DELETE_PROGRAM(g_flat_fill_solid_shadeless_program); + SAFE_DELETE_PROGRAM(g_flat_fill_texture2d_shadeless_program); + SAFE_DELETE_PROGRAM(g_smooth_fill_solid_shadeless_program); + SAFE_DELETE_PROGRAM(g_smooth_fill_texture2d_shadeless_program); + SAFE_DELETE_PROGRAM(g_wireframe_program); + +#undef SAFE_DELETE_PROGRAM +} + +namespace opensubdiv_capi { + +namespace { + +GLuint prepare_patchDraw(OpenSubdiv_GLMesh* gl_mesh, bool fill_quads) { + GLint program = 0; + if (!g_use_osd_glsl) { + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + if (program) { + GLint model; + glGetIntegerv(GL_SHADE_MODEL, &model); + GLint location = glGetUniformLocation(program, "osd_flat_shading"); + if (location != -1) { + glUniform1i(location, model == GL_FLAT); + } + // Face-vertex data. + opensubdiv_capi::GLMeshFVarData* fvar_data = gl_mesh->internal->fvar_data; + if (fvar_data != NULL) { + if (fvar_data->texture_buffer) { + glActiveTexture(GL_TEXTURE31); + glBindTexture(GL_TEXTURE_BUFFER, fvar_data->texture_buffer); + glActiveTexture(GL_TEXTURE0); + } + if (fvar_data->offset_buffer) { + glActiveTexture(GL_TEXTURE30); + glBindTexture(GL_TEXTURE_BUFFER, fvar_data->offset_buffer); + glActiveTexture(GL_TEXTURE0); + } + GLint location = glGetUniformLocation(program, "osd_fvar_count"); + if (location != -1) { + glUniform1i(location, fvar_data->fvar_width); + } + location = glGetUniformLocation(program, "osd_active_uv_offset"); + if (location != -1) { + if (fvar_data->channel_offsets.size() > 0 && g_active_uv_index >= 0) { + glUniform1i(location, + fvar_data->channel_offsets[g_active_uv_index]); + } else { + glUniform1i(location, 0); + } + } + } else { + glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), 0); + glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0); + } + } + return program; + } + if (fill_quads) { + int model; + GLboolean use_texture_2d; + glGetIntegerv(GL_SHADE_MODEL, &model); + glGetBooleanv(GL_TEXTURE_2D, &use_texture_2d); + if (model == GL_FLAT) { + if (use_texture_2d) { + program = g_flat_fill_texture2d_program; + } else { + program = g_flat_fill_solid_program; + } + } else { + if (use_texture_2d) { + program = g_smooth_fill_texture2d_program; + } else { + program = g_smooth_fill_solid_program; + } + } + + } else { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + program = g_wireframe_program; + } + bindProgram(gl_mesh, program); + return program; +} + +void perform_drawElements(GLuint program, + int patch_index, + int num_elements, + int start_element) { + if (program) { + glUniform1i(glGetUniformLocation(program, "PrimitiveIdBase"), patch_index); + } + glDrawElements(GL_LINES_ADJACENCY, + num_elements, + GL_UNSIGNED_INT, + reinterpret_cast<void*>(start_element * sizeof(unsigned int))); +} + +void finishPatchDraw(bool fill_quads) { + // TODO(sergey): Some of the stuff could be done once after the whole + // mesh is displayed. + /// Restore state. + if (!fill_quads) { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + glBindVertexArray(0); + if (g_use_osd_glsl) { + // TODO(sergey): Store previously used program and roll back to it? + glUseProgram(0); + } +} + +void drawPartitionPatchesRange(GLMeshInterface* mesh, + GLuint program, + int start_patch, + int num_patches) { + int traversed_patches = 0, num_remained_patches = num_patches; + const OpenSubdiv::Osd::PatchArrayVector &patches = + mesh->GetPatchTable()->GetPatchArrays(); + for (int i = 0; i < patches.size(); ++i) { + const OpenSubdiv::Osd::PatchArray &patch = patches[i]; + OpenSubdiv::Far::PatchDescriptor desc = patch.GetDescriptor(); + OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType(); + if (patchType == OpenSubdiv::Far::PatchDescriptor::QUADS) { + const int num_block_patches = patch.GetNumPatches(); + if (start_patch >= traversed_patches && + start_patch < traversed_patches + num_block_patches) { + const int num_control_verts = desc.GetNumControlVertices(); + const int start_draw_patch = start_patch - traversed_patches; + const int num_draw_patches = std::min( + num_remained_patches, num_block_patches - start_draw_patch); + perform_drawElements( + program, i + start_draw_patch, num_draw_patches * num_control_verts, + patch.GetIndexBase() + start_draw_patch * num_control_verts); + num_remained_patches -= num_draw_patches; + } + if (num_remained_patches == 0) { + break; + } + traversed_patches += num_block_patches; + } + } +} + +static void drawAllPatches(GLMeshInterface* mesh, GLuint program) { + const OpenSubdiv::Osd::PatchArrayVector &patches = + mesh->GetPatchTable()->GetPatchArrays(); + for (int i = 0; i < patches.size(); ++i) { + const OpenSubdiv::Osd::PatchArray &patch = patches[i]; + OpenSubdiv::Far::PatchDescriptor desc = patch.GetDescriptor(); + OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType(); + + if (patchType == OpenSubdiv::Far::PatchDescriptor::QUADS) { + perform_drawElements(program, i, + patch.GetNumPatches() * desc.GetNumControlVertices(), + patch.GetIndexBase()); + } + } +} + +} // namespace + +void GLMeshDisplayPrepare(struct OpenSubdiv_GLMesh* /*gl_mesh*/, + const bool use_osd_glsl, + const int active_uv_index) { + g_active_uv_index = active_uv_index; + g_use_osd_glsl = (use_osd_glsl != 0); + // Update transformation matrices. + glGetFloatv(GL_PROJECTION_MATRIX, g_transform.projection_matrix); + glGetFloatv(GL_MODELVIEW_MATRIX, g_transform.model_view_matrix); + copy_m3_m4((float(*)[3])g_transform.normal_matrix, + (float(*)[4])g_transform.model_view_matrix); + invert_m3((float(*)[3])g_transform.normal_matrix); + transpose_m3((float(*)[3])g_transform.normal_matrix); + // Update OpenGL lights positions, colors etc. + g_lighting_data.num_enabled = 0; + for (int i = 0; i < MAX_LIGHTS; ++i) { + GLboolean enabled; + glGetBooleanv(GL_LIGHT0 + i, &enabled); + if (enabled) { + g_lighting_data.num_enabled++; + } + // TODO(sergey): Stop using glGetLight. + glGetLightfv(GL_LIGHT0 + i, GL_POSITION, + g_lighting_data.lights[i].position); + glGetLightfv(GL_LIGHT0 + i, GL_AMBIENT, g_lighting_data.lights[i].ambient); + glGetLightfv(GL_LIGHT0 + i, GL_DIFFUSE, g_lighting_data.lights[i].diffuse); + glGetLightfv(GL_LIGHT0 + i, GL_SPECULAR, + g_lighting_data.lights[i].specular); + glGetLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, + g_lighting_data.lights[i].spot_direction); +#ifdef SUPPORT_COLOR_MATERIAL + glGetLightfv(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, + &g_lighting_data.lights[i].constant_attenuation); + glGetLightfv(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, + &g_lighting_data.lights[i].linear_attenuation); + glGetLightfv(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, + &g_lighting_data.lights[i].quadratic_attenuation); + glGetLightfv(GL_LIGHT0 + i, GL_SPOT_CUTOFF, + &g_lighting_data.lights[i].spot_cutoff); + glGetLightfv(GL_LIGHT0 + i, GL_SPOT_EXPONENT, + &g_lighting_data.lights[i].spot_exponent); + g_lighting_data.lights[i].spot_cos_cutoff = + cos(g_lighting_data.lights[i].spot_cutoff); +#endif + } +} + +void GLMeshDisplayDrawPatches(OpenSubdiv_GLMesh* gl_mesh, + const bool fill_quads, + const int start_patch, + const int num_patches) { + GLMeshInterface* mesh = gl_mesh->internal->mesh_interface; + // Make sure all global invariants are initialized. + if (!openSubdiv_initGLMeshDrawingResources()) { + return; + } + /// Setup GLSL/OpenGL to draw patches in current context. + GLuint program = prepare_patchDraw(gl_mesh, fill_quads != 0); + if (start_patch != -1) { + drawPartitionPatchesRange(mesh, program, start_patch, num_patches); + } else { + drawAllPatches(mesh, program); + } + // Finish patch drawing by restoring all changes to the OpenGL context. + finishPatchDraw(fill_quads != 0); +} + +} // namespace opensubdiv_capi diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.h b/intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.h new file mode 100644 index 00000000000..5421b5cf707 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.h @@ -0,0 +1,39 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_GL_MESH_DRAW_H_ +#define OPENSUBDIV_GL_MESH_DRAW_H_ + +#include <stdint.h> // for bool + +struct OpenSubdiv_GLMesh; + +namespace opensubdiv_capi { + +void GLMeshDisplayPrepare(struct OpenSubdiv_GLMesh* gl_mesh, + const bool use_osd_glsl, + const int active_uv_index); + +void GLMeshDisplayDrawPatches(OpenSubdiv_GLMesh* gl_mesh, + const bool fill_quads, + const int start_patch, + const int num_patches); + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_GL_MESH_DRAW_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.cc b/intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.cc new file mode 100644 index 00000000000..5c9033c8dd1 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.cc @@ -0,0 +1,175 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/opensubdiv_gl_mesh_fvar.h" + +#include <GL/glew.h> +#include <opensubdiv/far/primvarRefiner.h> + +namespace opensubdiv_capi { + +//////////////////////////////////////////////////////////////////////////////// +// GLMeshFVarData + +GLMeshFVarData::GLMeshFVarData() + : texture_buffer(0), + offset_buffer(0) { +} + +GLMeshFVarData::~GLMeshFVarData() { + release(); +} + +void GLMeshFVarData::release() { + if (texture_buffer) { + glDeleteTextures(1, &texture_buffer); + } + if (offset_buffer) { + glDeleteTextures(1, &offset_buffer); + } + texture_buffer = 0; + offset_buffer = 0; + fvar_width = 0; + channel_offsets.clear(); +} + +void GLMeshFVarData::create( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv::Far::PatchTable* patch_table, + int fvar_width, + const float* fvar_src_data) { + release(); + this->fvar_width = fvar_width; + /// Expand fvar data to per-patch array. + const int max_level = topology_refiner->GetMaxLevel(); + const int num_channels = patch_table->GetNumFVarChannels(); + std::vector<float> data; + int fvar_data_offset = 0; + channel_offsets.resize(num_channels); + for (int channel = 0; channel < num_channels; ++channel) { + OpenSubdiv::Far::ConstIndexArray indices = + patch_table->GetFVarValues(channel); + channel_offsets[channel] = data.size(); + data.reserve(data.size() + indices.size() * fvar_width); + for (int fvert = 0; fvert < indices.size(); ++fvert) { + int index = indices[fvert] * fvar_width; + for (int i = 0; i < fvar_width; ++i) { + data.push_back(fvar_src_data[fvar_data_offset + index++]); + } + } + if (topology_refiner->IsUniform()) { + const int num_values_max = + topology_refiner->GetLevel(max_level).GetNumFVarValues(channel); + fvar_data_offset += num_values_max * fvar_width; + } else { + const int num_values_total = + topology_refiner->GetNumFVarValuesTotal(channel); + fvar_data_offset += num_values_total * fvar_width; + } + } + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, + data.size() * sizeof(float), &data[0], + GL_STATIC_DRAW); + glGenTextures(1, &texture_buffer); + glBindTexture(GL_TEXTURE_BUFFER, texture_buffer); + glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, buffer); + glDeleteBuffers(1, &buffer); + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, + channel_offsets.size() * sizeof(int), + &channel_offsets[0], + GL_STATIC_DRAW); + glGenTextures(1, &offset_buffer); + glBindTexture(GL_TEXTURE_BUFFER, offset_buffer); + glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, buffer); + glBindTexture(GL_TEXTURE_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +//////////////////////////////////////////////////////////////////////////////// +// Helper functions. + +struct FVarVertex { + float u, v; + + void Clear() { + u = v = 0.0f; + } + + void AddWithWeight(FVarVertex const& src, float weight) { + u += weight * src.u; + v += weight * src.v; + } +}; + +void interpolateFVarData(const OpenSubdiv::Far::TopologyRefiner& refiner, + const std::vector<float>& uvs, + std::vector<float>* fvar_data) { + const int fvar_width = 2; + const int max_level = refiner.GetMaxLevel(); + size_t fvar_data_offset = 0, values_offset = 0; + for (int channel = 0; channel < refiner.GetNumFVarChannels(); ++channel) { + const int num_values = refiner.GetLevel(0).GetNumFVarValues(channel) * 2; + const int num_values_max = + refiner.GetLevel(max_level).GetNumFVarValues(channel); + const int num_values_total = refiner.GetNumFVarValuesTotal(channel); + if (num_values_total <= 0) { + continue; + } + OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner); + if (refiner.IsUniform()) { + // For uniform we only keep the highest level of refinement. + fvar_data->resize(fvar_data->size() + num_values_max * fvar_width); + std::vector<FVarVertex> buffer(num_values_total - num_values_max); + FVarVertex* src = &buffer[0]; + memcpy(src, &uvs[values_offset], num_values * sizeof(float)); + // Defer the last level to treat separately with its alternate + // destination. + for (int level = 1; level < max_level; ++level) { + FVarVertex* dst = + src + refiner.GetLevel(level - 1).GetNumFVarValues(channel); + primvar_refiner.InterpolateFaceVarying(level, src, dst, channel); + src = dst; + } + FVarVertex* dst = + reinterpret_cast<FVarVertex*>(&(*fvar_data)[fvar_data_offset]); + primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel); + fvar_data_offset += num_values_max * fvar_width; + } else { + // For adaptive we keep all levels. + fvar_data->resize(fvar_data->size() + num_values_total * fvar_width); + FVarVertex* src = + reinterpret_cast<FVarVertex*>(&(*fvar_data)[fvar_data_offset]); + memcpy(src, &uvs[values_offset], num_values * sizeof(float)); + for (int level = 1; level <= max_level; ++level) { + FVarVertex* dst = + src + refiner.GetLevel(level - 1).GetNumFVarValues(channel); + primvar_refiner.InterpolateFaceVarying(level, src, dst, channel); + src = dst; + } + fvar_data_offset += num_values_total * fvar_width; + } + values_offset += num_values; + } +} + +} // namespace opensubdiv_capi diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.h b/intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.h new file mode 100644 index 00000000000..039a94eaf86 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.h @@ -0,0 +1,57 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_GL_MESH_FVAR_H_ +#define OPENSUBDIV_GL_MESH_FVAR_H_ + +// NOTE: This is a [sane(er)] port of previous ground work for getting UVs to +// work. Still needs a lot of work to make it easy, correct and have proper +// data ownership. + +#include <opensubdiv/far/topologyRefiner.h> +#include <opensubdiv/far/patchTable.h> + +#include <vector> + +namespace opensubdiv_capi { + +// The buffer which holds GPU resources for face-varying elements. +class GLMeshFVarData { + public: + GLMeshFVarData(); + ~GLMeshFVarData(); + + void release(); + void create(const OpenSubdiv::Far::TopologyRefiner* refiner, + const OpenSubdiv::Far::PatchTable* patch_table, + int fvar_width, + const float* fvar_src_data); + + unsigned int texture_buffer; + unsigned int offset_buffer; + std::vector<int> channel_offsets; + int fvar_width; +}; + +void interpolateFVarData(const OpenSubdiv::Far::TopologyRefiner& refiner, + const std::vector<float>& uvs, + std::vector<float>* fvar_data); + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_GL_MESH_FVAR_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.cc b/intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.cc new file mode 100644 index 00000000000..3abba9cf8f8 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.cc @@ -0,0 +1,32 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/opensubdiv_gl_mesh_internal.h" + +#include "internal/opensubdiv_gl_mesh_fvar.h" + +OpenSubdiv_GLMeshInternal::OpenSubdiv_GLMeshInternal() + : evaluator_type(OPENSUBDIV_EVALUATOR_CPU), + mesh_interface(NULL), + fvar_data(NULL) { +} + +OpenSubdiv_GLMeshInternal::~OpenSubdiv_GLMeshInternal() { + delete mesh_interface; + delete fvar_data; +} diff --git a/intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.h b/intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.h new file mode 100644 index 00000000000..7796b450e69 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.h @@ -0,0 +1,43 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_GL_MESH_INTERNAL_H_ +#define OPENSUBDIV_GL_MESH_INTERNAL_H_ + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/osd/glMesh.h> + +#include "opensubdiv_capi_type.h" + +namespace opensubdiv_capi { +class GLMeshFVarData; +} // namespace opensubdiv_capi + +typedef struct OpenSubdiv_GLMeshInternal { + OpenSubdiv_GLMeshInternal(); + ~OpenSubdiv_GLMeshInternal(); + + eOpenSubdivEvaluator evaluator_type; + OpenSubdiv::Osd::GLMeshInterface* mesh_interface; + opensubdiv_capi::GLMeshFVarData* fvar_data; +} OpenSubdiv_GLMeshInternal; + +#endif // OPENSUBDIV_GL_MESH_INTERNAL_H_ diff --git a/intern/opensubdiv/opensubdiv_gpu_capi.cc b/intern/opensubdiv/internal/opensubdiv_gpu.cc index d28b48ddd18..d28b48ddd18 100644 --- a/intern/opensubdiv/opensubdiv_gpu_capi.cc +++ b/intern/opensubdiv/internal/opensubdiv_gpu.cc diff --git a/intern/opensubdiv/internal/opensubdiv_internal.h b/intern/opensubdiv/internal/opensubdiv_internal.h new file mode 100644 index 00000000000..b9d196bbe9e --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_internal.h @@ -0,0 +1,38 @@ +// Copyright 2015 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_INTERNAL_H_ +#define OPENSUBDIV_INTERNAL_H_ + +// Perform full topology validation when exporting it to OpenSubdiv. +#ifdef NDEBUG +// Never do for release builds. +# undef OPENSUBDIV_VALIDATE_TOPOLOGY +#else +// TODO(sergey): Always disabled for now, the check doesn't handle multiple +// non-manifolds from the OpenSubdiv side currently. +// # undef OPENSUBDIV_VALIDATE_TOPOLOGY +# define OPENSUBDIV_VALIDATE_TOPOLOGY +#endif + +// Currently OpenSubdiv expects topology to be oriented, but sometimes it's +// handy to disable orientation code to check whether it causes some weird +// issues by using pre-oriented model. +#define OPENSUBDIV_ORIENT_TOPOLOGY + +#endif // OPENSUBDIV_INTERNAL_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_topology_refiner.cc b/intern/opensubdiv/internal/opensubdiv_topology_refiner.cc new file mode 100644 index 00000000000..aa6ba953ef9 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_topology_refiner.cc @@ -0,0 +1,315 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "opensubdiv_topology_refiner_capi.h" + +#include <vector> + +#include "MEM_guardedalloc.h" +#include "internal/opensubdiv_converter_factory.h" +#include "internal/opensubdiv_converter_internal.h" +#include "internal/opensubdiv_internal.h" +#include "internal/opensubdiv_topology_refiner_internal.h" + +namespace { + +const OpenSubdiv::Far::TopologyRefiner* getOSDTopologyRefiner( + const OpenSubdiv_TopologyRefiner* topology_refiner) { + return topology_refiner->internal->osd_topology_refiner; +} + +const OpenSubdiv::Far::TopologyLevel* getOSDTopologyBaseLevel( + const OpenSubdiv_TopologyRefiner* topology_refiner) { + return &getOSDTopologyRefiner(topology_refiner)->GetLevel(0); +} + +int getSubdivisionLevel(const OpenSubdiv_TopologyRefiner* topology_refiner) { + return topology_refiner->internal->settings.level; +} + +bool getIsAdaptive(const OpenSubdiv_TopologyRefiner* topology_refiner) { + return topology_refiner->internal->settings.is_adaptive; +} + +int getNumVertices(const OpenSubdiv_TopologyRefiner* topology_refiner) { + return getOSDTopologyBaseLevel(topology_refiner)->GetNumVertices(); +} + +int getNumEdges(const OpenSubdiv_TopologyRefiner* topology_refiner) { + return getOSDTopologyBaseLevel(topology_refiner)->GetNumEdges(); +} + +int getNumFaces(const OpenSubdiv_TopologyRefiner* topology_refiner) { + return getOSDTopologyBaseLevel(topology_refiner)->GetNumFaces(); +} + +int getNumFaceVertices(const OpenSubdiv_TopologyRefiner* topology_refiner, + const int face_index) { + const OpenSubdiv::Far::TopologyLevel* base_level = + getOSDTopologyBaseLevel(topology_refiner); + return base_level->GetFaceVertices(face_index).size(); +} + +int getNumFacePtexFaces(const OpenSubdiv_TopologyRefiner* topology_refiner, + const int face_index) { + const int num_face_vertices = + topology_refiner->getNumFaceVertices(topology_refiner, face_index); + if (num_face_vertices == 4) { + return 1; + } else { + return num_face_vertices; + } +} + +int getNumPtexFaces(const OpenSubdiv_TopologyRefiner* topology_refiner) { + const int num_faces = topology_refiner->getNumFaces(topology_refiner); + int num_ptex_faces = 0; + for (int face_index = 0; face_index < num_faces; ++face_index) { + num_ptex_faces += + topology_refiner->getNumFacePtexFaces(topology_refiner, face_index); + } + return num_ptex_faces; +} + +void fillFacePtexIndexOffset(const OpenSubdiv_TopologyRefiner* topology_refiner, + int* face_ptex_index_offset) { + const int num_faces = topology_refiner->getNumFaces(topology_refiner); + int num_ptex_faces = 0; + for (int face_index = 0; face_index < num_faces; ++face_index) { + face_ptex_index_offset[face_index] = num_ptex_faces; + num_ptex_faces += + topology_refiner->getNumFacePtexFaces(topology_refiner, face_index); + } +} + +void assignFunctionPointers(OpenSubdiv_TopologyRefiner* topology_refiner) { + topology_refiner->getSubdivisionLevel = getSubdivisionLevel; + topology_refiner->getIsAdaptive = getIsAdaptive; + + topology_refiner->getNumVertices = getNumVertices; + topology_refiner->getNumEdges = getNumEdges; + topology_refiner->getNumFaces = getNumFaces; + topology_refiner->getNumFaceVertices = getNumFaceVertices; + + topology_refiner->getNumFacePtexFaces = getNumFacePtexFaces; + topology_refiner->getNumPtexFaces = getNumPtexFaces; + topology_refiner->fillFacePtexIndexOffset = fillFacePtexIndexOffset; +} + +OpenSubdiv_TopologyRefiner* allocateTopologyRefiner() { + OpenSubdiv_TopologyRefiner* topology_refiner = + OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefiner); + topology_refiner->internal = + OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerInternal); + assignFunctionPointers(topology_refiner); + return topology_refiner; +} + +} // namespace + +OpenSubdiv_TopologyRefiner* openSubdiv_createTopologyRefinerFromConverter( + OpenSubdiv_Converter* converter, + const OpenSubdiv_TopologyRefinerSettings* settings) { + OpenSubdiv_TopologyRefiner* topology_refiner = allocateTopologyRefiner(); + topology_refiner->internal->osd_topology_refiner = + opensubdiv_capi::createOSDTopologyRefinerFromConverter(converter); + // Store setting which we want to keep track of and which can not be stored + // in OpenSubdiv's descriptor yet. + topology_refiner->internal->settings = *settings; + return topology_refiner; +} + +void openSubdiv_deleteTopologyRefiner( + OpenSubdiv_TopologyRefiner* topology_refiner) { + OBJECT_GUARDED_DELETE(topology_refiner->internal, + OpenSubdiv_TopologyRefinerInternal); + OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefiner); +} + +//////////////////////////////////////////////////////////////////////////////// +// Comparison with converter. + +namespace { + +/////////////////////////////////////////////////////////// +// Quick preliminary checks. + +bool checkSchemeTypeMatches( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + const OpenSubdiv::Sdc::SchemeType converter_scheme_type = + opensubdiv_capi::getSchemeTypeFromCAPI( + converter->getSchemeType(converter)); + return (converter_scheme_type == topology_refiner->GetSchemeType()); +} + +bool checkOptionsMatches( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + typedef OpenSubdiv::Sdc::Options Options; + const Options options = topology_refiner->GetSchemeOptions(); + const Options::FVarLinearInterpolation fvar_interpolation = + options.GetFVarLinearInterpolation(); + const Options::FVarLinearInterpolation converter_fvar_interpolation = + opensubdiv_capi::getFVarLinearInterpolationFromCAPI( + converter->getFVarLinearInterpolation(converter)); + if (fvar_interpolation != converter_fvar_interpolation) { + return false; + } + return true; +} + +bool checkGeometryCoountersMatches( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + using OpenSubdiv::Far::TopologyLevel; + const TopologyLevel& base_level = topology_refiner->GetLevel(0); + return ( + (converter->getNumVertices(converter) == base_level.GetNumVertices()) && + (converter->getNumEdges(converter) == base_level.GetNumEdges()) && + (converter->getNumFaces(converter) == base_level.GetNumFaces())); +} + +bool checkPreliminaryMatches( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + return checkSchemeTypeMatches(topology_refiner, converter) && + checkOptionsMatches(topology_refiner, converter) && + checkGeometryCoountersMatches(topology_refiner, converter); +} + +/////////////////////////////////////////////////////////// +// Geometry comparison. + +bool checkGeometryEdgesMatch( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + using OpenSubdiv::Far::ConstIndexArray; + using OpenSubdiv::Far::TopologyLevel; + const TopologyLevel& base_level = topology_refiner->GetLevel(0); + const int num_edges = base_level.GetNumEdges(); + for (int edge_index = 0; edge_index < num_edges; ++edge_index) { + const ConstIndexArray& edge_vertices = + base_level.GetEdgeVertices(edge_index); + int conv_edge_vertices[2]; + converter->getEdgeVertices(converter, edge_index, conv_edge_vertices); + if (conv_edge_vertices[0] != edge_vertices[0] || + conv_edge_vertices[1] != edge_vertices[1]) { + return false; + } + } + return true; +} + +bool checkGeometryFacesMatch( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + using OpenSubdiv::Far::ConstIndexArray; + using OpenSubdiv::Far::TopologyLevel; + const TopologyLevel& base_level = topology_refiner->GetLevel(0); + const int num_faces = base_level.GetNumFaces(); + // TODO(sergey): Consider using data structure which keeps handful of + // elements on stack before doing heep allocation. + std::vector<int> conv_face_vertices; + for (int face_index = 0; face_index < num_faces; ++face_index) { + const ConstIndexArray& face_vertices = + base_level.GetFaceVertices(face_index); + const int num_face_vertices = face_vertices.size(); + if (num_face_vertices != + converter->getNumFaceVertices(converter, face_index)) { + return false; + } + conv_face_vertices.resize(num_face_vertices); + converter->getFaceVertices(converter, face_index, &conv_face_vertices[0]); + // Check face-vertex indices in the direct order (assuming topology + // orientation is disabled or did not flip order of the face-vertices). + // + // TODO(sergey): Can we simply memcmp() with OpenSubdiv's array? + bool direct_match = true; + for (int face_vertex_index = 0; face_vertex_index < num_face_vertices; + ++face_vertex_index) { + if (conv_face_vertices[face_vertex_index] != + face_vertices[face_vertex_index]) { + 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 + for (int face_vertex_index = 0; face_vertex_index < num_face_vertices; + ++face_vertex_index) { + if (conv_face_vertices[face_vertex_index] != + face_vertices[num_face_vertices - face_vertex_index - 1]) { + return false; + } + } +#endif + return false; + } + } + return true; +} + +bool checkGeometryMatches( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + return checkGeometryEdgesMatch(topology_refiner, converter) && + checkGeometryFacesMatch(topology_refiner, converter); +} + +/////////////////////////////////////////////////////////// +// Compare attributes which affects on topology + +bool checkEdgeSharpnessMatch( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + using OpenSubdiv::Far::ConstIndexArray; + using OpenSubdiv::Far::TopologyLevel; + const TopologyLevel& base_level = topology_refiner->GetLevel(0); + const int num_edges = base_level.GetNumEdges(); + for (int edge_index = 0; edge_index < num_edges; ++edge_index) { + const float sharpness = base_level.GetEdgeSharpness(edge_index); + const float conv_sharpness = + opensubdiv_capi::getCompatibleEdgeSharpness(converter, edge_index); + if (sharpness != conv_sharpness) { + return false; + } + } + return false; +} + +bool checkTopologyAttributesMatch( + const OpenSubdiv::Far::TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + return checkEdgeSharpnessMatch(topology_refiner, converter); +} + +} // namespace + +bool openSubdiv_topologyRefinerCompareWithConverter( + const OpenSubdiv_TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter) { + const OpenSubdiv::Far::TopologyRefiner* refiner = + getOSDTopologyRefiner(topology_refiner); + return (checkPreliminaryMatches(refiner, converter) && + checkGeometryMatches(refiner, converter) && + checkTopologyAttributesMatch(refiner, converter)); +} diff --git a/intern/opensubdiv/opensubdiv_topology_refiner.h b/intern/opensubdiv/internal/opensubdiv_topology_refiner.h index b00f6a54201..b00f6a54201 100644 --- a/intern/opensubdiv/opensubdiv_topology_refiner.h +++ b/intern/opensubdiv/internal/opensubdiv_topology_refiner.h diff --git a/intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.cc b/intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.cc new file mode 100644 index 00000000000..4636679761f --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.cc @@ -0,0 +1,26 @@ +// Copyright 2016 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/opensubdiv_topology_refiner_internal.h" + +OpenSubdiv_TopologyRefinerInternal::OpenSubdiv_TopologyRefinerInternal() + : osd_topology_refiner(NULL) {} + +OpenSubdiv_TopologyRefinerInternal::~OpenSubdiv_TopologyRefinerInternal() { + delete osd_topology_refiner; +} diff --git a/intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.h b/intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.h new file mode 100644 index 00000000000..f7ca6a7ad5e --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.h @@ -0,0 +1,47 @@ +// Copyright 2016 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_TOPOLOGY_REFINER_INTERNAL_H_ +#define OPENSUBDIV_TOPOLOGY_REFINER_INTERNAL_H_ + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +#include <opensubdiv/far/topologyRefiner.h> + +#include "opensubdiv_topology_refiner_capi.h" + +struct OpenSubdiv_TopologyRefinerInternal { + public: + OpenSubdiv_TopologyRefinerInternal(); + ~OpenSubdiv_TopologyRefinerInternal(); + + OpenSubdiv::Far::TopologyRefiner* osd_topology_refiner; + + // Subdivision settingsa this refiner is created for. + // + // We store it here since OpenSubdiv's refiner will only know about level and + // "adaptivity" after performing actual "refine" step. + // + // Ideally, we would also support refining topology without re-importing it + // from external world, but that is for later. + OpenSubdiv_TopologyRefinerSettings settings; +}; + +#endif // OPENSUBDIV_TOPOLOGY_REFINER_H_ diff --git a/intern/opensubdiv/internal/opensubdiv_util.cc b/intern/opensubdiv/internal/opensubdiv_util.cc new file mode 100644 index 00000000000..87bfce2116c --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_util.cc @@ -0,0 +1,61 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin +// Contributor(s): Brecht van Lommel + +#include "internal/opensubdiv_util.h" + +#include <GL/glew.h> +#include <cstring> + +#ifdef _MSC_VER +# include <iso646.h> +#endif + +namespace opensubdiv_capi { + +void stringSplit(std::vector<std::string>* tokens, + const std::string& str, + const std::string& separators, + bool skip_empty) { + size_t token_start = 0, token_length = 0; + for (size_t i = 0; i < str.length(); ++i) { + const char ch = str[i]; + if (separators.find(ch) == std::string::npos) { + // Append non-separator char to a token. + ++token_length; + } else { + // Append current token to the list (if any). + if (token_length > 0 || !skip_empty) { + std::string token = str.substr(token_start, token_length); + tokens->push_back(token); + } + // Re-set token pointers, + token_start = i + 1; + token_length = 0; + } + } + // Append token which might be at the end of the string. + if ((token_length != 0) || + (!skip_empty && token_start > 0 && + separators.find(str[token_start-1]) != std::string::npos)) { + std::string token = str.substr(token_start, token_length); + tokens->push_back(token); + } +} + +} // namespace opensubdiv_capi diff --git a/intern/opensubdiv/internal/opensubdiv_util.h b/intern/opensubdiv/internal/opensubdiv_util.h new file mode 100644 index 00000000000..6ed19b37e33 --- /dev/null +++ b/intern/opensubdiv/internal/opensubdiv_util.h @@ -0,0 +1,39 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin +// Contributor(s): Brecht van Lommel + +#ifndef OPENSUBDIV_UTIL_H_ +#define OPENSUBDIV_UTIL_H_ + +#include <vector> +#include <string> + +namespace opensubdiv_capi { + +#define STRINGIFY_ARG(x) "" #x +#define STRINGIFY_APPEND(a, b) "" a #b +#define STRINGIFY(x) STRINGIFY_APPEND("", x) + +void stringSplit(std::vector<std::string>* tokens, + const std::string& str, + const std::string& separators, + bool skip_empty); + +} // namespace opensubdiv_capi + +#endif // OPENSUBDIV_UTIL_H_ diff --git a/intern/opensubdiv/opensubdiv_capi.cc b/intern/opensubdiv/opensubdiv_capi.cc deleted file mode 100644 index 638039f4f3e..00000000000 --- a/intern/opensubdiv/opensubdiv_capi.cc +++ /dev/null @@ -1,441 +0,0 @@ -/* - * ***** 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) 2013 Blender Foundation. - * All rights reserved. - * - * Contributor(s): Sergey Sharybin. - * Brecht van Lommel - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#include "opensubdiv_capi.h" - -#ifdef _MSC_VER -# include "iso646.h" -#endif - -#include <stdlib.h> -#include <GL/glew.h> - -#include <opensubdiv/version.h> -#include <opensubdiv/osd/glMesh.h> - -/* CPU Backend */ -#include <opensubdiv/osd/cpuGLVertexBuffer.h> -#include <opensubdiv/osd/cpuEvaluator.h> - -#ifdef OPENSUBDIV_HAS_OPENMP -# include <opensubdiv/osd/ompEvaluator.h> -#endif /* OPENSUBDIV_HAS_OPENMP */ - -#ifdef OPENSUBDIV_HAS_OPENCL -# include <opensubdiv/osd/clGLVertexBuffer.h> -# include <opensubdiv/osd/clEvaluator.h> -# include "opensubdiv_device_context_opencl.h" -#endif /* OPENSUBDIV_HAS_OPENCL */ - -#ifdef OPENSUBDIV_HAS_CUDA -# include <opensubdiv/osd/cudaGLVertexBuffer.h> -# include <opensubdiv/osd/cudaEvaluator.h> -# include "opensubdiv_device_context_cuda.h" -#endif /* OPENSUBDIV_HAS_CUDA */ - -#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK -# include <opensubdiv/osd/glXFBEvaluator.h> -# include <opensubdiv/osd/glVertexBuffer.h> -#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */ - -#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE -# include <opensubdiv/osd/glComputeEvaluator.h> -# include <opensubdiv/osd/glVertexBuffer.h> -#endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */ - -#include <opensubdiv/osd/glPatchTable.h> -#include <opensubdiv/far/stencilTable.h> -#include <opensubdiv/far/primvarRefiner.h> - -#include "opensubdiv_gl_mesh.h" -#include "opensubdiv_intern.h" -#include "opensubdiv_topology_refiner.h" - -#include "MEM_guardedalloc.h" - -#include <string> -#include <vector> - -using std::string; -using std::vector; - -#define STRINGIFY_ARG(x) "" #x -#define STRINGIFY_APPEND(a, b) "" a #b -#define STRINGIFY(x) STRINGIFY_APPEND("", x) - -/* **************** Types declaration **************** */ - -using OpenSubdiv::Osd::GLMeshInterface; -using OpenSubdiv::Osd::Mesh; -using OpenSubdiv::Osd::MeshBitset; -using OpenSubdiv::Far::StencilTable; -using OpenSubdiv::Osd::GLPatchTable; - -using OpenSubdiv::Osd::Mesh; - -/* CPU backend */ -using OpenSubdiv::Osd::CpuGLVertexBuffer; -using OpenSubdiv::Osd::CpuEvaluator; -typedef Mesh<CpuGLVertexBuffer, - StencilTable, - CpuEvaluator, - GLPatchTable> OsdCpuMesh; - -#ifdef OPENSUBDIV_HAS_OPENMP -using OpenSubdiv::Osd::OmpEvaluator; -typedef Mesh<CpuGLVertexBuffer, - StencilTable, - OmpEvaluator, - GLPatchTable> OsdOmpMesh; -#endif /* OPENSUBDIV_HAS_OPENMP */ - -#ifdef OPENSUBDIV_HAS_OPENCL -using OpenSubdiv::Osd::CLEvaluator; -using OpenSubdiv::Osd::CLGLVertexBuffer; -using OpenSubdiv::Osd::CLStencilTable; -/* TODO(sergey): Use CLDeviceCOntext similar to OSD examples? */ -typedef Mesh<CLGLVertexBuffer, - CLStencilTable, - CLEvaluator, - GLPatchTable, - CLDeviceContext> OsdCLMesh; -static CLDeviceContext g_clDeviceContext; -#endif /* OPENSUBDIV_HAS_OPENCL */ - -#ifdef OPENSUBDIV_HAS_CUDA -using OpenSubdiv::Osd::CudaEvaluator; -using OpenSubdiv::Osd::CudaGLVertexBuffer; -using OpenSubdiv::Osd::CudaStencilTable; -typedef Mesh<CudaGLVertexBuffer, - CudaStencilTable, - CudaEvaluator, - GLPatchTable> OsdCudaMesh; -static CudaDeviceContext g_cudaDeviceContext; -#endif /* OPENSUBDIV_HAS_CUDA */ - -#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK -using OpenSubdiv::Osd::GLXFBEvaluator; -using OpenSubdiv::Osd::GLStencilTableTBO; -using OpenSubdiv::Osd::GLVertexBuffer; -typedef Mesh<GLVertexBuffer, - GLStencilTableTBO, - GLXFBEvaluator, - GLPatchTable> OsdGLSLTransformFeedbackMesh; -#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */ - -#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE -using OpenSubdiv::Osd::GLComputeEvaluator; -using OpenSubdiv::Osd::GLStencilTableSSBO; -using OpenSubdiv::Osd::GLVertexBuffer; -typedef Mesh<GLVertexBuffer, - GLStencilTableSSBO, - GLComputeEvaluator, - GLPatchTable> OsdGLSLComputeMesh; -#endif - -namespace { - -#if !defined(OPENSUBDIV_VERSION_NUMBER) && !defined(OPENSUBDIV_VERSION_MINOR) -void stringSplit(vector<string>* tokens, - const string& str, - const string& separators, - bool skip_empty) { - size_t token_start = 0, token_length = 0; - for (size_t i = 0; i < str.length(); ++i) { - const char ch = str[i]; - if (separators.find(ch) == string::npos) { - /* Append non-separator char to a token. */ - ++token_length; - } else { - /* Append current token to the list (if any). */ - if (token_length > 0 || !skip_empty) { - string token = str.substr(token_start, token_length); - tokens->push_back(token); - } - /* Re-set token pointers, */ - token_start = i + 1; - token_length = 0; - } - } - /* Append token which might be at the end of the string. */ - if ((token_length != 0) || - (!skip_empty && token_start > 0 && - separators.find(str[token_start-1]) != string::npos)) { - string token = str.substr(token_start, token_length); - tokens->push_back(token); - } -} -#endif - -struct FVarVertex { - float u, v; - void Clear() { - u = v = 0.0f; - } - void AddWithWeight(FVarVertex const & src, float weight) { - u += weight * src.u; - v += weight * src.v; - } -}; - -static void interpolate_fvar_data(OpenSubdiv::Far::TopologyRefiner& refiner, - const std::vector<float> uvs, - std::vector<float> &fvar_data) { - /* TODO(sergey): Make it somehow more generic way. */ - const int fvar_width = 2; - const int max_level = refiner.GetMaxLevel(); - size_t fvar_data_offset = 0, values_offset = 0; - for (int channel = 0; channel < refiner.GetNumFVarChannels(); ++channel) { - const int num_values = refiner.GetLevel(0).GetNumFVarValues(channel) * 2, - num_values_max = refiner.GetLevel(max_level).GetNumFVarValues(channel), - num_values_total = refiner.GetNumFVarValuesTotal(channel); - if (num_values_total <= 0) { - continue; - } - OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner); - if (refiner.IsUniform()) { - /* For uniform we only keep the highest level of refinement. */ - fvar_data.resize(fvar_data.size() + num_values_max * fvar_width); - std::vector<FVarVertex> buffer(num_values_total - num_values_max); - FVarVertex *src = &buffer[0]; - memcpy(src, &uvs[values_offset], num_values * sizeof(float)); - /* Defer the last level to treat separately with its alternate - * destination. - */ - for (int level = 1; level < max_level; ++level) { - FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel); - primvar_refiner.InterpolateFaceVarying(level, src, dst, channel); - src = dst; - } - FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]); - primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel); - fvar_data_offset += num_values_max * fvar_width; - } else { - /* For adaptive we keep all levels. */ - fvar_data.resize(fvar_data.size() + num_values_total * fvar_width); - FVarVertex *src = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]); - memcpy(src, &uvs[values_offset], num_values * sizeof(float)); - for (int level = 1; level <= max_level; ++level) { - FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel); - primvar_refiner.InterpolateFaceVarying(level, src, dst, channel); - src = dst; - } - fvar_data_offset += num_values_total * fvar_width; - } - values_offset += num_values; - } -} - -} // namespace - -struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner( - OpenSubdiv_TopologyRefinerDescr *topology_refiner, - int evaluator_type, - int level) -{ - using OpenSubdiv::Far::TopologyRefiner; - - MeshBitset bits; - /* TODO(sergey): Adaptive subdivisions are not currently - * possible because of the lack of tessellation shader. - */ - bits.set(OpenSubdiv::Osd::MeshAdaptive, 0); - bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, 0); - bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, 1); - bits.set(OpenSubdiv::Osd::MeshFVarData, 1); - bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1); - - const int num_vertex_elements = 3; - const int num_varying_elements = 3; - - GLMeshInterface *mesh = NULL; - TopologyRefiner *refiner = topology_refiner->osd_refiner; - - switch(evaluator_type) { -#define CHECK_EVALUATOR_TYPE(type, class) \ - case OPENSUBDIV_EVALUATOR_ ## type: \ - mesh = new class(refiner, \ - num_vertex_elements, \ - num_varying_elements, \ - level, \ - bits); \ - break; - - CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh) - -#ifdef OPENSUBDIV_HAS_OPENMP - CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh) -#endif - -#ifdef OPENSUBDIV_HAS_OPENCL - CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh) -#endif - -#ifdef OPENSUBDIV_HAS_CUDA - CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh) -#endif - -#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK - CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK, - OsdGLSLTransformFeedbackMesh) -#endif - -#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE - CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh) -#endif - -#undef CHECK_EVALUATOR_TYPE - } - - if (mesh == NULL) { - return NULL; - } - - OpenSubdiv_GLMesh *gl_mesh = - (OpenSubdiv_GLMesh *) OBJECT_GUARDED_NEW(OpenSubdiv_GLMesh); - gl_mesh->evaluator_type = evaluator_type; - gl_mesh->descriptor = (OpenSubdiv_GLMeshDescr *) mesh; - gl_mesh->topology_refiner = topology_refiner; - - if (refiner->GetNumFVarChannels() > 0) { - std::vector<float> fvar_data; - interpolate_fvar_data(*refiner, topology_refiner->uvs, fvar_data); - openSubdiv_osdGLAllocFVar(topology_refiner, gl_mesh, &fvar_data[0]); - } - else { - gl_mesh->fvar_data = NULL; - } - - return gl_mesh; -} - -void openSubdiv_deleteOsdGLMesh(struct OpenSubdiv_GLMesh *gl_mesh) -{ - openSubdiv_osdGLDestroyFVar(gl_mesh); - switch (gl_mesh->evaluator_type) { -#define CHECK_EVALUATOR_TYPE(type, class) \ - case OPENSUBDIV_EVALUATOR_ ## type: \ - delete (class *) gl_mesh->descriptor; \ - break; - - CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh) - -#ifdef OPENSUBDIV_HAS_OPENMP - CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh) -#endif - -#ifdef OPENSUBDIV_HAS_OPENCL - CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh) -#endif - -#ifdef OPENSUBDIV_HAS_CUDA - CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh) -#endif - -#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK - CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK, - OsdGLSLTransformFeedbackMesh) -#endif - -#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE - CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh) -#endif - -#undef CHECK_EVALUATOR_TYPE - } - - /* NOTE: OSD refiner was owned by gl_mesh, no need to free it here. */ - OBJECT_GUARDED_DELETE(gl_mesh->topology_refiner, OpenSubdiv_TopologyRefinerDescr); - OBJECT_GUARDED_DELETE(gl_mesh, OpenSubdiv_GLMesh); -} - -unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer(struct OpenSubdiv_GLMesh *gl_mesh) -{ - return ((GLMeshInterface *)gl_mesh->descriptor)->GetPatchTable()->GetPatchIndexBuffer(); -} - -unsigned int openSubdiv_getOsdGLMeshVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh) -{ - return ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer(); -} - -void openSubdiv_osdGLMeshUpdateVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh, - const float *vertex_data, - int start_vertex, - int num_verts) -{ - ((GLMeshInterface *)gl_mesh->descriptor)->UpdateVertexBuffer(vertex_data, - start_vertex, - num_verts); -} - -void openSubdiv_osdGLMeshRefine(struct OpenSubdiv_GLMesh *gl_mesh) -{ - ((GLMeshInterface *)gl_mesh->descriptor)->Refine(); -} - -void openSubdiv_osdGLMeshSynchronize(struct OpenSubdiv_GLMesh *gl_mesh) -{ - ((GLMeshInterface *)gl_mesh->descriptor)->Synchronize(); -} - -void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh) -{ - ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer(); -} - -const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner( - OpenSubdiv_GLMesh *gl_mesh) -{ - return gl_mesh->topology_refiner; -} - -int openSubdiv_getVersionHex(void) -{ -#if defined(OPENSUBDIV_VERSION_NUMBER) - return OPENSUBDIV_VERSION_NUMBER; -#elif defined(OPENSUBDIV_VERSION_MAJOR) - return OPENSUBDIV_VERSION_MAJOR * 10000 + - OPENSUBDIV_VERSION_MINOR * 100 + - OPENSUBDIV_VERSION_PATCH; -#elif defined(OPENSUBDIV_VERSION) - const char* version = STRINGIFY(OPENSUBDIV_VERSION); - if (version[0] == 'v') { - version += 1; - } - int major = 0, minor = 0, patch = 0; - vector<string> tokens; - stringSplit(&tokens, version, "_", true); - if (tokens.size() == 3) { - major = atoi(tokens[0].c_str()); - minor = atoi(tokens[1].c_str()); - patch = atoi(tokens[2].c_str()); - } - return major * 10000 + minor * 100 + patch; -#else - return 0; -#endif -} diff --git a/intern/opensubdiv/opensubdiv_capi.h b/intern/opensubdiv/opensubdiv_capi.h index c29d08a77e1..a26ea36b863 100644 --- a/intern/opensubdiv/opensubdiv_capi.h +++ b/intern/opensubdiv/opensubdiv_capi.h @@ -1,163 +1,43 @@ -/* - * ***** 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) 2013 Blender Foundation. - * All rights reserved. - * - * Contributor(s): Sergey Sharybin - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#ifndef __OPENSUBDIV_CAPI_H__ -#define __OPENSUBDIV_CAPI_H__ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CAPI_H_ +#define OPENSUBDIV_CAPI_H_ + +#include "opensubdiv_capi_type.h" #ifdef __cplusplus extern "C" { #endif -// Types declaration. -struct OpenSubdiv_GLMesh; -struct OpenSubdiv_GLMeshDescr; -struct OpenSubdiv_GLMeshFVarData; -struct OpenSubdiv_TopologyRefinerDescr; - -typedef struct OpenSubdiv_GLMesh OpenSubdiv_GLMesh; - -// Keep this a bitmask os it's possible to pass available -// evaluators to Blender. -enum { - OPENSUBDIV_EVALUATOR_CPU = (1 << 0), - OPENSUBDIV_EVALUATOR_OPENMP = (1 << 1), - OPENSUBDIV_EVALUATOR_OPENCL = (1 << 2), - OPENSUBDIV_EVALUATOR_CUDA = (1 << 3), - OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK = (1 << 4), - OPENSUBDIV_EVALUATOR_GLSL_COMPUTE = (1 << 5), -}; - -enum { - OPENSUBDIV_SCHEME_CATMARK, - OPENSUBDIV_SCHEME_BILINEAR, - OPENSUBDIV_SCHEME_LOOP, -}; - -/* TODO(sergey): Re-name and avoid bad level data access. */ -OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner( - struct OpenSubdiv_TopologyRefinerDescr *topology_refiner, - int evaluator_type, - int level); - -void openSubdiv_deleteOsdGLMesh(OpenSubdiv_GLMesh *gl_mesh); -unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer( - OpenSubdiv_GLMesh *gl_mesh); -unsigned int openSubdiv_getOsdGLMeshVertexBuffer(OpenSubdiv_GLMesh *gl_mesh); -void openSubdiv_osdGLMeshUpdateVertexBuffer(OpenSubdiv_GLMesh *gl_mesh, - const float *vertex_data, - int start_vertex, - int num_verts); -void openSubdiv_osdGLMeshRefine(OpenSubdiv_GLMesh *gl_mesh); -void openSubdiv_osdGLMeshSynchronize(OpenSubdiv_GLMesh *gl_mesh); -void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh); - -const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner( - OpenSubdiv_GLMesh *gl_mesh); - -/* ============================= Evaluator API ============================== */ - -struct OpenSubdiv_EvaluatorDescr; -typedef struct OpenSubdiv_EvaluatorDescr OpenSubdiv_EvaluatorDescr; - -/* TODO(sergey): Avoid bad-level data access, */ -OpenSubdiv_EvaluatorDescr *openSubdiv_createEvaluatorDescr( - struct OpenSubdiv_TopologyRefinerDescr *topology_refiner, - int subsurf_level); - -void openSubdiv_deleteEvaluatorDescr( - OpenSubdiv_EvaluatorDescr *evaluator_descr); - -void openSubdiv_setEvaluatorCoarsePositions( - OpenSubdiv_EvaluatorDescr *evaluator_descr, - const float *positions, - int start_vertex_index, - int num_vertices); -void openSubdiv_setEvaluatorVaryingData( - OpenSubdiv_EvaluatorDescr *evaluator_descr, - const float *varying_data, - int start_vertex_index, - int num_vertices); - -void openSubdiv_setEvaluatorCoarsePositionsFromBuffer( - OpenSubdiv_EvaluatorDescr *evaluator_descr, - const void *buffer, - int start_offset, - int stride, - int start_vertex_index, - int num_vertices); - -void openSubdiv_refineEvaluator(OpenSubdiv_EvaluatorDescr *evaluator_descr); - -void openSubdiv_evaluateLimit(OpenSubdiv_EvaluatorDescr *evaluator_descr, - int osd_face_index, - float face_u, float face_v, - float P[3], - float dPdu[3], - float dPdv[3]); - -void openSubdiv_evaluateVarying(OpenSubdiv_EvaluatorDescr *evaluator_descr, - int osd_face_index, - float face_u, float face_v, - float varying[3]); - -/* ============================== Mesh drawing =============================== */ - -/* Initialize/Deinitialize global OpenGL drawing buffers/GLSL programs. */ -bool openSubdiv_osdGLDisplayInit(void); -void openSubdiv_osdGLDisplayDeinit(void); - -/* Initialize all the invariants which stays the same for every single path, - * for example lighting model stays untouched for the whole mesh. - * - * TODO(sergey): Some of the stuff could be initialized once for all meshes. - */ -void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl, int active_uv_index); - -/* Draw specified patches. */ -void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh, - int fill_quads, - int start_patch, - int num_patches); - -void openSubdiv_osdGLAllocFVar( - struct OpenSubdiv_TopologyRefinerDescr *topology_refiner, - OpenSubdiv_GLMesh *gl_mesh, - const float *fvar_data); - -void openSubdiv_osdGLDestroyFVar(OpenSubdiv_GLMesh *gl_mesh); - -/* =========================== Utility functions ============================ */ - -int openSubdiv_getAvailableEvaluators(void); +// Global initialization/deinitialization. +// +// Supposed to be called from main thread. void openSubdiv_init(void); void openSubdiv_cleanup(void); +// Bitmask of eOpenSubdivEvaluator. +int openSubdiv_getAvailableEvaluators(void); + int openSubdiv_getVersionHex(void); #ifdef __cplusplus } #endif -#endif // __OPENSUBDIV_CAPI_H__ +#endif // OPENSUBDIV_CAPI_H_ diff --git a/intern/opensubdiv/opensubdiv_capi_type.h b/intern/opensubdiv/opensubdiv_capi_type.h new file mode 100644 index 00000000000..04fe2afff53 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_capi_type.h @@ -0,0 +1,41 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CAPI_TYPES_H_ +#define OPENSUBDIV_CAPI_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Keep this a bitmask os it's possible to pass available +// evaluators to Blender. +typedef enum eOpenSubdivEvaluator { + OPENSUBDIV_EVALUATOR_CPU = (1 << 0), + OPENSUBDIV_EVALUATOR_OPENMP = (1 << 1), + OPENSUBDIV_EVALUATOR_OPENCL = (1 << 2), + OPENSUBDIV_EVALUATOR_CUDA = (1 << 3), + OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK = (1 << 4), + OPENSUBDIV_EVALUATOR_GLSL_COMPUTE = (1 << 5), +} eOpenSubdivEvaluator; + +#ifdef __cplusplus +} +#endif + +#endif // OPENSUBDIV_CAPI_TYPES_H_ diff --git a/intern/opensubdiv/opensubdiv_converter.cc b/intern/opensubdiv/opensubdiv_converter.cc deleted file mode 100644 index fec15b118ae..00000000000 --- a/intern/opensubdiv/opensubdiv_converter.cc +++ /dev/null @@ -1,764 +0,0 @@ -/* - * ***** 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> - -#include "MEM_guardedalloc.h" - -#include "opensubdiv_converter_capi.h" -#include "opensubdiv_intern.h" -#include "opensubdiv_topology_refiner.h" - - -#include <stack> - -#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; -} - -struct TopologyRefinerData { - const OpenSubdiv_Converter& conv; - std::vector<float> *uvs; -}; - -} /* namespace */ -#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */ - -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()); -} - -#ifdef OPENSUBDIV_ORIENT_TOPOLOGY -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()); -} - -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 - -} /* namespace */ - -template <> -inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology( - TopologyRefiner& refiner, - const TopologyRefinerData& cb_data) -{ - const OpenSubdiv_Converter& conv = cb_data.conv; - /* 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 <> -inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology( - TopologyRefiner& refiner, - const TopologyRefinerData &cb_data) -{ - const OpenSubdiv_Converter& conv = cb_data.conv; - 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]); - } -#ifdef OPENSUBDIV_ORIENT_TOPOLOGY - /* Make face normals consistent. */ - bool *face_used = new bool[num_faces]; - 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); - for (int vert = 0; vert < num_verts; ++vert) { - - /* 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); - /* 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); -#ifdef OPENSUBDIV_ORIENT_TOPOLOGY - /* ** Order vertex edges and faces in a CCW order. ** */ - memset(face_used, 0, sizeof(bool) * num_faces); - /* Number of edges and faces added to the ordered array. */ - int edge_count_ordered = 0, face_count_ordered = 0; - /* 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; - } - } - 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; - } - } - } - 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); - if (edge_faces.size() == 1) { - face_start = edge_faces[0]; - 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; - } - } - /* 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; - dst_vert_faces[face_count_ordered++] = face_start; - dst_vert_edges[edge_count_ordered++] = edge_start; - face_used[face_start] = true; - 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) { - /* Multiple manifolds found, stop for now and handle rest - * in the next iteration. - */ - 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) { - break; - } - 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; - face_used[face_start] = true; - } - edge_start = edge_next; - } - } - /* Verify ordering doesn't ruin connectivity information. */ - assert(face_count_ordered == num_vert_faces); - assert(edge_count_ordered == num_vert_edges); - 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; - } -#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 */ - delete [] vert_edges; - delete [] vert_faces; - } -#ifdef OPENSUBDIV_ORIENT_TOPOLOGY - delete [] face_used; -#endif - populateBaseLocalIndices(refiner); - return true; -}; - -template <> -inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags( - TopologyRefiner& refiner, - const TopologyRefinerData& cb_data) -{ - const OpenSubdiv_Converter& conv = cb_data.conv; - typedef OpenSubdiv::Sdc::Crease Crease; - - int num_edges = conv.get_num_edges(&conv); - for (int edge = 0; edge < num_edges; ++edge) { - 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; - } - setBaseEdgeSharpness(refiner, edge, 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 verts which are adjacent to a loose edge as sharp, - * but this decision needs some more investigation. - */ - int num_vert = conv.get_num_verts(&conv); - for (int vert = 0; vert < num_vert; ++vert) { - 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; - } - } - 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); - } - } - - return true; -} - -template <> -inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology( - TopologyError /*errCode*/, - const char *msg, - const TopologyRefinerData& /*mesh*/) -{ - printf("OpenSubdiv Error: %s\n", msg); -} - -template <> -inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology( - TopologyRefiner& refiner, - const TopologyRefinerData& cb_data) -{ - const OpenSubdiv_Converter& conv = cb_data.conv; - const int num_layers = conv.get_num_uv_layers(&conv); - if (num_layers <= 0) { - /* No UV maps, we can skip any face-varying data. */ - return true; - } - const int num_faces = getNumBaseFaces(refiner); - size_t uvs_offset = 0; - 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(cb_data.uvs->size() + num_uvs * 2); - conv.get_uvs(&conv, &cb_data.uvs->at(uvs_offset)); - uvs_offset += num_uvs * 2; - /* 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; - } - } - conv.finish_uv_layer(&conv); - } - return true; -} - -} /* 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 scheme type passed via C-API"); - return OpenSubdiv::Sdc::SCHEME_CATMARK; -} - -OpenSubdiv::Sdc::Options::FVarLinearInterpolation -get_capi_fvar_linear_interpolation( - OpenSubdiv_FVarLinearInterpolation linear_interpolation) -{ - typedef OpenSubdiv::Sdc::Options Options; - switch (linear_interpolation) { - case OSD_FVAR_LINEAR_INTERPOLATION_NONE: - return Options::FVAR_LINEAR_NONE; - case OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY: - return Options::FVAR_LINEAR_CORNERS_ONLY; - case OSD_FVAR_LINEAR_INTERPOLATION_BOUNDARIES: - return Options::FVAR_LINEAR_BOUNDARIES; - case OSD_FVAR_LINEAR_INTERPOLATION_ALL: - return Options::FVAR_LINEAR_ALL; - } - assert(!"Unknown fvar linear interpolation passed via C-API"); - return Options::FVAR_LINEAR_NONE; -} - -} /* namespace */ - -struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr( - OpenSubdiv_Converter *converter) -{ - typedef OpenSubdiv::Sdc::Options Options; - - using OpenSubdiv::Far::TopologyRefinerFactory; - const OpenSubdiv::Sdc::SchemeType scheme_type = - get_capi_scheme_type(converter->get_scheme_type(converter)); - const Options::FVarLinearInterpolation linear_interpolation = - get_capi_fvar_linear_interpolation( - converter->get_fvar_linear_interpolation(converter)); - Options options; - options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY); - options.SetCreasingMethod(Options::CREASE_UNIFORM); - options.SetFVarLinearInterpolation(linear_interpolation); - - TopologyRefinerFactory<TopologyRefinerData>::Options - topology_options(scheme_type, options); -#ifdef OPENSUBDIV_VALIDATE_TOPOLOGY - topology_options.validateFullTopology = true; -#endif - OpenSubdiv_TopologyRefinerDescr *result = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerDescr); - TopologyRefinerData cb_data = {*converter, &result->uvs}; - /* We don't use guarded allocation here so we can re-use the refiner - * for GL mesh creation directly. - */ - result->osd_refiner = - TopologyRefinerFactory<TopologyRefinerData>::Create( - cb_data, - topology_options); - - return result; -} - -void openSubdiv_deleteTopologyRefinerDescr( - OpenSubdiv_TopologyRefinerDescr *topology_refiner) -{ - delete topology_refiner->osd_refiner; - OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefinerDescr); -} - -int openSubdiv_topologyRefinerGetSubdivLevel( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner) -{ - using OpenSubdiv::Far::TopologyRefiner; - const TopologyRefiner *refiner = topology_refiner->osd_refiner; - return refiner->GetMaxLevel(); -} - -int openSubdiv_topologyRefinerGetNumVerts( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner) -{ - using OpenSubdiv::Far::TopologyLevel; - using OpenSubdiv::Far::TopologyRefiner; - const TopologyRefiner *refiner = topology_refiner->osd_refiner; - 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; - const TopologyRefiner *refiner = topology_refiner->osd_refiner; - 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; - const TopologyRefiner *refiner = topology_refiner->osd_refiner; - const TopologyLevel &base_level = refiner->GetLevel(0); - return base_level.GetNumFaces(); -} - -int openSubdiv_topologyRefinerGetNumFaceVerts( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner, - int face) -{ - using OpenSubdiv::Far::TopologyLevel; - using OpenSubdiv::Far::TopologyRefiner; - const TopologyRefiner *refiner = topology_refiner->osd_refiner; - const TopologyLevel &base_level = refiner->GetLevel(0); - return base_level.GetFaceVertices(face).size(); -} - -int openSubdiv_topologyRefnerCompareConverter( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner, - OpenSubdiv_Converter *converter) -{ - typedef OpenSubdiv::Sdc::Options Options; - using OpenSubdiv::Far::ConstIndexArray; - using OpenSubdiv::Far::TopologyRefiner; - using OpenSubdiv::Far::TopologyLevel; - const TopologyRefiner *refiner = topology_refiner->osd_refiner; - 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_scheme_type(converter)); - if (scheme_type != refiner->GetSchemeType()) { - return false; - } - const Options options = refiner->GetSchemeOptions(); - const Options::FVarLinearInterpolation interp = - options.GetFVarLinearInterpolation(); - const Options::FVarLinearInterpolation new_interp = - get_capi_fvar_linear_interpolation( - converter->get_fvar_linear_interpolation(converter)); - if (new_interp != interp) { - 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]); - bool direct_match = true; - for (int i = 0; i < face_verts.size(); ++i) { - if (conv_face_verts[i] != face_verts[i]) { - 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; - } - } -#else - return false; -#endif - } - } - /* Compare sharpness. */ - for (int edge = 0; edge < num_edges; ++edge) { - ConstIndexArray edge_faces = base_level.GetEdgeFaces(edge); - float sharpness = base_level.GetEdgeSharpness(edge); - float conv_sharpness; - if (edge_faces.size() == 2) { - conv_sharpness = converter->get_edge_sharpness(converter, edge); - } - else { - conv_sharpness = OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE; - } - if (sharpness != conv_sharpness) { - return false; - } - } - return true; -} diff --git a/intern/opensubdiv/opensubdiv_converter_capi.h b/intern/opensubdiv/opensubdiv_converter_capi.h index ea4f20c5961..1d76da4d76b 100644 --- a/intern/opensubdiv/opensubdiv_converter_capi.h +++ b/intern/opensubdiv/opensubdiv_converter_capi.h @@ -1,149 +1,150 @@ -/* - * ***** 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 ***** - */ - -#ifndef __OPENSUBDIV_CONVERTER_CAPI_H__ -#define __OPENSUBDIV_CONVERTER_CAPI_H__ +// Copyright 2015 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CONVERTER_CAPI_H_ +#define OPENSUBDIV_CONVERTER_CAPI_H_ #ifdef __cplusplus extern "C" { #endif -struct OpenSubdiv_TopologyRefinerDescr; -typedef struct OpenSubdiv_TopologyRefinerDescr OpenSubdiv_TopologyRefinerDescr; - -typedef struct OpenSubdiv_Converter OpenSubdiv_Converter; - typedef enum OpenSubdiv_SchemeType { - OSD_SCHEME_BILINEAR, - OSD_SCHEME_CATMARK, - OSD_SCHEME_LOOP, + OSD_SCHEME_BILINEAR, + OSD_SCHEME_CATMARK, + OSD_SCHEME_LOOP, } OpenSubdiv_SchemeType; typedef enum OpenSubdiv_FVarLinearInterpolation { - OSD_FVAR_LINEAR_INTERPOLATION_NONE, - OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY, - OSD_FVAR_LINEAR_INTERPOLATION_BOUNDARIES, - OSD_FVAR_LINEAR_INTERPOLATION_ALL, + OSD_FVAR_LINEAR_INTERPOLATION_NONE, + OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY, + OSD_FVAR_LINEAR_INTERPOLATION_BOUNDARIES, + OSD_FVAR_LINEAR_INTERPOLATION_ALL, } OpenSubdiv_FVarLinearInterpolation; typedef struct OpenSubdiv_Converter { - /* TODO(sergey): Needs to be implemented. */ - /* OpenSubdiv::Sdc::Options get_options() const; */ - - OpenSubdiv_SchemeType (*get_scheme_type)( - const OpenSubdiv_Converter *converter); - - OpenSubdiv_FVarLinearInterpolation (*get_fvar_linear_interpolation)( - const OpenSubdiv_Converter *converter); - - int (*get_num_faces)(const OpenSubdiv_Converter *converter); - int (*get_num_edges)(const OpenSubdiv_Converter *converter); - int (*get_num_verts)(const OpenSubdiv_Converter *converter); - - /* Face relationships. */ - int (*get_num_face_verts)(const OpenSubdiv_Converter *converter, - int face); - void (*get_face_verts)(const OpenSubdiv_Converter *converter, - int face, - int *face_verts); - void (*get_face_edges)(const OpenSubdiv_Converter *converter, - int face, - int *face_edges); - - /* Edge relationships. */ - void (*get_edge_verts)(const OpenSubdiv_Converter *converter, - int edge, - int *edge_verts); - int (*get_num_edge_faces)(const OpenSubdiv_Converter *converter, - int edge); - void (*get_edge_faces)(const OpenSubdiv_Converter *converter, - int edge, - int *edge_faces); - float (*get_edge_sharpness)(const OpenSubdiv_Converter *converter, - int edge); - - /* Vertex relationships. */ - int (*get_num_vert_edges)(const OpenSubdiv_Converter *converter, int vert); - void (*get_vert_edges)(const OpenSubdiv_Converter *converter, - int vert, - int *vert_edges); - int (*get_num_vert_faces)(const OpenSubdiv_Converter *converter, int vert); - void (*get_vert_faces)(const OpenSubdiv_Converter *converter, - int vert, - int *vert_faces); - - /* Face-varying data. */ - int (*get_num_uv_layers)(const OpenSubdiv_Converter *converter); - - void (*precalc_uv_layer)(const OpenSubdiv_Converter *converter, int layer); - void (*finish_uv_layer)(const OpenSubdiv_Converter *converter); - - int (*get_num_uvs)(const OpenSubdiv_Converter *converter); - void (*get_uvs)(const OpenSubdiv_Converter *converter, float *uvs); - - int (*get_face_corner_uv_index)(const OpenSubdiv_Converter *converter, - int face, - int corner); - - /* User data associated with this converter. */ - void (*free_user_data)(const OpenSubdiv_Converter *converter); - void *user_data; + OpenSubdiv_SchemeType (*getSchemeType)( + const struct OpenSubdiv_Converter* converter); + + OpenSubdiv_FVarLinearInterpolation (*getFVarLinearInterpolation)( + const struct OpenSubdiv_Converter* converter); + + ////////////////////////////////////////////////////////////////////////////// + // Global geometry counters. + // Number of faces/edges/vertices in the base mesh. + int (*getNumFaces)(const struct OpenSubdiv_Converter* converter); + int (*getNumEdges)(const struct OpenSubdiv_Converter* converter); + int (*getNumVertices)(const struct OpenSubdiv_Converter* converter); + + ////////////////////////////////////////////////////////////////////////////// + // Face relationships. + + // Number of vertices the face consists of. + int (*getNumFaceVertices)(const struct OpenSubdiv_Converter* converter, + const int face_index); + // Array of vertex indices the face consists of. + void (*getFaceVertices)(const struct OpenSubdiv_Converter* converter, + const int face_index, + int* face_vertices); + // Array of edge indices the face consists of. + // Aligned with the vertex indices array, edge i connects face vertex i + // with face index i+1. + void (*getFaceEdges)(const struct OpenSubdiv_Converter *converter, + const int face_index, + int *face_edges); + + ////////////////////////////////////////////////////////////////////////////// + // Edge relationships. + + // Vertices the edge consists of. + void (*getEdgeVertices)(const struct OpenSubdiv_Converter* converter, + const int edge_index, + int edge_vertices[2]); + // Number of faces which are sharing the given edge. + int (*getNumEdgeFaces)(const struct OpenSubdiv_Converter* converter, + const int edge_index); + // Array of face indices which are sharing the given edge. + void (*getEdgeFaces)(const struct OpenSubdiv_Converter* converter, + const int edge, + int* edge_faces); + // Edge sharpness (aka crease). + float (*getEdgeSharpness)(const struct OpenSubdiv_Converter* converter, + const int edge_index); + + ////////////////////////////////////////////////////////////////////////////// + // Vertex relationships. + + // Number of edges which are adjacent to the given vertex. + int (*getNumVertexEdges)(const struct OpenSubdiv_Converter* converter, + const int vertex_index); + // Array fo edge indices which are adjacent to the given vertex. + void (*getVertexEdges)(const struct OpenSubdiv_Converter* converter, + const int vertex_index, + int* vertex_edges); + // Number of faces which are adjacent to the given vertex. + int (*getNumVertexFaces)(const struct OpenSubdiv_Converter* converter, + const int vertex_index); + // Array fo face indices which are adjacent to the given vertex. + void (*getVertexFaces)(const struct OpenSubdiv_Converter* converter, + const int vertex_index, + int* vertex_faces); + + ////////////////////////////////////////////////////////////////////////////// + // Face-varying data. + + ///////////////////////////////////// + // UV coordinates. + + // Number of UV layers. + int (*getNumUVLayers)(const struct OpenSubdiv_Converter* converter); + + // We need some corner connectivity information, which might not be trivial + // to be gathered (might require multiple matching calculations per corver + // query). + // precalc() is called before any corner connectivity or UV coordinate is + // queried from the given layer, allowing converter to calculate and cache + // complex complex-to-calculate information. + // finish() is called after converter is done porting UV layer to OpenSubdiv, + // allowing to free cached data. + void (*precalcUVLayer)(const struct OpenSubdiv_Converter* converter, + const int layer_index); + void (*finishUVLayer)(const struct OpenSubdiv_Converter* converter); + + // Get number of UV coordinates in the current layer (layer which was + // specified in precalcUVLayer(). + int (*getNumUVCoordinates)(const struct OpenSubdiv_Converter* converter); + // Get cooridnates themselves. + void (*getUVCoordinates)(const struct OpenSubdiv_Converter* converter, + float* uvs_coordinates); + // For the given face index and its corner (known as loop in Blender) + // get corrsponding UV coordinate index. + int (*getFaceCornerUVIndex)(const struct OpenSubdiv_Converter* converter, + const int face_index, + const int corner_index); + + ////////////////////////////////////////////////////////////////////////////// + // User data associated with this converter. + + void (*freeUserData)(const struct OpenSubdiv_Converter* converter); + void* user_data; } OpenSubdiv_Converter; -OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr( - OpenSubdiv_Converter *converter); - -void openSubdiv_deleteTopologyRefinerDescr( - OpenSubdiv_TopologyRefinerDescr *topology_refiner); - -/* TODO(sergey): Those calls are not strictly related to conversion. - * needs some dedicated file perhaps. - */ - -int openSubdiv_topologyRefinerGetSubdivLevel( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner); - -int openSubdiv_topologyRefinerGetNumVerts( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner); - -int openSubdiv_topologyRefinerGetNumEdges( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner); - -int openSubdiv_topologyRefinerGetNumFaces( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner); - -int openSubdiv_topologyRefinerGetNumFaceVerts( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner, - int face); - -int openSubdiv_topologyRefnerCompareConverter( - const OpenSubdiv_TopologyRefinerDescr *topology_refiner, - OpenSubdiv_Converter *converter); - #ifdef __cplusplus } #endif -#endif /* __OPENSUBDIV_CONVERTER_CAPI_H__ */ +#endif /* OPENSUBDIV_CONVERTER_CAPI_H_ */ diff --git a/intern/opensubdiv/opensubdiv_device_context_cuda.cc b/intern/opensubdiv/opensubdiv_device_context_cuda.cc deleted file mode 100644 index 46b66a6b35e..00000000000 --- a/intern/opensubdiv/opensubdiv_device_context_cuda.cc +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Adopted from OpenSubdiv with the following license: - * - * Copyright 2015 Pixar - * - * Licensed under the Apache License, Version 2.0 (the "Apache License") - * with the following modification; you may not use this file except in - * compliance with the Apache License and the following modification to it: - * Section 6. Trademarks. is deleted and replaced with: - * - * 6. Trademarks. This License does not grant permission to use the trade - * names, trademarks, service marks, or product names of the Licensor - * and its affiliates, except as required to comply with Section 4(c) of - * the License and to reproduce the content of the NOTICE file. - * - * You may obtain a copy of the Apache License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Apache License with the above modification is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the Apache License for the specific - * language governing permissions and limitations under the Apache License. - */ - -#ifdef OPENSUBDIV_HAS_CUDA - -#ifdef _MSC_VER -# include "iso646.h" -#endif - -#include "opensubdiv_device_context_cuda.h" - -#if defined(_WIN32) -# include <windows.h> -#elif defined(__APPLE__) -# include <OpenGL/OpenGL.h> -#else -# include <X11/Xlib.h> -# include <GL/glx.h> -#endif - -#include <cstdio> -#include <algorithm> -#include <cuda.h> -#include <cuda_runtime_api.h> -#include <cuda_gl_interop.h> - -#define message(fmt, ...) -//#define message(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) -#define error(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) - -static int _GetCudaDeviceForCurrentGLContext() -{ - // Find and use the CUDA device for the current GL context - unsigned int interopDeviceCount = 0; - int interopDevices[1]; - cudaError_t status = cudaGLGetDevices(&interopDeviceCount, interopDevices, - 1, cudaGLDeviceListCurrentFrame); - if (status == cudaErrorNoDevice or interopDeviceCount != 1) { - message("CUDA no interop devices found.\n"); - return 0; - } - int device = interopDevices[0]; - -#if defined(_WIN32) - return device; - -#elif defined(__APPLE__) - return device; - -#else // X11 - Display * display = glXGetCurrentDisplay(); - int screen = DefaultScreen(display); - if (device != screen) { - error("The CUDA interop device (%d) does not match " - "the screen used by the current GL context (%d), " - "which may cause slow performance on systems " - "with multiple GPU devices.", device, screen); - } - message("CUDA init using device for current GL context: %d\n", device); - return device; -#endif -} - -/* From "NVIDIA GPU Computing SDK 4.2/C/common/inc/cutil_inline_runtime.h": */ - -/* Beginning of GPU Architecture definitions */ -inline int _ConvertSMVer2Cores_local(int major, int minor) -{ - /* Defines for GPU Architecture types (using the SM version to determine - * the # of cores per SM - */ - typedef struct { - int SM; /* 0xMm (hexidecimal notation), - * M = SM Major version, - * and m = SM minor version - */ - int Cores; - } sSMtoCores; - - sSMtoCores nGpuArchCoresPerSM[] = - { { 0x10, 8 }, /* Tesla Generation (SM 1.0) G80 class */ - { 0x11, 8 }, /* Tesla Generation (SM 1.1) G8x class */ - { 0x12, 8 }, /* Tesla Generation (SM 1.2) G9x class */ - { 0x13, 8 }, /* Tesla Generation (SM 1.3) GT200 class */ - { 0x20, 32 }, /* Fermi Generation (SM 2.0) GF100 class */ - { 0x21, 48 }, /* Fermi Generation (SM 2.1) GF10x class */ - { 0x30, 192}, /* Fermi Generation (SM 3.0) GK10x class */ - { -1, -1 } - }; - - int index = 0; - while (nGpuArchCoresPerSM[index].SM != -1) { - if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) { - return nGpuArchCoresPerSM[index].Cores; - } - index++; - } - printf("MapSMtoCores undefined SMversion %d.%d!\n", major, minor); - return -1; -} -/* End of GPU Architecture definitions. */ - -/* This function returns the best GPU (with maximum GFLOPS) */ -inline int cutGetMaxGflopsDeviceId() -{ - int current_device = 0, sm_per_multiproc = 0; - int max_compute_perf = 0, max_perf_device = -1; - int device_count = 0, best_SM_arch = 0; - int compat_major, compat_minor; - - cuDeviceGetCount(&device_count); - /* Find the best major SM Architecture GPU device. */ - while (current_device < device_count) { - cuDeviceComputeCapability(&compat_major, &compat_minor, current_device); - if (compat_major > 0 && compat_major < 9999) { - best_SM_arch = std::max(best_SM_arch, compat_major); - } - current_device++; - } - - /* Find the best CUDA capable GPU device. */ - current_device = 0; - while (current_device < device_count) { - cuDeviceComputeCapability(&compat_major, &compat_minor, current_device); - if (compat_major == 9999 && compat_minor == 9999) { - sm_per_multiproc = 1; - } else { - sm_per_multiproc = _ConvertSMVer2Cores_local(compat_major, - compat_minor); - } - int multi_processor_count; - cuDeviceGetAttribute(&multi_processor_count, - CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, - current_device); - int clock_rate; - cuDeviceGetAttribute(&clock_rate, - CU_DEVICE_ATTRIBUTE_CLOCK_RATE, - current_device); - int compute_perf = multi_processor_count * sm_per_multiproc * clock_rate; - if (compute_perf > max_compute_perf) { - /* If we find GPU with SM major > 2, search only these */ - if (best_SM_arch > 2) { - /* If our device==dest_SM_arch, choose this, or else pass. */ - if (compat_major == best_SM_arch) { - max_compute_perf = compute_perf; - max_perf_device = current_device; - } - } else { - max_compute_perf = compute_perf; - max_perf_device = current_device; - } - } - ++current_device; - } - return max_perf_device; -} - -bool CudaDeviceContext::HAS_CUDA_VERSION_4_0() -{ -#ifdef OPENSUBDIV_HAS_CUDA - static bool cudaInitialized = false; - static bool cudaLoadSuccess = true; - if (!cudaInitialized) { - cudaInitialized = true; - -# ifdef OPENSUBDIV_HAS_CUEW - cudaLoadSuccess = cuewInit(CUEW_INIT_CUDA) == CUEW_SUCCESS; - if (!cudaLoadSuccess) { - fprintf(stderr, "Loading CUDA failed.\n"); - } -# endif - // Need to initialize CUDA here so getting device - // with the maximum FPLOS works fine. - if (cuInit(0) == CUDA_SUCCESS) { - // This is to deal with cases like NVidia Optimus, - // when there might be CUDA library installed but - // NVidia card is not being active. - if (cutGetMaxGflopsDeviceId() < 0) { - cudaLoadSuccess = false; - } - } - else { - cudaLoadSuccess = false; - } - } - return cudaLoadSuccess; -#else - return false; -#endif -} - -CudaDeviceContext::CudaDeviceContext() - : _initialized(false) { -} - -CudaDeviceContext::~CudaDeviceContext() { - cudaDeviceReset(); -} - -bool CudaDeviceContext::Initialize() -{ - /* See if any cuda device is available. */ - int deviceCount = 0; - cudaGetDeviceCount(&deviceCount); - message("CUDA device count: %d\n", deviceCount); - if (deviceCount <= 0) { - return false; - } - cudaGLSetGLDevice(_GetCudaDeviceForCurrentGLContext()); - _initialized = true; - return true; -} - -#endif /* OPENSUBDIV_HAS_CUDA */ diff --git a/intern/opensubdiv/opensubdiv_device_context_cuda.h b/intern/opensubdiv/opensubdiv_device_context_cuda.h deleted file mode 100644 index eb30b76f507..00000000000 --- a/intern/opensubdiv/opensubdiv_device_context_cuda.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Adopted from OpenSubdiv with the following license: - * - * Copyright 2013 Pixar - * - * Licensed under the Apache License, Version 2.0 (the "Apache License") - * with the following modification; you may not use this file except in - * compliance with the Apache License and the following modification to it: - * Section 6. Trademarks. is deleted and replaced with: - * - * 6. Trademarks. This License does not grant permission to use the trade - * names, trademarks, service marks, or product names of the Licensor - * and its affiliates, except as required to comply with Section 4(c) of - * the License and to reproduce the content of the NOTICE file. - * - * You may obtain a copy of the Apache License at - * - * http: //www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Apache License with the above modification is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the Apache License for the specific - * language governing permissions and limitations under the Apache License. - * - */ - -#ifndef __OPENSUBDIV_DEV_CE_CONTEXT_CUDA_H__ -#define __OPENSUBDIV_DEV_CE_CONTEXT_CUDA_H__ - -struct ID3D11Device; - -class CudaDeviceContext { -public: - CudaDeviceContext(); - ~CudaDeviceContext(); - - static bool HAS_CUDA_VERSION_4_0(); - - /* Initialze cuda device from the current GL context. */ - bool Initialize(); - - /* Initialze cuda device from the ID3D11Device/ */ - bool Initialize(ID3D11Device *device); - - /* Returns true if the cuda device has already been initialized. */ - bool IsInitialized() const { - return _initialized; - } -private: - bool _initialized; -}; - -#endif /* __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__ */ diff --git a/intern/opensubdiv/opensubdiv_device_context_opencl.cc b/intern/opensubdiv/opensubdiv_device_context_opencl.cc deleted file mode 100644 index 4cacdc9e845..00000000000 --- a/intern/opensubdiv/opensubdiv_device_context_opencl.cc +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Adopted from OpenSubdiv with the following license: - * - * Copyright 2015 Pixar - * - * Licensed under the Apache License, Version 2.0 (the "Apache License") - * with the following modification; you may not use this file except in - * compliance with the Apache License and the following modification to it: - * Section 6. Trademarks. is deleted and replaced with: - * - * 6. Trademarks. This License does not grant permission to use the trade - * names, trademarks, service marks, or product names of the Licensor - * and its affiliates, except as required to comply with Section 4(c) of - * the License and to reproduce the content of the NOTICE file. - * - * You may obtain a copy of the Apache License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Apache License with the above modification is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the Apache License for the specific - * language governing permissions and limitations under the Apache License. - * - */ - -#ifdef OPENSUBDIV_HAS_OPENCL - -#ifdef _MSC_VER -# include "iso646.h" -#endif - -#include "opensubdiv_device_context_opencl.h" - -#if defined(_WIN32) -# include <windows.h> -#elif defined(__APPLE__) -# include <OpenGL/OpenGL.h> -#else -# include <GL/glx.h> -#endif - -#include <cstdio> -#include <cstring> -#include <string> - -#define message(...) // fprintf(stderr, __VA_ARGS__) -#define error(...) fprintf(stderr, __VA_ARGS__) - -/* Returns the first found platform. */ -static cl_platform_id findPlatform() { - cl_uint numPlatforms; - cl_int ciErrNum = clGetPlatformIDs(0, NULL, &numPlatforms); - if (ciErrNum != CL_SUCCESS) { - error("Error %d in clGetPlatformIDs call.\n", ciErrNum); - return NULL; - } - if (numPlatforms == 0) { - error("No OpenCL platform found.\n"); - return NULL; - } - cl_platform_id *clPlatformIDs = new cl_platform_id[numPlatforms]; - ciErrNum = clGetPlatformIDs(numPlatforms, clPlatformIDs, NULL); - char chBuffer[1024]; - for (cl_uint i = 0; i < numPlatforms; ++i) { - ciErrNum = clGetPlatformInfo(clPlatformIDs[i], CL_PLATFORM_NAME, - 1024, chBuffer,NULL); - if (ciErrNum == CL_SUCCESS) { - cl_platform_id platformId = clPlatformIDs[i]; - delete[] clPlatformIDs; - return platformId; - } - } - delete[] clPlatformIDs; - return NULL; -} - -/* Return. the device in clDevices which supports the extension. */ -static int findExtensionSupportedDevice(cl_device_id *clDevices, - int numDevices, - const char *extensionName) { - /* Find a device that supports sharing with GL/D3D11 - * (SLI / X-fire configurations) - */ - cl_int ciErrNum; - for (int i = 0; i < numDevices; ++i) { - /* Get extensions string size. */ - size_t extensionSize; - ciErrNum = clGetDeviceInfo(clDevices[i], - CL_DEVICE_EXTENSIONS, 0, NULL, - &extensionSize); - if (ciErrNum != CL_SUCCESS) { - error("Error %d in clGetDeviceInfo\n", ciErrNum); - return -1; - } - if (extensionSize > 0) { - /* Get extensions string. */ - char *extensions = new char[extensionSize]; - ciErrNum = clGetDeviceInfo(clDevices[i], CL_DEVICE_EXTENSIONS, - extensionSize, extensions, - &extensionSize); - if (ciErrNum != CL_SUCCESS) { - error("Error %d in clGetDeviceInfo\n", ciErrNum); - delete[] extensions; - continue; - } - std::string extString(extensions); - delete[] extensions; - /* Parse string. This is bit deficient since the extentions - * is space separated. - * - * The actual string would be "cl_khr_d3d11_sharing" - * or "cl_nv_d3d11_sharing" - */ - if (extString.find(extensionName) != std::string::npos) { - return i; - } - } - } - return -1; -} - -CLDeviceContext::CLDeviceContext() - : _clContext(NULL), - _clCommandQueue(NULL) { -} - -CLDeviceContext::~CLDeviceContext() { - if (_clCommandQueue) - clReleaseCommandQueue(_clCommandQueue); - if (_clContext) - clReleaseContext(_clContext); -} - -bool CLDeviceContext::HAS_CL_VERSION_1_1() -{ -#ifdef OPENSUBDIV_HAS_CLEW - static bool clewInitialized = false; - static bool clewLoadSuccess; - if (not clewInitialized) { - clewInitialized = true; - clewLoadSuccess = clewInit() == CLEW_SUCCESS; - if (!clewLoadSuccess) { - error("Loading OpenCL failed.\n"); - } - } - return clewLoadSuccess; -#endif - return true; -} - -bool CLDeviceContext::Initialize() -{ -#ifdef OPENSUBDIV_HAS_CLEW - if (!clGetPlatformIDs) { - error("Error clGetPlatformIDs function not bound.\n"); - return false; - } -#endif - cl_int ciErrNum; - cl_platform_id cpPlatform = findPlatform(); - -#if defined(_WIN32) - cl_context_properties props[] = { - CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), - CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), - CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, - 0 - }; -#elif defined(__APPLE__) - CGLContextObj kCGLContext = CGLGetCurrentContext(); - CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext); - cl_context_properties props[] = { - CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup, - 0 - }; -#else - cl_context_properties props[] = { - CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(), - CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(), - CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, - 0 - }; -#endif - -#if defined(__APPLE__) - _clContext = clCreateContext(props, 0, NULL, clLogMessagesToStdoutAPPLE, - NULL, &ciErrNum); - if (ciErrNum != CL_SUCCESS) { - error("Error %d in clCreateContext\n", ciErrNum); - return false; - } - - size_t devicesSize = 0; - clGetGLContextInfoAPPLE(_clContext, kCGLContext, - CL_CGL_DEVICES_FOR_SUPPORTED_VIRTUAL_SCREENS_APPLE, - 0, NULL, &devicesSize); - int numDevices = int(devicesSize / sizeof(cl_device_id)); - if (numDevices == 0) { - error("No sharable devices.\n"); - return false; - } - cl_device_id *clDevices = new cl_device_id[numDevices]; - clGetGLContextInfoAPPLE(_clContext, kCGLContext, - CL_CGL_DEVICES_FOR_SUPPORTED_VIRTUAL_SCREENS_APPLE, - numDevices * sizeof(cl_device_id), clDevices, NULL); - int clDeviceUsed = 0; - -#else // not __APPLE__ - /* Get the number of GPU devices available to the platform. */ - cl_uint numDevices = 0; - clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 0, NULL, &numDevices); - if (numDevices == 0) { - error("No CL GPU device found.\n"); - return false; - } - - /* Create the device list. */ - cl_device_id *clDevices = new cl_device_id[numDevices]; - clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, numDevices, clDevices, NULL); - - const char *extension = "cl_khr_gl_sharing"; - int clDeviceUsed = findExtensionSupportedDevice(clDevices, numDevices, - extension); - - if (clDeviceUsed < 0) { - error("No device found that supports CL/GL context sharing\n"); - delete[] clDevices; - return false; - } - - _clContext = clCreateContext(props, 1, &clDevices[clDeviceUsed], - NULL, NULL, &ciErrNum); -#endif // not __APPLE__ - if (ciErrNum != CL_SUCCESS) { - error("Error %d in clCreateContext\n", ciErrNum); - delete[] clDevices; - return false; - } - _clCommandQueue = clCreateCommandQueue(_clContext, clDevices[clDeviceUsed], - 0, &ciErrNum); - delete[] clDevices; - if (ciErrNum != CL_SUCCESS) { - error("Error %d in clCreateCommandQueue\n", ciErrNum); - return false; - } - return true; -} - -#endif /* OPENSUBDIV_HAS_OPENCL */ diff --git a/intern/opensubdiv/opensubdiv_device_context_opencl.h b/intern/opensubdiv/opensubdiv_device_context_opencl.h deleted file mode 100644 index a640dce1f07..00000000000 --- a/intern/opensubdiv/opensubdiv_device_context_opencl.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Adopted from OpenSubdiv with the following license: - * - * Copyright 2015 Pixar - * - * Licensed under the Apache License, Version 2.0 (the "Apache License") - * with the following modification; you may not use this file except in - * compliance with the Apache License and the following modification to it: - * Section 6. Trademarks. is deleted and replaced with: - * - * 6. Trademarks. This License does not grant permission to use the trade - * names, trademarks, service marks, or product names of the Licensor - * and its affiliates, except as required to comply with Section 4(c) of - * the License and to reproduce the content of the NOTICE file. - * - * You may obtain a copy of the Apache License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Apache License with the above modification is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the Apache License for the specific - * language governing permissions and limitations under the Apache License. - * - */ - -#ifndef __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__ -#define __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__ - -#include <opensubdiv/osd/opencl.h> - -class CLDeviceContext { -public: - CLDeviceContext(); - ~CLDeviceContext(); - - static bool HAS_CL_VERSION_1_1 (); - - bool Initialize(); - - bool IsInitialized() const { - return (_clContext != NULL); - } - - cl_context GetContext() const { - return _clContext; - } - cl_command_queue GetCommandQueue() const { - return _clCommandQueue; - } - -protected: - cl_context _clContext; - cl_command_queue _clCommandQueue; -}; - -#endif /* __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__ */ diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.cc b/intern/opensubdiv/opensubdiv_evaluator_capi.cc deleted file mode 100644 index fb5313b8501..00000000000 --- a/intern/opensubdiv/opensubdiv_evaluator_capi.cc +++ /dev/null @@ -1,516 +0,0 @@ -/* - * ***** 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 "opensubdiv_capi.h" - -#include <cstdio> -#include <vector> - -#ifdef _MSC_VER -# include "iso646.h" -#endif - -#include <opensubdiv/far/patchMap.h> -#include <opensubdiv/far/patchTable.h> -#include <opensubdiv/far/patchTableFactory.h> -#include <opensubdiv/osd/cpuEvaluator.h> -#include <opensubdiv/osd/cpuPatchTable.h> -#include <opensubdiv/osd/cpuVertexBuffer.h> -#include <opensubdiv/osd/mesh.h> -#include <opensubdiv/osd/types.h> - -#include "opensubdiv_intern.h" -#include "opensubdiv_topology_refiner.h" - -#include "MEM_guardedalloc.h" - -using OpenSubdiv::Osd::BufferDescriptor; -using OpenSubdiv::Osd::PatchCoord; -using OpenSubdiv::Far::PatchMap; -using OpenSubdiv::Far::PatchTable; -using OpenSubdiv::Far::PatchTableFactory; -using OpenSubdiv::Far::StencilTable; -using OpenSubdiv::Far::StencilTableFactory; -using OpenSubdiv::Far::TopologyRefiner; - -namespace { - -/* Helper class to wrap numerous of patch coords into a buffer. - * Used to pass coordinates to the CPU evaluator. Other evaluators - * are not supported. - */ -class PatchCoordBuffer : public std::vector<PatchCoord> { -public: - static PatchCoordBuffer *Create(int size) - { - PatchCoordBuffer *buffer = new PatchCoordBuffer(); - buffer->resize(size); - return buffer; - } - PatchCoord *BindCpuBuffer() { - return (PatchCoord*)&(*this)[0]; - } - int GetNumVertices() { - return size(); - } - void UpdateData(const PatchCoord *patch_coords, - int num_patch_coords) - { - memcpy(&(*this)[0], - (void*)patch_coords, - num_patch_coords * sizeof(PatchCoord)); - } -}; - -/* Helper class to wrap single of patch coord into a buffer. - * Used to pass coordinates to the CPU evaluator. Other evaluators - * are not supported. - */ -class SinglePatchCoordBuffer { -public: - SinglePatchCoordBuffer() { - } - SinglePatchCoordBuffer(const PatchCoord& patch_coord) - : patch_coord_(patch_coord){ - } - static SinglePatchCoordBuffer *Create() - { - SinglePatchCoordBuffer *buffer = new SinglePatchCoordBuffer(); - return buffer; - } - PatchCoord *BindCpuBuffer() { - return (PatchCoord*)&patch_coord_; - } - int GetNumVertices() { - return 1; - } - void UpdateData(const PatchCoord& patch_coord) - { - patch_coord_ = patch_coord; - } -protected: - PatchCoord patch_coord_; -}; - -/* Helper class which is aimed to be used in cases when buffer - * is small enough and better to be allocated in stack rather - * than in heap. - * - * TODO(sergey): Check if bare arrays could be used by CPU evaluator. - */ -template <int element_size, int num_verts> -class StackAllocatedBuffer { -public: - static PatchCoordBuffer *Create(int /*size*/) - { - StackAllocatedBuffer<element_size, num_verts> *buffer = - new StackAllocatedBuffer<element_size, num_verts>(); - return buffer; - } - float *BindCpuBuffer() { - return &data_[0]; - } - int GetNumVertices() { - return num_verts; - } - /* TODO(sergey): Support UpdateData(). */ -protected: - float data_[element_size * num_verts]; -}; - -/* Volatile evaluator which can be used from threads. - * - * TODO(sergey): Make it possible to evaluate coordinates in chunks. - */ -template<typename SRC_VERTEX_BUFFER, - typename EVAL_VERTEX_BUFFER, - typename STENCIL_TABLE, - typename PATCH_TABLE, - typename EVALUATOR, - typename DEVICE_CONTEXT = void> -class VolatileEvalOutput { -public: - typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache; - - VolatileEvalOutput(const StencilTable *vertex_stencils, - const StencilTable *varying_stencils, - int num_coarse_verts, - int num_total_verts, - const PatchTable *patch_table, - EvaluatorCache *evaluator_cache = NULL, - DEVICE_CONTEXT *device_context = NULL) - : src_desc_( /*offset*/ 0, /*length*/ 3, /*stride*/ 3), - src_varying_desc_(/*offset*/ 0, /*length*/ 3, /*stride*/ 3), - num_coarse_verts_(num_coarse_verts), - evaluator_cache_ (evaluator_cache), - device_context_(device_context) - { - using OpenSubdiv::Osd::convertToCompatibleStencilTable; - src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_verts, device_context_); - src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_verts, device_context_); - patch_table_ = PATCH_TABLE::Create(patch_table, device_context_); - patch_coords_ = NULL; - vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils, - device_context_); - varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils, - device_context_); - } - - ~VolatileEvalOutput() - { - delete src_data_; - delete src_varying_data_; - delete patch_table_; - delete vertex_stencils_; - delete varying_stencils_; - } - - void UpdateData(const float *src, int start_vertex, int num_vertices) - { - src_data_->UpdateData(src, start_vertex, num_vertices, device_context_); - } - - void UpdateVaryingData(const float *src, int start_vertex, int num_vertices) - { - src_varying_data_->UpdateData(src, - start_vertex, - num_vertices, - device_context_); - } - - void Refine() - { - BufferDescriptor dst_desc = src_desc_; - dst_desc.offset += num_coarse_verts_ * src_desc_.stride; - - const EVALUATOR *eval_instance = - OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, - src_desc_, - dst_desc, - device_context_); - - EVALUATOR::EvalStencils(src_data_, src_desc_, - src_data_, dst_desc, - vertex_stencils_, - eval_instance, - device_context_); - - dst_desc = src_varying_desc_; - dst_desc.offset += num_coarse_verts_ * src_varying_desc_.stride; - eval_instance = - OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, - src_varying_desc_, - dst_desc, - device_context_); - - EVALUATOR::EvalStencils(src_varying_data_, src_varying_desc_, - src_varying_data_, dst_desc, - varying_stencils_, - eval_instance, - device_context_); - } - - void EvalPatchCoord(PatchCoord& patch_coord, float P[3]) - { - StackAllocatedBuffer<6, 1> vertex_data; - BufferDescriptor vertex_desc(0, 3, 6); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); - const EVALUATOR *eval_instance = - OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, - src_desc_, - vertex_desc, - device_context_); - EVALUATOR::EvalPatches(src_data_, src_desc_, - &vertex_data, vertex_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, eval_instance, device_context_); - float *refined_verts = vertex_data.BindCpuBuffer(); - memcpy(P, refined_verts, sizeof(float) * 3); - } - - void EvalPatchesWithDerivatives(PatchCoord& patch_coord, - float P[3], - float dPdu[3], - float dPdv[3]) - { - StackAllocatedBuffer<6, 1> vertex_data, derivatives; - BufferDescriptor vertex_desc(0, 3, 6), - du_desc(0, 3, 6), - dv_desc(3, 3, 6); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); - const EVALUATOR *eval_instance = - OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, - src_desc_, - vertex_desc, - du_desc, - dv_desc, - device_context_); - EVALUATOR::EvalPatches(src_data_, src_desc_, - &vertex_data, vertex_desc, - &derivatives, du_desc, - &derivatives, dv_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, eval_instance, device_context_); - float *refined_verts = vertex_data.BindCpuBuffer(); - memcpy(P, refined_verts, sizeof(float) * 3); - if (dPdu != NULL || dPdv != NULL) { - float *refined_drivatives = derivatives.BindCpuBuffer(); - if (dPdu) { - memcpy(dPdu, refined_drivatives, sizeof(float) * 3); - } - if (dPdv) { - memcpy(dPdv, refined_drivatives + 3, sizeof(float) * 3); - } - } - } - - void EvalPatchVarying(PatchCoord& patch_coord, - float varying[3]) { - StackAllocatedBuffer<3, 1> varying_data; - BufferDescriptor varying_desc(0, 3, 3); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); - EVALUATOR const *eval_instance = - OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_, - src_varying_desc_, - varying_desc, - device_context_); - - EVALUATOR::EvalPatches(src_varying_data_, src_varying_desc_, - &varying_data, varying_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, eval_instance, device_context_); - float *refined_varying = varying_data.BindCpuBuffer(); - memcpy(varying, refined_varying, sizeof(float) * 3); - } -private: - SRC_VERTEX_BUFFER *src_data_; - SRC_VERTEX_BUFFER *src_varying_data_; - PatchCoordBuffer *patch_coords_; - PATCH_TABLE *patch_table_; - BufferDescriptor src_desc_; - BufferDescriptor src_varying_desc_; - int num_coarse_verts_; - - const STENCIL_TABLE *vertex_stencils_; - const STENCIL_TABLE *varying_stencils_; - - EvaluatorCache *evaluator_cache_; - DEVICE_CONTEXT *device_context_; -}; - -} /* namespace */ - -typedef VolatileEvalOutput<OpenSubdiv::Osd::CpuVertexBuffer, - OpenSubdiv::Osd::CpuVertexBuffer, - OpenSubdiv::Far::StencilTable, - OpenSubdiv::Osd::CpuPatchTable, - OpenSubdiv::Osd::CpuEvaluator> CpuEvalOutput; - -typedef struct OpenSubdiv_EvaluatorDescr { - CpuEvalOutput *eval_output; - const PatchMap *patch_map; - const PatchTable *patch_table; -} OpenSubdiv_EvaluatorDescr; - -OpenSubdiv_EvaluatorDescr *openSubdiv_createEvaluatorDescr( - OpenSubdiv_TopologyRefinerDescr *topology_refiner, - int subsurf_level) -{ - /* TODO(sergey): Look into re-using refiner with GLMesh. */ - TopologyRefiner *refiner = topology_refiner->osd_refiner; - if(refiner == NULL) { - /* Happens on bad topology. */ - return NULL; - } - /* Apply uniform refinement to the mesh so that we can use the - * limit evaluation API features. - */ - TopologyRefiner::UniformOptions options(subsurf_level); - refiner->RefineUniform(options); - /* Generate stencil table to update the bi-cubic patches control - * vertices after they have been re-posed (both for vertex & varying - * interpolation). - */ - StencilTableFactory::Options vertex_stencil_options; - vertex_stencil_options.generateOffsets = true; - vertex_stencil_options.generateIntermediateLevels = false; - const StencilTable *vertex_stencils = - StencilTableFactory::Create(*refiner, vertex_stencil_options); - StencilTableFactory::Options varying_stencil_options; - varying_stencil_options.generateOffsets = true; - varying_stencil_options.generateIntermediateLevels = false; - varying_stencil_options.interpolationMode = - StencilTableFactory::INTERPOLATE_VARYING; - const StencilTable *varying_stencils = - StencilTableFactory::Create(*refiner, varying_stencil_options); - /* Generate bi-cubic patch table for the limit surface. */ - PatchTableFactory::Options poptions; - poptions.SetEndCapType(PatchTableFactory::Options::ENDCAP_BSPLINE_BASIS); - const PatchTable *patch_table = - PatchTableFactory::Create(*refiner, poptions); - /* Append local points stencils. */ - const StencilTable *local_point_stencil_table = - patch_table->GetLocalPointStencilTable(); - if (local_point_stencil_table != NULL) { - const StencilTable *table = - StencilTableFactory::AppendLocalPointStencilTable( - *refiner, - vertex_stencils, - local_point_stencil_table); - delete vertex_stencils; - vertex_stencils = table; - } - const StencilTable *local_point_varying_stencil_table = - patch_table->GetLocalPointVaryingStencilTable(); - if (local_point_varying_stencil_table != NULL) { - const StencilTable *table = - StencilTableFactory::AppendLocalPointStencilTable( - *refiner, - varying_stencils, - local_point_varying_stencil_table); - delete varying_stencils; - varying_stencils = table; - } - - /* Total number of vertices = coarse verts + refined verts + gregory - * basis verts. - */ - const int num_total_verts = vertex_stencils->GetNumControlVertices() + - vertex_stencils->GetNumStencils(); - const int num_coarse_verts = refiner->GetLevel(0).GetNumVertices(); - /* Create OpenSubdiv's CPU side evaluator. */ - CpuEvalOutput *eval_output = new CpuEvalOutput(vertex_stencils, - varying_stencils, - num_coarse_verts, - num_total_verts, - patch_table); - OpenSubdiv::Far::PatchMap *patch_map = new PatchMap(*patch_table); - /* Wrap everything we need into an object which we control from our - * side. - */ - OpenSubdiv_EvaluatorDescr *evaluator_descr; - evaluator_descr = OBJECT_GUARDED_NEW(OpenSubdiv_EvaluatorDescr); - evaluator_descr->eval_output = eval_output; - evaluator_descr->patch_map = patch_map; - evaluator_descr->patch_table = patch_table; - /* TOOD(sergey): Look into whether we've got duplicated stencils arrays. */ - delete varying_stencils; - delete vertex_stencils; - return evaluator_descr; -} - -void openSubdiv_deleteEvaluatorDescr(OpenSubdiv_EvaluatorDescr *evaluator_descr) -{ - delete evaluator_descr->eval_output; - delete evaluator_descr->patch_map; - delete evaluator_descr->patch_table; - OBJECT_GUARDED_DELETE(evaluator_descr, OpenSubdiv_EvaluatorDescr); -} - -void openSubdiv_setEvaluatorCoarsePositions( - OpenSubdiv_EvaluatorDescr *evaluator_descr, - const float *positions, - int start_vertex_index, - int num_vertices) -{ - /* TODO(sergey): Add sanity check on indices. */ - evaluator_descr->eval_output->UpdateData(positions, - start_vertex_index, - num_vertices); -} - -void openSubdiv_setEvaluatorVaryingData( - OpenSubdiv_EvaluatorDescr *evaluator_descr, - const float *varying_data, - int start_vertex_index, - int num_vertices) -{ - /* TODO(sergey): Add sanity check on indices. */ - evaluator_descr->eval_output->UpdateVaryingData(varying_data, - start_vertex_index, - num_vertices); -} - -void openSubdiv_setEvaluatorCoarsePositionsFromBuffer( - OpenSubdiv_EvaluatorDescr *evaluator_descr, - const void *buffer, - int start_offset, - int stride, - int start_vertex_index, - int num_vertices) -{ - const unsigned char *current_buffer = (unsigned char *)buffer; - current_buffer += start_offset; - /* TODO(sergey): Add sanity check on indices. */ - for (int i = 0; i < num_vertices; ++i) { - const int current_vertex_index = start_vertex_index + i; - evaluator_descr->eval_output->UpdateData((float *)current_buffer, - current_vertex_index, - 1); - current_buffer += stride; - } -} - -void openSubdiv_refineEvaluator(OpenSubdiv_EvaluatorDescr *evaluator_descr) -{ - evaluator_descr->eval_output->Refine(); -} - -void openSubdiv_evaluateLimit(OpenSubdiv_EvaluatorDescr *evaluator_descr, - int osd_face_index, - float face_u, float face_v, - float P[3], - float dPdu[3], - float dPdv[3]) -{ - assert((face_u >= 0.0f) && (face_u <= 1.0f) && (face_v >= 0.0f) && (face_v <= 1.0f)); - const PatchTable::PatchHandle *handle = - evaluator_descr->patch_map->FindPatch(osd_face_index, face_u, face_v); - PatchCoord patch_coord(*handle, face_u, face_v); - if (dPdu != NULL || dPdv != NULL) { - evaluator_descr->eval_output->EvalPatchesWithDerivatives(patch_coord, - P, - dPdu, - dPdv); - } - else { - evaluator_descr->eval_output->EvalPatchCoord(patch_coord, P); - } -} - -void openSubdiv_evaluateVarying(OpenSubdiv_EvaluatorDescr *evaluator_descr, - int osd_face_index, - float face_u, float face_v, - float varying[3]) -{ - assert((face_u >= 0.0f) && (face_u <= 1.0f) && (face_v >= 0.0f) && (face_v <= 1.0f)); - const PatchTable::PatchHandle *handle = - evaluator_descr->patch_map->FindPatch(osd_face_index, face_u, face_v); - PatchCoord patch_coord(*handle, face_u, face_v); - evaluator_descr->eval_output->EvalPatchVarying(patch_coord, varying); -} diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.h b/intern/opensubdiv/opensubdiv_evaluator_capi.h new file mode 100644 index 00000000000..07a55cfd349 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_evaluator_capi.h @@ -0,0 +1,117 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_EVALUATOR_CAPI_H_ +#define OPENSUBDIV_EVALUATOR_CAPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct OpenSubdiv_EvaluatorInternal; +struct OpenSubdiv_TopologyRefiner; + +typedef struct OpenSubdiv_Evaluator { + // Set coarse positions from a continuous array of coordinates. + void (*setCoarsePositions)(struct OpenSubdiv_Evaluator* evaluator, + const float* positions, + const int start_vertex_index, + const int num_vertices); + // Set varying data from a continuous array of data. + void (*setVaryingData)(struct OpenSubdiv_Evaluator* evaluator, + const float* varying_data, + const int start_vertex_index, const int num_vertices); + // Set face varying data from a continuous array of data. + // + // TODO(sergey): Find a better name for vertex here. It is not the vertex of + // geometry, but a vertex of UV map. + void (*setFaceVaryingData)(struct OpenSubdiv_Evaluator* evaluator, + const float* face_varying_data, + const int start_vertex_index, + const int num_vertices); + + // Set coarse vertex position from a continuous memory buffer where + // first coordinate starts at offset of `start_offset` and there is `stride` + // bytes between adjacent vertex coordinates. + void (*setCoarsePositionsFromBuffer)(struct OpenSubdiv_Evaluator* evaluator, + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices); + // Set varying data from a continuous memory buffer where + // first coordinate starts at offset of `start_offset` and there is `stride` + // bytes between adjacent vertex coordinates. + void (*setVaryingDataFromBuffer)(struct OpenSubdiv_Evaluator* evaluator, + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices); + // Set face varying data from a continuous memory buffer where + // first coordinate starts at offset of `start_offset` and there is `stride` + // bytes between adjacent vertex coordinates. + // + // TODO(sergey): Find a better name for vertex here. It is not the vertex of + // geometry, but a vertex of UV map. + void (*setFaceVaryingDataFromBuffer)(struct OpenSubdiv_Evaluator* evaluator, + const void* buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices); + + // Refine after coarse positions update. + void (*refine)(struct OpenSubdiv_Evaluator* evaluator); + + // Evaluate given ptex face at given bilinear coordinate. + // If derivatives are NULL, they will not be evaluated. + void (*evaluateLimit)(struct OpenSubdiv_Evaluator* evaluator, + const int ptex_face_index, + float face_u, float face_v, + float P[3], float dPdu[3], float dPdv[3]); + + // Evaluate varying data at a given bilinear coordinate of given ptex face. + void (*evaluateVarying)(struct OpenSubdiv_Evaluator* evaluator, + const int ptex_face_index, + float face_u, float face_v, + float varying[3]); + + // Evaluate face-varying data at a given bilinear coordinate of given + // ptex face. + void (*evaluateFaceVarying)(struct OpenSubdiv_Evaluator* evaluator, + const int ptex_face_index, + float face_u, float face_v, + float face_varying[2]); + + // Internal storage for the use in this module only. + // + // This is where actual OpenSubdiv's evaluator is living. + struct OpenSubdiv_EvaluatorInternal* internal; +} OpenSubdiv_Evaluator; + +OpenSubdiv_Evaluator* openSubdiv_createEvaluatorFromTopologyRefiner( + struct OpenSubdiv_TopologyRefiner* topology_refiner); + +void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator* evaluator); + +#ifdef __cplusplus +} +#endif + +#endif // OPENSUBDIV_EVALUATOR_CAPI_H_ diff --git a/intern/opensubdiv/opensubdiv_gl_mesh.h b/intern/opensubdiv/opensubdiv_gl_mesh.h deleted file mode 100644 index 1e99fc5bce4..00000000000 --- a/intern/opensubdiv/opensubdiv_gl_mesh.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * ***** 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) 2018 Blender Foundation. - * All rights reserved. - * - * Contributor(s): Sergey Sharybin. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#ifndef __OPENSUBDIV_GL_MESH_H__ -#define __OPENSUBDIV_GL_MESH_H__ - -struct OpenSubdiv_GLMeshDescr; -struct OpenSubdiv_TopologyRefinerDescr; -struct OpenSubdiv_GLMeshFVarData; - -typedef struct OpenSubdiv_GLMesh { - int evaluator_type; - OpenSubdiv_GLMeshDescr *descriptor; - OpenSubdiv_TopologyRefinerDescr *topology_refiner; - OpenSubdiv_GLMeshFVarData *fvar_data; -} OpenSubdiv_GLMesh; - -#endif /* __OPENSUBDIV_GL_MESH_H__ */ diff --git a/intern/opensubdiv/opensubdiv_gl_mesh_capi.h b/intern/opensubdiv/opensubdiv_gl_mesh_capi.h new file mode 100644 index 00000000000..971f6b9dcd3 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_gl_mesh_capi.h @@ -0,0 +1,92 @@ +// Copyright 2013 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_CAPI_GL_MESH_CAPI_H_ +#define OPENSUBDIV_CAPI_GL_MESH_CAPI_H_ + +#include <stdint.h> // for bool + +#include "opensubdiv_capi_type.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct OpenSubdiv_GLMeshInternal; + +// Mesh which is displayable in OpenGL context. +typedef struct OpenSubdiv_GLMesh { + ////////////////////////////////////////////////////////////////////////////// + // Subdivision/topology part. + + // Returns the GL index buffer containing the patch control vertices. + unsigned int (*getPatchIndexBuffer)(struct OpenSubdiv_GLMesh* gl_mesh); + + // Bind GL buffer which contains vertices (VBO). + // TODO(sergey): Is this a coarse vertices? + void (*bindVertexBuffer)(struct OpenSubdiv_GLMesh* gl_mesh); + + // Set coarse positions from a continuous array of coordinates. + void (*setCoarsePositions)(struct OpenSubdiv_GLMesh* gl_mesh, + const float* positions, + const int start_vertex, + const int num_vertices); + // TODO(sergey): setCoarsePositionsFromBuffer(). + + // Refine after coarse positions update. + void (*refine)(struct OpenSubdiv_GLMesh* gl_mesh); + + // Synchronize after coarse positions update and refine. + void (*synchronize)(struct OpenSubdiv_GLMesh* gl_mesh); + + ////////////////////////////////////////////////////////////////////////////// + // Drawing part. + + // Prepare mesh for display. + void (*prepareDraw)(struct OpenSubdiv_GLMesh* gl_mesh, + const bool use_osd_glsl, + const int active_uv_index); + + // Draw given range of patches. + // + // If fill_quads is false, then patches are drawn in wireframe. + void (*drawPatches)(struct OpenSubdiv_GLMesh *gl_mesh, + const bool fill_quads, + const int start_patch, const int num_patches); + + // Internal storage for the use in this module only. + // + // Tease: This contains an actual OpenSubdiv's Mesh object. + struct OpenSubdiv_GLMeshInternal* internal; +} OpenSubdiv_GLMesh; + +OpenSubdiv_GLMesh* openSubdiv_createOsdGLMeshFromTopologyRefiner( + struct OpenSubdiv_TopologyRefiner* topology_refiner, + eOpenSubdivEvaluator evaluator_type); + +void openSubdiv_deleteOsdGLMesh(OpenSubdiv_GLMesh *gl_mesh); + +// Global resources needed for GL mesh drawing. +bool openSubdiv_initGLMeshDrawingResources(void); +void openSubdiv_deinitGLMeshDrawingResources(void); + +#ifdef __cplusplus +} +#endif + +#endif // OPENSUBDIV_CAPI_GL_MESH_CAPI_H_ diff --git a/intern/opensubdiv/opensubdiv_intern.h b/intern/opensubdiv/opensubdiv_intern.h deleted file mode 100644 index ccb32f9d0ac..00000000000 --- a/intern/opensubdiv/opensubdiv_intern.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * ***** 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 ***** - */ - -#ifndef __OPENSUBDIV_INTERN_H__ -#define __OPENSUBDIV_INTERN_H__ - -/* Perform full topology validation when exporting it to OpenSubdiv. */ -#ifdef NDEBUG -# undef OPENSUBDIV_VALIDATE_TOPOLOGY -#else -/* TODO(sergey): Always disabled for now, the check doesn't handle - * multiple non-manifolds from the OpenSubdiv side currently. - */ -# undef OPENSUBDIV_VALIDATE_TOPOLOGY -#endif - -/* Currently OpenSubdiv expects topology to be oriented, - * but sometimes it's handy to disable orientation code - * to check whether it causes some weird issues by using - * pre-oriented model. - */ -#define OPENSUBDIV_ORIENT_TOPOLOGY - -#endif /* __OPENSUBDIV_INTERN_H__ */ diff --git a/intern/opensubdiv/opensubdiv_topology_refiner_capi.h b/intern/opensubdiv/opensubdiv_topology_refiner_capi.h new file mode 100644 index 00000000000..103ccd2c1f9 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_topology_refiner_capi.h @@ -0,0 +1,110 @@ +// Copyright 2018 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_TOPOLOGY_REFINER_CAPI_H_ +#define OPENSUBDIV_TOPOLOGY_REFINER_CAPI_H_ + +#include <stdint.h> // for bool + +#ifdef __cplusplus +extern "C" { +#endif + +struct OpenSubdiv_Converter; +struct OpenSubdiv_TopologyRefinerInternal; + +// Those settings don't really belong to OpenSubdiv's topology refiner, but +// we are keeping track of them on our side of topology refiner. This is to +// make it possible to ensure we are not trying to abuse same OpenSubdiv's +// topology refiner with different subdivision levels or with different +// adaptive settings. +typedef struct OpenSubdiv_TopologyRefinerSettings { + bool is_adaptive; + int level; +} OpenSubdiv_TopologyRefinerSettings; + +typedef struct OpenSubdiv_TopologyRefiner { + // Query subdivision level the refiner is created for. + int (*getSubdivisionLevel)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner); + bool (*getIsAdaptive)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner); + + // Query basic topology information from base level. + int (*getNumVertices)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner); + int (*getNumEdges)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner); + int (*getNumFaces)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner); + int (*getNumFaceVertices)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner, + const int face_index); + + // Ptex face corresponds to OpenSubdiv's internal "patch" and to Blender's + // subdivision grid. The rule commes as: + // - Triangle face consist of 3 ptex faces, ordered in the order of + // face-vertices. + // - Quad face consists of a single ptex face. + // - N-gons (similar to triangle) consists of N ptex faces, ordered same + // way as for triangle. + int (*getNumFacePtexFaces)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner, + const int face_index); + int (*getNumPtexFaces)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner); + + // Initialize a per-base-face offset measured in ptex face indices. + // + // Basically, face_ptex_offset[base_face_index] is a total number of ptex + // faces created for bases faces [0 .. base_face_index - 1]. + // + // The array must contain at least total number of ptex faces elements. + void (*fillFacePtexIndexOffset)( + const struct OpenSubdiv_TopologyRefiner* topology_refiner, + int* face_ptex_index_offset); + + // Internal storage for the use in this module only. + // + // Tease: Contains actual OpenSubdiv's refiner and (optionally) some other + // data and state needed for an internbal use. + struct OpenSubdiv_TopologyRefinerInternal* internal; +} OpenSubdiv_TopologyRefiner; + +OpenSubdiv_TopologyRefiner* openSubdiv_createTopologyRefinerFromConverter( + struct OpenSubdiv_Converter* converter, + const OpenSubdiv_TopologyRefinerSettings* settings); + +void openSubdiv_deleteTopologyRefiner( + OpenSubdiv_TopologyRefiner* topology_refiner); + +// Compare given topology refiner with converter. Returns truth if topology +// refiner matches given converter, false otherwise. +// +// This allows users to construct converter (which is supposed to be cheap) +// and compare with existing refiner before going into more computationally +// complicated parts of subdivision process. +bool openSubdiv_topologyRefinerCompareWithConverter( + const OpenSubdiv_TopologyRefiner* topology_refiner, + const OpenSubdiv_Converter* converter); + +#ifdef __cplusplus +} +#endif + +#endif // OPENSUBDIV_TOPOLOGY_REFINER_CAPI_H_ diff --git a/intern/opensubdiv/opensubdiv_utils_capi.cc b/intern/opensubdiv/opensubdiv_utils_capi.cc deleted file mode 100644 index 72e3751e6b3..00000000000 --- a/intern/opensubdiv/opensubdiv_utils_capi.cc +++ /dev/null @@ -1,88 +0,0 @@ -/* - * ***** 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) 2013 Blender Foundation. - * All rights reserved. - * - * Contributor(s): Sergey Sharybin. - * Brecht van Lommel - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#include "opensubdiv_capi.h" - -#include <cstring> -#include <GL/glew.h> - -#ifdef _MSC_VER -# include "iso646.h" -#endif - -#ifdef OPENSUBDIV_HAS_OPENCL -# include "opensubdiv_device_context_opencl.h" -#endif /* OPENSUBDIV_HAS_OPENCL */ - -#ifdef OPENSUBDIV_HAS_CUDA -# include "opensubdiv_device_context_cuda.h" -#endif /* OPENSUBDIV_HAS_CUDA */ - -int openSubdiv_getAvailableEvaluators(void) -{ - int flags = OPENSUBDIV_EVALUATOR_CPU; - -#ifdef OPENSUBDIV_HAS_OPENMP - flags |= OPENSUBDIV_EVALUATOR_OPENMP; -#endif /* OPENSUBDIV_HAS_OPENMP */ - -#ifdef OPENSUBDIV_HAS_OPENCL - if (CLDeviceContext::HAS_CL_VERSION_1_1()) { - flags |= OPENSUBDIV_EVALUATOR_OPENCL; - } -#endif /* OPENSUBDIV_HAS_OPENCL */ - -#ifdef OPENSUBDIV_HAS_CUDA - if (CudaDeviceContext::HAS_CUDA_VERSION_4_0()) { - flags |= OPENSUBDIV_EVALUATOR_CUDA; - } -#endif /* OPENSUBDIV_HAS_OPENCL */ - -#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK - if (GLEW_VERSION_4_1) { - flags |= OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK; - } -#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */ - -#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE - if (GLEW_VERSION_4_3 || GLEW_ARB_compute_shader) { - flags |= OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; - } -#endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */ - - return flags; -} - -void openSubdiv_init(void) -{ - /* Ensure all OpenGL strings are cached. */ - (void)openSubdiv_getAvailableEvaluators(); -} - -void openSubdiv_cleanup(void) -{ - openSubdiv_osdGLDisplayDeinit(); -} diff --git a/intern/opensubdiv/gpu_shader_opensubdiv_fragment.glsl b/intern/opensubdiv/shader/gpu_shader_opensubdiv_fragment.glsl index 1e36d549360..1e36d549360 100644 --- a/intern/opensubdiv/gpu_shader_opensubdiv_fragment.glsl +++ b/intern/opensubdiv/shader/gpu_shader_opensubdiv_fragment.glsl diff --git a/intern/opensubdiv/gpu_shader_opensubdiv_geometry.glsl b/intern/opensubdiv/shader/gpu_shader_opensubdiv_geometry.glsl index b16a5cca733..b16a5cca733 100644 --- a/intern/opensubdiv/gpu_shader_opensubdiv_geometry.glsl +++ b/intern/opensubdiv/shader/gpu_shader_opensubdiv_geometry.glsl diff --git a/intern/opensubdiv/gpu_shader_opensubdiv_vertex.glsl b/intern/opensubdiv/shader/gpu_shader_opensubdiv_vertex.glsl index 6fcf5ad20cd..6fcf5ad20cd 100644 --- a/intern/opensubdiv/gpu_shader_opensubdiv_vertex.glsl +++ b/intern/opensubdiv/shader/gpu_shader_opensubdiv_vertex.glsl |