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:
Diffstat (limited to 'intern/opensubdiv/internal')
-rw-r--r--intern/opensubdiv/internal/opensubdiv.cc99
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_factory.cc469
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_factory.h37
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_internal.cc87
-rw-r--r--intern/opensubdiv/internal/opensubdiv_converter_internal.h51
-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.cc158
-rw-r--r--intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc835
-rw-r--r--intern/opensubdiv/internal/opensubdiv_evaluator_internal.h137
-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_internal.h35
-rw-r--r--intern/opensubdiv/internal/opensubdiv_topology_refiner.cc367
-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.h44
28 files changed, 4456 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..901a421314a
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_converter_factory.cc
@@ -0,0 +1,469 @@
+// 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);
+ }
+ // Vertices.
+ const int num_vertices = converter->getNumVertices(converter);
+ setNumBaseVertices(refiner, num_vertices);
+ // If converter does not provide full topology, we are done.
+ if (!converter->specifiesFullTopology(converter)) {
+ return true;
+ }
+ // Edges and edge-faces.
+ const int num_edges = converter->getNumEdges(converter);
+ setNumBaseEdges(refiner, num_edges);
+ for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
+ const int num_edge_faces =
+ converter->getNumEdgeFaces(converter, edge_index);
+ setNumBaseEdgeFaces(refiner, edge_index, num_edge_faces);
+ }
+ // Vertex-faces and vertex-edges.
+ for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
+ const int num_vert_edges =
+ converter->getNumVertexEdges(converter, vertex_index);
+ const int num_vert_faces =
+ converter->getNumVertexFaces(converter, vertex_index);
+ setNumBaseVertexEdges(refiner, vertex_index, num_vert_edges);
+ setNumBaseVertexFaces(refiner, vertex_index, num_vert_faces);
+ }
+ return true;
+}
+
+template <>
+inline bool
+TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
+ TopologyRefiner& refiner,
+ const TopologyRefinerData& cb_data) {
+ using Far::IndexArray;
+ const OpenSubdiv_Converter* converter = cb_data.converter;
+ const bool full_topology_specified =
+ converter->specifiesFullTopology(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]);
+ if (full_topology_specified) {
+ IndexArray dst_face_edges = getBaseFaceEdges(refiner, face_index);
+ converter->getFaceEdges(converter, face_index, &dst_face_edges[0]);
+ }
+ }
+ // If converter does not provide full topology, we are done.
+ if (!full_topology_specified) {
+ return true;
+ }
+ // 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 bool full_topology_specified =
+ converter->specifiesFullTopology(converter);
+ const int num_edges = converter->getNumEdges(converter);
+ for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
+ const float sharpness =
+ converter->getEdgeSharpness(converter, edge_index);
+ if (sharpness < 1e-6f) {
+ continue;
+ }
+ if (full_topology_specified) {
+ setBaseEdgeSharpness(refiner, edge_index, sharpness);
+ } else {
+ int edge_vertices[2];
+ converter->getEdgeVertices(converter, edge_index, edge_vertices);
+ const int base_edge_index = findBaseEdge(
+ refiner, edge_vertices[0], edge_vertices[1]);
+ if (base_edge_index == OpenSubdiv::Far::INDEX_INVALID) {
+ printf("OpenSubdiv Error: failed to find reconstructed edge\n");
+ return false;
+ }
+ setBaseEdgeSharpness(refiner, base_edge_index, sharpness);
+ }
+ }
+ // OpenSubdiv expects non-manifold vertices to be sharp but at the time it
+ // handles correct cases when vertex is a corner of plane. Currently mark
+ // vertices which are adjacent to a loose edge as sharp, but this decision
+ // needs some more investigation.
+ const int num_vertices = converter->getNumVertices(converter);
+ for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
+ ConstIndexArray vertex_edges = getBaseVertexEdges(refiner, vertex_index);
+ if (converter->isInfiniteSharpVertex(converter, vertex_index)) {
+ setBaseVertexSharpness(
+ refiner, vertex_index, Crease::SHARPNESS_INFINITE);
+ continue;
+ }
+ float sharpness = converter->getVertexSharpness(converter, vertex_index);
+ if (vertex_edges.size() == 2) {
+ const int edge0 = vertex_edges[0], edge1 = vertex_edges[1];
+ const float sharpness0 = refiner._levels[0]->getEdgeSharpness(edge0);
+ const float sharpness1 = refiner._levels[0]->getEdgeSharpness(edge1);
+ // TODO(sergey): Find a better mixing between edge and vertex sharpness.
+ sharpness += std::min(sharpness0, sharpness1);
+ sharpness = std::min(sharpness, 1.0f);
+ }
+ setBaseVertexSharpness(refiner, vertex_index, sharpness);
+ }
+ return true;
+}
+
+template <>
+inline bool
+TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology(
+ TopologyRefiner& refiner,
+ const TopologyRefinerData& cb_data) {
+ const OpenSubdiv_Converter* converter = cb_data.converter;
+ const int num_layers = converter->getNumUVLayers(converter);
+ if (num_layers <= 0) {
+ // No UV maps, we can skip any face-varying data.
+ return true;
+ }
+ const int num_faces = getNumBaseFaces(refiner);
+ for (int layer_index = 0; layer_index < num_layers; ++layer_index) {
+ converter->precalcUVLayer(converter, layer_index);
+ const int num_uvs = converter->getNumUVCoordinates(converter);
+ // Fill in per-corner index of the UV.
+ const int channel = createBaseFVarChannel(refiner, num_uvs);
+ // TODO(sergey): Need to check whether converter changed the winding of
+ // face to match OpenSubdiv's expectations.
+ for (int face_index = 0; face_index < num_faces; ++face_index) {
+ Far::IndexArray dst_face_uvs =
+ getBaseFaceFVarValues(refiner, face_index, channel);
+ for (int corner = 0; corner < dst_face_uvs.size(); ++corner) {
+ const int uv_index =
+ converter->getFaceCornerUVIndex(converter, face_index, corner);
+ dst_face_uvs[corner] = uv_index;
+ }
+ }
+ converter->finishUVLayer(converter);
+ }
+ return true;
+}
+
+template <>
+inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology(
+ TopologyError /*errCode*/, const char* msg,
+ const TopologyRefinerData& /*mesh*/) {
+ printf("OpenSubdiv Error: %s\n", msg);
+}
+
+} /* namespace 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..2f7a7109ea1
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_converter_internal.cc
@@ -0,0 +1,87 @@
+// 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_CORNERS_PLUS1:
+ return Options::FVAR_LINEAR_CORNERS_PLUS1;
+ case OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_PLUS2:
+ return Options::FVAR_LINEAR_CORNERS_PLUS2;
+ 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;
+}
+
+OpenSubdiv_FVarLinearInterpolation
+getCAPIFVarLinearInterpolationFromOSD(
+ OpenSubdiv::Sdc::Options::FVarLinearInterpolation linear_interpolation) {
+ typedef OpenSubdiv::Sdc::Options Options;
+ switch (linear_interpolation) {
+ case Options::FVAR_LINEAR_NONE:
+ return OSD_FVAR_LINEAR_INTERPOLATION_NONE;
+ case Options::FVAR_LINEAR_CORNERS_ONLY:
+ return OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_ONLY;
+ case Options::FVAR_LINEAR_CORNERS_PLUS1:
+ return OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_PLUS1;
+ case Options::FVAR_LINEAR_CORNERS_PLUS2:
+ return OSD_FVAR_LINEAR_INTERPOLATION_CORNERS_PLUS2;
+ case Options::FVAR_LINEAR_BOUNDARIES:
+ return OSD_FVAR_LINEAR_INTERPOLATION_BOUNDARIES;
+ case Options::FVAR_LINEAR_ALL:
+ return OSD_FVAR_LINEAR_INTERPOLATION_ALL;
+ }
+ assert(!"Unknown fvar linear interpolation passed via C-API");
+ return OSD_FVAR_LINEAR_INTERPOLATION_NONE;
+}
+
+} // 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..411514c45dc
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_converter_internal.h
@@ -0,0 +1,51 @@
+// 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);
+
+// Similar to above, just other way around.
+OpenSubdiv_FVarLinearInterpolation
+getCAPIFVarLinearInterpolationFromOSD(
+ OpenSubdiv::Sdc::Options::FVarLinearInterpolation linear_interpolation);
+
+} // 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..49b8626448b
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_evaluator.cc
@@ -0,0 +1,158 @@
+// 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 int face_varying_channel,
+ const float* face_varying_data,
+ const int start_vertex_index, const int num_vertices) {
+ evaluator->internal->eval_output->setFaceVaryingData(face_varying_channel,
+ 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 int face_varying_channel,
+ const void* buffer,
+ const int start_offset,
+ const int stride,
+ const int start_vertex_index,
+ const int num_vertices) {
+ evaluator->internal->eval_output->setFaceVaryingDataFromBuffer(
+ face_varying_channel,
+ 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 face_varying_channel,
+ const int ptex_face_index,
+ float face_u, float face_v,
+ float face_varying[2]) {
+ evaluator->internal->eval_output->evaluateFaceVarying(
+ face_varying_channel, 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..33df7a45e39
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc
@@ -0,0 +1,835 @@
+// 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 <opensubdiv/version.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "internal/opensubdiv_topology_refiner_internal.h"
+#include "internal/opensubdiv_util.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;
+
+// TODO(sergey): Remove after official requirement bump for OSD version.
+#if OPENSUBDIV_VERSION_NUMBER >= 30200
+# define OPENSUBDIV_HAS_FVAR_EVALUATION
+#else
+# undef OPENSUBDIV_HAS_FVAR_EVALUATION
+#endif
+
+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];
+};
+
+template <typename EVAL_VERTEX_BUFFER,
+ typename STENCIL_TABLE,
+ typename PATCH_TABLE,
+ typename EVALUATOR,
+ typename DEVICE_CONTEXT = void>
+class FaceVaryingVolatileEval {
+ public:
+ typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache;
+
+ FaceVaryingVolatileEval(int face_varying_channel,
+ const StencilTable* face_varying_stencils,
+ int face_varying_width,
+ PATCH_TABLE* patch_table,
+ EvaluatorCache* evaluator_cache = NULL,
+ DEVICE_CONTEXT* device_context = NULL)
+ : face_varying_channel_(face_varying_channel),
+ src_face_varying_desc_(0, face_varying_width, face_varying_width),
+ patch_table_(patch_table),
+ evaluator_cache_(evaluator_cache),
+ device_context_(device_context) {
+ using OpenSubdiv::Osd::convertToCompatibleStencilTable;
+ 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_);
+ }
+
+ ~FaceVaryingVolatileEval() {
+ delete src_face_varying_data_;
+ delete face_varying_stencils_;
+ }
+
+ void updateData(const float* src, int start_vertex, int num_vertices) {
+ src_face_varying_data_->UpdateData(src,
+ start_vertex,
+ num_vertices,
+ device_context_);
+ }
+
+ void refine() {
+ BufferDescriptor dst_face_varying_desc = src_face_varying_desc_;
+ dst_face_varying_desc.offset += num_coarse_face_varying_vertices_ *
+ src_face_varying_desc_.stride;
+ const EVALUATOR* 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 evalPatch(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);
+ }
+
+ protected:
+ int face_varying_channel_;
+
+ BufferDescriptor src_face_varying_desc_;
+
+ int num_coarse_face_varying_vertices_;
+ EVAL_VERTEX_BUFFER* src_face_varying_data_;
+ const STENCIL_TABLE* face_varying_stencils_;
+
+ // NOTE: We reference this, do not own it.
+ PATCH_TABLE* patch_table_;
+
+ EvaluatorCache* evaluator_cache_;
+ DEVICE_CONTEXT* device_context_;
+};
+
+// 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;
+ typedef FaceVaryingVolatileEval<EVAL_VERTEX_BUFFER,
+ STENCIL_TABLE,
+ PATCH_TABLE,
+ EVALUATOR,
+ DEVICE_CONTEXT> FaceVaryingEval;
+
+ VolatileEvalOutput(
+ const StencilTable* vertex_stencils,
+ const StencilTable* varying_stencils,
+ const vector<const StencilTable*>& all_face_varying_stencils,
+ 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),
+ 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_);
+ // Create evaluators for every face varying channel.
+ face_varying_evaluators.reserve(all_face_varying_stencils.size());
+ int face_varying_channel = 0;
+ foreach (const StencilTable* face_varying_stencils,
+ all_face_varying_stencils) {
+ face_varying_evaluators.push_back(
+ new FaceVaryingEval(face_varying_channel,
+ face_varying_stencils,
+ face_varying_width,
+ patch_table_,
+ evaluator_cache_,
+ device_context_));
+ ++face_varying_channel;
+ }
+ }
+
+ ~VolatileEvalOutput() {
+ delete src_data_;
+ delete src_varying_data_;
+ delete patch_table_;
+ delete vertex_stencils_;
+ delete varying_stencils_;
+ foreach (FaceVaryingEval* face_varying_evaluator, face_varying_evaluators) {
+ delete face_varying_evaluator;
+ }
+ }
+
+ // 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 int face_varying_channel,
+ const float* src,
+ int start_vertex,
+ int num_vertices) {
+ assert(face_varying_channel >= 0);
+ assert(face_varying_channel < face_varying_evaluators.size());
+ face_varying_evaluators[face_varying_channel]->updateData(
+ src, start_vertex, num_vertices);
+ }
+
+ bool hasVaryingData() const {
+ // return varying_stencils_ != NULL;
+ // TODO(sergey): Check this based on actual topology.
+ return false;
+ }
+
+ bool hasFaceVaryingData() const {
+ return face_varying_evaluators.size() != 0;
+ }
+
+ 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()) {
+ foreach (FaceVaryingEval* face_varying_evaluator,
+ face_varying_evaluators) {
+ face_varying_evaluator->refine();
+ }
+ }
+ }
+
+ 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 int face_varying_channel,
+ const PatchCoord& patch_coord,
+ float face_varying[2]) {
+ assert(face_varying_channel >= 0);
+ assert(face_varying_channel < face_varying_evaluators.size());
+ face_varying_evaluators[face_varying_channel]->evalPatch(
+ patch_coord, face_varying);
+ }
+
+ private:
+ SRC_VERTEX_BUFFER* src_data_;
+ SRC_VERTEX_BUFFER* src_varying_data_;
+ PatchCoordBuffer* patch_coords_;
+ PATCH_TABLE* patch_table_;
+ BufferDescriptor src_desc_;
+ BufferDescriptor src_varying_desc_;
+
+ int num_coarse_vertices_;
+
+ const STENCIL_TABLE* vertex_stencils_;
+ const STENCIL_TABLE* varying_stencils_;
+
+ int face_varying_width_;
+ vector<FaceVaryingEval*> face_varying_evaluators;
+
+ 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 vector<const StencilTable*>& all_face_varying_stencils,
+ const int face_varying_width,
+ const PatchTable* patch_table,
+ EvaluatorCache* evaluator_cache = NULL)
+ : VolatileEvalOutput<CpuVertexBuffer,
+ CpuVertexBuffer,
+ StencilTable,
+ CpuPatchTable,
+ CpuEvaluator> (vertex_stencils,
+ varying_stencils,
+ all_face_varying_stencils,
+ 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 int face_varying_channel,
+ 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_channel,
+ 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 int face_varying_channel,
+ 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(
+ face_varying_channel,
+ 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 face_varying_channel,
+ 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(
+ face_varying_channel, 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) {
+ using opensubdiv_capi::vector;
+ 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 int num_face_varying_channels = refiner->GetNumFVarChannels();
+ const bool has_face_varying_data = (num_face_varying_channels != 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 = true;
+ 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.
+vector<const StencilTable*> all_face_varying_stencils;
+#ifdef OPENSUBDIV_HAS_FVAR_EVALUATION
+ all_face_varying_stencils.reserve(num_face_varying_channels);
+ for (int face_varying_channel = 0;
+ face_varying_channel < num_face_varying_channels;
+ ++face_varying_channel) {
+ 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;
+ face_varying_stencil_options.fvarChannel = face_varying_channel;
+ all_face_varying_stencils.push_back(
+ StencilTableFactory::Create(*refiner, face_varying_stencil_options));
+ }
+#endif
+ // 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 = true;
+ 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;
+ }
+#ifdef OPENSUBDIV_HAS_FVAR_EVALUATION
+ for (int face_varying_channel = 0;
+ face_varying_channel < num_face_varying_channels;
+ ++face_varying_channel) {
+ const StencilTable* table =
+ StencilTableFactory::AppendLocalPointStencilTableFaceVarying(
+ *refiner,
+ all_face_varying_stencils[face_varying_channel],
+ patch_table->GetLocalPointFaceVaryingStencilTable(
+ face_varying_channel),
+ face_varying_channel);
+ if (table != NULL) {
+ delete all_face_varying_stencils[face_varying_channel];
+ all_face_varying_stencils[face_varying_channel] = table;
+ }
+ }
+#endif
+ // 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,
+ all_face_varying_stencils,
+ 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;
+ foreach (const StencilTable* table, all_face_varying_stencils) {
+ delete table;
+ }
+ 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..7d9178f38dc
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h
@@ -0,0 +1,137 @@
+// 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 int face_varying_channel,
+ 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 int face_varying_channel,
+ 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 face_varying_channel,
+ 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_internal.h b/intern/opensubdiv/internal/opensubdiv_internal.h
new file mode 100644
index 00000000000..16365896edf
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_internal.h
@@ -0,0 +1,35 @@
+// 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
+# 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..5e1e0bd86b3
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_topology_refiner.cc
@@ -0,0 +1,367 @@
+// 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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Query basic topology information from base level.
+
+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();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PTex face geometry queries.
+
+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);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Face-varying data.
+
+int getNumFVarChannels(
+ const struct OpenSubdiv_TopologyRefiner* topology_refiner) {
+ const OpenSubdiv::Far::TopologyLevel* base_level =
+ getOSDTopologyBaseLevel(topology_refiner);
+ return base_level->GetNumFVarChannels();
+}
+
+OpenSubdiv_FVarLinearInterpolation getFVarLinearInterpolation(
+ const struct OpenSubdiv_TopologyRefiner* topology_refiner) {
+ return opensubdiv_capi::getCAPIFVarLinearInterpolationFromOSD(
+ getOSDTopologyRefiner(topology_refiner)->GetFVarLinearInterpolation());
+}
+
+int getNumFVarValues(
+ const struct OpenSubdiv_TopologyRefiner* topology_refiner,
+ const int channel) {
+ const OpenSubdiv::Far::TopologyLevel* base_level =
+ getOSDTopologyBaseLevel(topology_refiner);
+ return base_level->GetNumFVarValues(channel);
+}
+
+const int* getFaceFVarValueIndices(
+ const struct OpenSubdiv_TopologyRefiner* topology_refiner,
+ const int face_index,
+ const int channel) {
+ const OpenSubdiv::Far::TopologyLevel* base_level =
+ getOSDTopologyBaseLevel(topology_refiner);
+ return &base_level->GetFaceFVarValues(face_index, channel)[0];
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Internal helpers.
+
+void assignFunctionPointers(OpenSubdiv_TopologyRefiner* topology_refiner) {
+ topology_refiner->getSubdivisionLevel = getSubdivisionLevel;
+ topology_refiner->getIsAdaptive = getIsAdaptive;
+ // Basic topology information.
+ topology_refiner->getNumVertices = getNumVertices;
+ topology_refiner->getNumEdges = getNumEdges;
+ topology_refiner->getNumFaces = getNumFaces;
+ topology_refiner->getNumFaceVertices = getNumFaceVertices;
+ // PTex face geometry.
+ topology_refiner->getNumFacePtexFaces = getNumFacePtexFaces;
+ topology_refiner->getNumPtexFaces = getNumPtexFaces;
+ topology_refiner->fillFacePtexIndexOffset = fillFacePtexIndexOffset;
+ // Face-varying data.
+ topology_refiner->getNumFVarChannels = getNumFVarChannels;
+ topology_refiner->getFVarLinearInterpolation = getFVarLinearInterpolation;
+ topology_refiner->getNumFVarValues = getNumFVarValues;
+ topology_refiner->getFaceFVarValueIndices = getFaceFVarValueIndices;
+}
+
+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::Far::TopologyRefiner* osd_topology_refiner =
+ opensubdiv_capi::createOSDTopologyRefinerFromConverter(converter);
+ if (osd_topology_refiner == NULL) {
+ // Happens on empty or bad topology.
+ return NULL;
+ }
+ OpenSubdiv_TopologyRefiner* topology_refiner = allocateTopologyRefiner();
+ topology_refiner->internal->osd_topology_refiner = osd_topology_refiner;
+ // 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 =
+ converter->getEdgeSharpness(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_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..db55504215d
--- /dev/null
+++ b/intern/opensubdiv/internal/opensubdiv_util.h
@@ -0,0 +1,44 @@
+// 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 {
+
+using std::string;
+using std::vector;
+
+#define foreach(x, y) for(x : y)
+
+#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_