2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * The Original Code is Copyright (C) 2013 Blender Foundation.
17 * All rights reserved.
20 #include "opensubdiv_capi.h"
29 #include <opensubdiv/version.h>
30 #include <opensubdiv/osd/glMesh.h>
33 #include <opensubdiv/osd/cpuGLVertexBuffer.h>
34 #include <opensubdiv/osd/cpuEvaluator.h>
36 #ifdef OPENSUBDIV_HAS_OPENMP
37 # include <opensubdiv/osd/ompEvaluator.h>
38 #endif /* OPENSUBDIV_HAS_OPENMP */
40 #ifdef OPENSUBDIV_HAS_OPENCL
41 # include <opensubdiv/osd/clGLVertexBuffer.h>
42 # include <opensubdiv/osd/clEvaluator.h>
43 # include "opensubdiv_device_context_opencl.h"
44 #endif /* OPENSUBDIV_HAS_OPENCL */
46 #ifdef OPENSUBDIV_HAS_CUDA
47 # include <opensubdiv/osd/cudaGLVertexBuffer.h>
48 # include <opensubdiv/osd/cudaEvaluator.h>
49 # include "opensubdiv_device_context_cuda.h"
50 #endif /* OPENSUBDIV_HAS_CUDA */
52 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
53 # include <opensubdiv/osd/glXFBEvaluator.h>
54 # include <opensubdiv/osd/glVertexBuffer.h>
55 #endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
57 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
58 # include <opensubdiv/osd/glComputeEvaluator.h>
59 # include <opensubdiv/osd/glVertexBuffer.h>
60 #endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */
62 #include <opensubdiv/osd/glPatchTable.h>
63 #include <opensubdiv/far/stencilTable.h>
64 #include <opensubdiv/far/primvarRefiner.h>
66 #include "opensubdiv_intern.h"
67 #include "opensubdiv_topology_refiner.h"
69 #include "MEM_guardedalloc.h"
77 #define STRINGIFY_ARG(x) "" #x
78 #define STRINGIFY_APPEND(a, b) "" a #b
79 #define STRINGIFY(x) STRINGIFY_APPEND("", x)
81 /* **************** Types declaration **************** */
83 using OpenSubdiv::Osd::GLMeshInterface;
84 using OpenSubdiv::Osd::Mesh;
85 using OpenSubdiv::Osd::MeshBitset;
86 using OpenSubdiv::Far::StencilTable;
87 using OpenSubdiv::Osd::GLPatchTable;
89 using OpenSubdiv::Osd::Mesh;
92 using OpenSubdiv::Osd::CpuGLVertexBuffer;
93 using OpenSubdiv::Osd::CpuEvaluator;
94 typedef Mesh<CpuGLVertexBuffer,
97 GLPatchTable> OsdCpuMesh;
99 #ifdef OPENSUBDIV_HAS_OPENMP
100 using OpenSubdiv::Osd::OmpEvaluator;
101 typedef Mesh<CpuGLVertexBuffer,
104 GLPatchTable> OsdOmpMesh;
105 #endif /* OPENSUBDIV_HAS_OPENMP */
107 #ifdef OPENSUBDIV_HAS_OPENCL
108 using OpenSubdiv::Osd::CLEvaluator;
109 using OpenSubdiv::Osd::CLGLVertexBuffer;
110 using OpenSubdiv::Osd::CLStencilTable;
111 /* TODO(sergey): Use CLDeviceCOntext similar to OSD examples? */
112 typedef Mesh<CLGLVertexBuffer,
116 CLDeviceContext> OsdCLMesh;
117 static CLDeviceContext g_clDeviceContext;
118 #endif /* OPENSUBDIV_HAS_OPENCL */
120 #ifdef OPENSUBDIV_HAS_CUDA
121 using OpenSubdiv::Osd::CudaEvaluator;
122 using OpenSubdiv::Osd::CudaGLVertexBuffer;
123 using OpenSubdiv::Osd::CudaStencilTable;
124 typedef Mesh<CudaGLVertexBuffer,
127 GLPatchTable> OsdCudaMesh;
128 static CudaDeviceContext g_cudaDeviceContext;
129 #endif /* OPENSUBDIV_HAS_CUDA */
131 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
132 using OpenSubdiv::Osd::GLXFBEvaluator;
133 using OpenSubdiv::Osd::GLStencilTableTBO;
134 using OpenSubdiv::Osd::GLVertexBuffer;
135 typedef Mesh<GLVertexBuffer,
138 GLPatchTable> OsdGLSLTransformFeedbackMesh;
139 #endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
141 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
142 using OpenSubdiv::Osd::GLComputeEvaluator;
143 using OpenSubdiv::Osd::GLStencilTableSSBO;
144 using OpenSubdiv::Osd::GLVertexBuffer;
145 typedef Mesh<GLVertexBuffer,
148 GLPatchTable> OsdGLSLComputeMesh;
153 #if !defined(OPENSUBDIV_VERSION_NUMBER) && !defined(OPENSUBDIV_VERSION_MINOR)
154 void stringSplit(vector<string>* tokens,
156 const string& separators,
158 size_t token_start = 0, token_length = 0;
159 for (size_t i = 0; i < str.length(); ++i) {
160 const char ch = str[i];
161 if (separators.find(ch) == string::npos) {
162 /* Append non-separator char to a token. */
165 /* Append current token to the list (if any). */
166 if (token_length > 0 || !skip_empty) {
167 string token = str.substr(token_start, token_length);
168 tokens->push_back(token);
170 /* Re-set token pointers, */
175 /* Append token which might be at the end of the string. */
176 if ((token_length != 0) ||
177 (!skip_empty && token_start > 0 &&
178 separators.find(str[token_start-1]) != string::npos)) {
179 string token = str.substr(token_start, token_length);
180 tokens->push_back(token);
190 void AddWithWeight(FVarVertex const & src, float weight) {
196 static void interpolate_fvar_data(OpenSubdiv::Far::TopologyRefiner& refiner,
197 const std::vector<float> uvs,
198 std::vector<float> &fvar_data) {
199 /* TODO(sergey): Make it somehow more generic way. */
200 const int fvar_width = 2;
201 const int max_level = refiner.GetMaxLevel();
202 size_t fvar_data_offset = 0, values_offset = 0;
203 for (int channel = 0; channel < refiner.GetNumFVarChannels(); ++channel) {
204 const int num_values = refiner.GetLevel(0).GetNumFVarValues(channel) * 2,
205 num_values_max = refiner.GetLevel(max_level).GetNumFVarValues(channel),
206 num_values_total = refiner.GetNumFVarValuesTotal(channel);
207 if (num_values_total <= 0) {
210 OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner);
211 if (refiner.IsUniform()) {
212 /* For uniform we only keep the highest level of refinement. */
213 fvar_data.resize(fvar_data.size() + num_values_max * fvar_width);
214 std::vector<FVarVertex> buffer(num_values_total - num_values_max);
215 FVarVertex *src = &buffer[0];
216 memcpy(src, &uvs[values_offset], num_values * sizeof(float));
217 /* Defer the last level to treat separately with its alternate
220 for (int level = 1; level < max_level; ++level) {
221 FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
222 primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
225 FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
226 primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel);
227 fvar_data_offset += num_values_max * fvar_width;
229 /* For adaptive we keep all levels. */
230 fvar_data.resize(fvar_data.size() + num_values_total * fvar_width);
231 FVarVertex *src = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
232 memcpy(src, &uvs[values_offset], num_values * sizeof(float));
233 for (int level = 1; level <= max_level; ++level) {
234 FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
235 primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
238 fvar_data_offset += num_values_total * fvar_width;
240 values_offset += num_values;
246 struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner(
247 OpenSubdiv_TopologyRefinerDescr *topology_refiner,
251 using OpenSubdiv::Far::TopologyRefiner;
254 /* TODO(sergey): Adaptive subdivisions are not currently
255 * possible because of the lack of tessellation shader.
257 bits.set(OpenSubdiv::Osd::MeshAdaptive, 0);
258 bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, 0);
259 bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, 1);
260 bits.set(OpenSubdiv::Osd::MeshFVarData, 1);
261 bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1);
263 const int num_vertex_elements = 3;
264 const int num_varying_elements = 3;
266 GLMeshInterface *mesh = NULL;
267 TopologyRefiner *refiner = topology_refiner->osd_refiner;
269 switch(evaluator_type) {
270 #define CHECK_EVALUATOR_TYPE(type, class) \
271 case OPENSUBDIV_EVALUATOR_ ## type: \
272 mesh = new class(refiner, \
273 num_vertex_elements, \
274 num_varying_elements, \
279 CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
281 #ifdef OPENSUBDIV_HAS_OPENMP
282 CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
285 #ifdef OPENSUBDIV_HAS_OPENCL
286 CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
289 #ifdef OPENSUBDIV_HAS_CUDA
290 CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
293 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
294 CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
295 OsdGLSLTransformFeedbackMesh)
298 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
299 CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
302 #undef CHECK_EVALUATOR_TYPE
309 OpenSubdiv_GLMesh *gl_mesh =
310 (OpenSubdiv_GLMesh *) OBJECT_GUARDED_NEW(OpenSubdiv_GLMesh);
311 gl_mesh->evaluator_type = evaluator_type;
312 gl_mesh->descriptor = (OpenSubdiv_GLMeshDescr *) mesh;
313 gl_mesh->topology_refiner = topology_refiner;
315 if (refiner->GetNumFVarChannels() > 0) {
316 std::vector<float> fvar_data;
317 interpolate_fvar_data(*refiner, topology_refiner->uvs, fvar_data);
318 openSubdiv_osdGLAllocFVar(topology_refiner, gl_mesh, &fvar_data[0]);
321 gl_mesh->fvar_data = NULL;
327 void openSubdiv_deleteOsdGLMesh(struct OpenSubdiv_GLMesh *gl_mesh)
329 openSubdiv_osdGLDestroyFVar(gl_mesh);
330 switch (gl_mesh->evaluator_type) {
331 #define CHECK_EVALUATOR_TYPE(type, class) \
332 case OPENSUBDIV_EVALUATOR_ ## type: \
333 delete (class *) gl_mesh->descriptor; \
336 CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
338 #ifdef OPENSUBDIV_HAS_OPENMP
339 CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
342 #ifdef OPENSUBDIV_HAS_OPENCL
343 CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
346 #ifdef OPENSUBDIV_HAS_CUDA
347 CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
350 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
351 CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
352 OsdGLSLTransformFeedbackMesh)
355 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
356 CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
359 #undef CHECK_EVALUATOR_TYPE
362 /* NOTE: OSD refiner was owned by gl_mesh, no need to free it here. */
363 OBJECT_GUARDED_DELETE(gl_mesh->topology_refiner, OpenSubdiv_TopologyRefinerDescr);
364 OBJECT_GUARDED_DELETE(gl_mesh, OpenSubdiv_GLMesh);
367 unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
369 return ((GLMeshInterface *)gl_mesh->descriptor)->GetPatchTable()->GetPatchIndexBuffer();
372 unsigned int openSubdiv_getOsdGLMeshVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
374 return ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
377 void openSubdiv_osdGLMeshUpdateVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh,
378 const float *vertex_data,
382 ((GLMeshInterface *)gl_mesh->descriptor)->UpdateVertexBuffer(vertex_data,
387 void openSubdiv_osdGLMeshRefine(struct OpenSubdiv_GLMesh *gl_mesh)
389 ((GLMeshInterface *)gl_mesh->descriptor)->Refine();
392 void openSubdiv_osdGLMeshSynchronize(struct OpenSubdiv_GLMesh *gl_mesh)
394 ((GLMeshInterface *)gl_mesh->descriptor)->Synchronize();
397 void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh)
399 ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
402 const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner(
403 OpenSubdiv_GLMesh *gl_mesh)
405 return gl_mesh->topology_refiner;
408 int openSubdiv_supportGPUDisplay(void)
410 // TODO: simplify extension check once Blender adopts GL 3.2
411 return openSubdiv_gpu_legacy_support() &&
413 (GLEW_VERSION_3_1 && GLEW_EXT_geometry_shader4) ||
415 GLEW_EXT_geometry_shader4 &&
416 GLEW_ARB_uniform_buffer_object &&
417 (GLEW_ARB_texture_buffer_object || GLEW_EXT_texture_buffer_object)));
418 /* also ARB_explicit_attrib_location? */
421 int openSubdiv_getVersionHex(void)
423 #if defined(OPENSUBDIV_VERSION_NUMBER)
424 return OPENSUBDIV_VERSION_NUMBER;
425 #elif defined(OPENSUBDIV_VERSION_MAJOR)
426 return OPENSUBDIV_VERSION_MAJOR * 10000 +
427 OPENSUBDIV_VERSION_MINOR * 100 +
428 OPENSUBDIV_VERSION_PATCH;
429 #elif defined(OPENSUBDIV_VERSION)
430 const char* version = STRINGIFY(OPENSUBDIV_VERSION);
431 if (version[0] == 'v') {
434 int major = 0, minor = 0, patch = 0;
435 vector<string> tokens;
436 stringSplit(&tokens, version, "_", true);
437 if (tokens.size() == 3) {
438 major = atoi(tokens[0].c_str());
439 minor = atoi(tokens[1].c_str());
440 patch = atoi(tokens[2].c_str());
442 return major * 10000 + minor * 100 + patch;