diff options
Diffstat (limited to 'intern/opensubdiv/internal')
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_ |