Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2018-07-16 10:28:05 +0300
committerSergey Sharybin <sergey.vfx@gmail.com>2018-07-16 10:52:37 +0300
commitd9203820467665ba19e1956e035de663849e695c (patch)
tree62d01753cafe31ac739c27f45272e2e4c0938c80 /intern/opensubdiv/internal
parentb29b73a67eca3e5ac71c4b7c89f25bf3fb694cc3 (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/opensubdiv/internal')
-rw-r--r--intern/opensubdiv/internal/opensubdiv.cc99
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_factory.cc438
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_factory.h37
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_internal.cc70
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_internal.h50
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_orient.cc66
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_orient.h50
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_orient_impl.h66
-rw-r--r--intern/opensubdiv/internal/opensubdiv_device_context_cuda.cc226
-rw-r--r--intern/opensubdiv/internal/opensubdiv_device_context_cuda.h54
-rw-r--r--intern/opensubdiv/internal/opensubdiv_device_context_opencl.cc269
-rw-r--r--intern/opensubdiv/internal/opensubdiv_device_context_opencl.h52
-rw-r--r--intern/opensubdiv/internal/opensubdiv_evaluator.cc154
-rw-r--r--intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc734
-rw-r--r--intern/opensubdiv/internal/opensubdiv_evaluator_internal.h134
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh.cc287
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.cc587
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh_draw.h39
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.cc175
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh_fvar.h57
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.cc32
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gl_mesh_internal.h43
-rw-r--r--intern/opensubdiv/internal/opensubdiv_gpu.cc788
-rw-r--r--intern/opensubdiv/internal/opensubdiv_internal.h38
-rw-r--r--intern/opensubdiv/internal/opensubdiv_topology_refiner.cc315
-rw-r--r--intern/opensubdiv/internal/opensubdiv_topology_refiner.h41
-rw-r--r--intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.cc26
-rw-r--r--intern/opensubdiv/internal/opensubdiv_topology_refiner_internal.h47
-rw-r--r--intern/opensubdiv/internal/opensubdiv_util.cc61
-rw-r--r--intern/opensubdiv/internal/opensubdiv_util.h39
30 files changed, 5074 insertions, 0 deletions
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/internal/opensubdiv_gpu.cc b/intern/opensubdiv/internal/opensubdiv_gpu.cc
new file mode 100644
index 00000000000..d28b48ddd18
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_gpu.cc
@@ -0,0 +1,788 @@
+/*
+ * ***** 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 *****
+ */
+
+#include "opensubdiv_capi.h"
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include <cstdio>
+#include <cmath>
+#include <GL/glew.h>
+
+#include <opensubdiv/osd/glMesh.h>
+
+#ifdef OPENSUBDIV_HAS_CUDA
+# include <opensubdiv/osd/cudaGLVertexBuffer.h>
+#endif /* OPENSUBDIV_HAS_CUDA */
+
+#include <opensubdiv/osd/cpuGLVertexBuffer.h>
+#include <opensubdiv/osd/cpuEvaluator.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "opensubdiv_capi.h"
+#include "opensubdiv_gl_mesh.h"
+#include "opensubdiv_topology_refiner.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): This is 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;
+
+struct OpenSubdiv_GLMeshFVarData
+{
+ OpenSubdiv_GLMeshFVarData() :
+ texture_buffer(0), offset_buffer(0) {
+ }
+
+ ~OpenSubdiv_GLMeshFVarData()
+ {
+ Release();
+ }
+
+ void 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 Create(const OpenSubdiv::Far::TopologyRefiner *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 = 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 < (int)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 (refiner->IsUniform()) {
+ const int num_values_max = refiner->GetLevel(max_level).GetNumFVarValues(channel);
+ fvar_data_offset += num_values_max * fvar_width;
+ } else {
+ const int num_values_total = 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);
+ }
+ GLuint texture_buffer;
+ GLuint offset_buffer;
+ std::vector<int> channel_offsets;
+ int fvar_width;
+};
+
+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); /* GL_TEXTURE0 */
+
+ glProgramUniform1i(program,
+ glGetUniformLocation(program, "FVarDataOffsetBuffer"),
+ 30); /* GL_TEXTURE30 */
+
+ glProgramUniform1i(program,
+ glGetUniformLocation(program, "FVarDataBuffer"),
+ 31); /* GL_TEXTURE31 */
+ }
+ else {
+ glUseProgram(program);
+ glUniform1i(glGetUniformLocation(program, "texture_buffer"), 0); /* GL_TEXTURE0 */
+ glUniform1i(glGetUniformLocation(program, "FVarDataOffsetBuffer"), 30); /* GL_TEXTURE30 */
+ glUniform1i(glGetUniformLocation(program, "FVarDataBuffer"), 31); /* GL_TEXTURE31 */
+ 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: 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 */
+ if (gl_mesh->fvar_data != NULL) {
+ if (gl_mesh->fvar_data->texture_buffer) {
+ glActiveTexture(GL_TEXTURE31);
+ glBindTexture(GL_TEXTURE_BUFFER, gl_mesh->fvar_data->texture_buffer);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ if (gl_mesh->fvar_data->offset_buffer) {
+ glActiveTexture(GL_TEXTURE30);
+ glBindTexture(GL_TEXTURE_BUFFER, gl_mesh->fvar_data->offset_buffer);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ glUniform1i(glGetUniformLocation(program, "osd_fvar_count"),
+ gl_mesh->fvar_data->fvar_width);
+ if (gl_mesh->fvar_data->channel_offsets.size() > 0 &&
+ g_active_uv_index >= 0)
+ {
+ glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"),
+ gl_mesh->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_osdGLDisplayInit(void)
+{
+ static bool need_init = true;
+ static bool init_success = false;
+
+ if (need_init) {
+ /* TODO: 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_osdGLDisplayDeinit(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
+}
+
+void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
+ 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: 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
+ }
+}
+
+static 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 */
+ if (gl_mesh->fvar_data != NULL) {
+ if (gl_mesh->fvar_data->texture_buffer) {
+ glActiveTexture(GL_TEXTURE31);
+ glBindTexture(GL_TEXTURE_BUFFER,
+ gl_mesh->fvar_data->texture_buffer);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ if (gl_mesh->fvar_data->offset_buffer) {
+ glActiveTexture(GL_TEXTURE30);
+ glBindTexture(GL_TEXTURE_BUFFER,
+ gl_mesh->fvar_data->offset_buffer);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ GLint location = glGetUniformLocation(program, "osd_fvar_count");
+ if (location != -1) {
+ glUniform1i(location, gl_mesh->fvar_data->fvar_width);
+ }
+
+ location = glGetUniformLocation(program, "osd_active_uv_offset");
+ if (location != -1) {
+ if (gl_mesh->fvar_data->channel_offsets.size() > 0 &&
+ g_active_uv_index >= 0)
+ {
+ glUniform1i(location,
+ gl_mesh->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;
+}
+
+static 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,
+ (void *)(start_element * sizeof(unsigned int)));
+}
+
+static void finish_patchDraw(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);
+ }
+}
+
+static void draw_partition_patches_range(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 < (int)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 draw_all_patches(GLMeshInterface *mesh,
+ GLuint program)
+{
+ const OpenSubdiv::Osd::PatchArrayVector& patches =
+ mesh->GetPatchTable()->GetPatchArrays();
+ for (int i = 0; i < (int)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());
+ }
+ }
+}
+
+void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
+ int fill_quads,
+ int start_patch,
+ int num_patches)
+{
+ GLMeshInterface *mesh =
+ (GLMeshInterface *)(gl_mesh->descriptor);
+
+ /* Make sure all global invariants are initialized. */
+ if (!openSubdiv_osdGLDisplayInit()) {
+ return;
+ }
+
+ /* Setup GLSL/OpenGL to draw patches in current context. */
+ GLuint program = prepare_patchDraw(gl_mesh, fill_quads != 0);
+
+ if (start_patch != -1) {
+ draw_partition_patches_range(mesh,
+ program,
+ start_patch,
+ num_patches);
+ }
+ else {
+ draw_all_patches(mesh, program);
+ }
+
+ /* Finish patch drawing by restoring all changes to the OpenGL context. */
+ finish_patchDraw(fill_quads != 0);
+}
+
+void openSubdiv_osdGLAllocFVar(OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ OpenSubdiv_GLMesh *gl_mesh,
+ const float *fvar_data)
+{
+ GLMeshInterface *mesh =
+ (GLMeshInterface *)(gl_mesh->descriptor);
+ gl_mesh->fvar_data = OBJECT_GUARDED_NEW(OpenSubdiv_GLMeshFVarData);
+ gl_mesh->fvar_data->Create(topology_refiner->osd_refiner,
+ mesh->GetFarPatchTable(),
+ 2,
+ fvar_data);
+}
+
+void openSubdiv_osdGLDestroyFVar(OpenSubdiv_GLMesh *gl_mesh)
+{
+ if (gl_mesh->fvar_data != NULL) {
+ OBJECT_GUARDED_DELETE(gl_mesh->fvar_data, OpenSubdiv_GLMeshFVarData);
+ }
+}
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/internal/opensubdiv_topology_refiner.h b/intern/opensubdiv/internal/opensubdiv_topology_refiner.h
new file mode 100644
index 00000000000..b00f6a54201
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_topology_refiner.h
@@ -0,0 +1,41 @@
+/*
+ * ***** 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) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENSUBDIV_TOPOLOGY_REFINER_H__
+#define __OPENSUBDIV_TOPOLOGY_REFINER_H__
+
+#include <opensubdiv/far/topologyRefiner.h>
+
+typedef struct OpenSubdiv_TopologyRefinerDescr {
+ OpenSubdiv::Far::TopologyRefiner *osd_refiner;
+
+ /* TODO(sergey): For now only, need to find better place
+ * after revisiting whole OSD drawing pipeline and Blender
+ * integration.
+ */
+ std::vector<float> uvs;
+} OpenSubdiv_TopologyRefinerDescr;
+
+#endif /* __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_