Merge branch 'master' into blender2.8
[blender.git] / intern / opensubdiv / opensubdiv_capi.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2013 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Sergey Sharybin.
22  *                 Brecht van Lommel
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 #include "opensubdiv_capi.h"
28
29 #ifdef _MSC_VER
30 #  include "iso646.h"
31 #endif
32
33 #include <stdlib.h>
34 #include <GL/glew.h>
35
36 #include <opensubdiv/version.h>
37 #include <opensubdiv/osd/glMesh.h>
38
39 /* CPU Backend */
40 #include <opensubdiv/osd/cpuGLVertexBuffer.h>
41 #include <opensubdiv/osd/cpuEvaluator.h>
42
43 #ifdef OPENSUBDIV_HAS_OPENMP
44 #  include <opensubdiv/osd/ompEvaluator.h>
45 #endif  /* OPENSUBDIV_HAS_OPENMP */
46
47 #ifdef OPENSUBDIV_HAS_OPENCL
48 #  include <opensubdiv/osd/clGLVertexBuffer.h>
49 #  include <opensubdiv/osd/clEvaluator.h>
50 #  include "opensubdiv_device_context_opencl.h"
51 #endif  /* OPENSUBDIV_HAS_OPENCL */
52
53 #ifdef OPENSUBDIV_HAS_CUDA
54 #  include <opensubdiv/osd/cudaGLVertexBuffer.h>
55 #  include <opensubdiv/osd/cudaEvaluator.h>
56 #  include "opensubdiv_device_context_cuda.h"
57 #endif  /* OPENSUBDIV_HAS_CUDA */
58
59 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
60 #  include <opensubdiv/osd/glXFBEvaluator.h>
61 #  include <opensubdiv/osd/glVertexBuffer.h>
62 #endif  /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
63
64 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
65 #  include <opensubdiv/osd/glComputeEvaluator.h>
66 #  include <opensubdiv/osd/glVertexBuffer.h>
67 #endif  /* OPENSUBDIV_HAS_GLSL_COMPUTE */
68
69 #include <opensubdiv/osd/glPatchTable.h>
70 #include <opensubdiv/far/stencilTable.h>
71 #include <opensubdiv/far/primvarRefiner.h>
72
73 #include "opensubdiv_intern.h"
74 #include "opensubdiv_topology_refiner.h"
75
76 #include "MEM_guardedalloc.h"
77
78 #include <string>
79 #include <vector>
80
81 using std::string;
82 using std::vector;
83
84 #define STRINGIFY_ARG(x) "" #x
85 #define STRINGIFY_APPEND(a, b) "" a #b
86 #define STRINGIFY(x) STRINGIFY_APPEND("", x)
87
88 /* **************** Types declaration **************** */
89
90 using OpenSubdiv::Osd::GLMeshInterface;
91 using OpenSubdiv::Osd::Mesh;
92 using OpenSubdiv::Osd::MeshBitset;
93 using OpenSubdiv::Far::StencilTable;
94 using OpenSubdiv::Osd::GLPatchTable;
95
96 using OpenSubdiv::Osd::Mesh;
97
98 /* CPU backend */
99 using OpenSubdiv::Osd::CpuGLVertexBuffer;
100 using OpenSubdiv::Osd::CpuEvaluator;
101 typedef Mesh<CpuGLVertexBuffer,
102              StencilTable,
103              CpuEvaluator,
104              GLPatchTable> OsdCpuMesh;
105
106 #ifdef OPENSUBDIV_HAS_OPENMP
107 using OpenSubdiv::Osd::OmpEvaluator;
108 typedef Mesh<CpuGLVertexBuffer,
109              StencilTable,
110              OmpEvaluator,
111              GLPatchTable> OsdOmpMesh;
112 #endif  /* OPENSUBDIV_HAS_OPENMP */
113
114 #ifdef OPENSUBDIV_HAS_OPENCL
115 using OpenSubdiv::Osd::CLEvaluator;
116 using OpenSubdiv::Osd::CLGLVertexBuffer;
117 using OpenSubdiv::Osd::CLStencilTable;
118 /* TODO(sergey): Use CLDeviceCOntext similar to OSD examples? */
119 typedef Mesh<CLGLVertexBuffer,
120              CLStencilTable,
121              CLEvaluator,
122              GLPatchTable,
123              CLDeviceContext> OsdCLMesh;
124 static CLDeviceContext g_clDeviceContext;
125 #endif  /* OPENSUBDIV_HAS_OPENCL */
126
127 #ifdef OPENSUBDIV_HAS_CUDA
128 using OpenSubdiv::Osd::CudaEvaluator;
129 using OpenSubdiv::Osd::CudaGLVertexBuffer;
130 using OpenSubdiv::Osd::CudaStencilTable;
131 typedef Mesh<CudaGLVertexBuffer,
132              CudaStencilTable,
133              CudaEvaluator,
134              GLPatchTable> OsdCudaMesh;
135 static CudaDeviceContext g_cudaDeviceContext;
136 #endif  /* OPENSUBDIV_HAS_CUDA */
137
138 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
139 using OpenSubdiv::Osd::GLXFBEvaluator;
140 using OpenSubdiv::Osd::GLStencilTableTBO;
141 using OpenSubdiv::Osd::GLVertexBuffer;
142 typedef Mesh<GLVertexBuffer,
143              GLStencilTableTBO,
144              GLXFBEvaluator,
145              GLPatchTable> OsdGLSLTransformFeedbackMesh;
146 #endif  /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
147
148 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
149 using OpenSubdiv::Osd::GLComputeEvaluator;
150 using OpenSubdiv::Osd::GLStencilTableSSBO;
151 using OpenSubdiv::Osd::GLVertexBuffer;
152 typedef Mesh<GLVertexBuffer,
153              GLStencilTableSSBO,
154              GLComputeEvaluator,
155              GLPatchTable> OsdGLSLComputeMesh;
156 #endif
157
158 namespace {
159
160 #if !defined(OPENSUBDIV_VERSION_NUMBER) && !defined(OPENSUBDIV_VERSION_MINOR)
161 void stringSplit(vector<string>* tokens,
162                  const string& str,
163                  const string& separators,
164                  bool skip_empty) {
165         size_t token_start = 0, token_length = 0;
166         for (size_t i = 0; i < str.length(); ++i) {
167                 const char ch = str[i];
168                 if (separators.find(ch) == string::npos) {
169                         /* Append non-separator char to a token. */
170                         ++token_length;
171                 } else {
172                         /* Append current token to the list (if any). */
173                         if (token_length > 0 || !skip_empty) {
174                                 string token = str.substr(token_start, token_length);
175                                 tokens->push_back(token);
176                         }
177                         /* Re-set token pointers, */
178                         token_start = i + 1;
179                         token_length = 0;
180                 }
181         }
182         /* Append token which might be at the end of the string. */
183         if ((token_length != 0) ||
184             (!skip_empty && token_start > 0 &&
185              separators.find(str[token_start-1]) != string::npos)) {
186                 string token = str.substr(token_start, token_length);
187                 tokens->push_back(token);
188         }
189 }
190 #endif
191
192 struct FVarVertex {
193         float u, v;
194         void Clear() {
195                 u = v = 0.0f;
196         }
197         void AddWithWeight(FVarVertex const & src, float weight) {
198                 u += weight * src.u;
199                 v += weight * src.v;
200         }
201 };
202
203 static void interpolate_fvar_data(OpenSubdiv::Far::TopologyRefiner& refiner,
204                                   const std::vector<float> uvs,
205                                   std::vector<float> &fvar_data) {
206         /* TODO(sergey): Make it somehow more generic way. */
207         const int fvar_width = 2;
208         const int max_level = refiner.GetMaxLevel();
209         size_t fvar_data_offset = 0, values_offset = 0;
210         for (int channel = 0; channel < refiner.GetNumFVarChannels(); ++channel) {
211                 const int num_values = refiner.GetLevel(0).GetNumFVarValues(channel) * 2,
212                           num_values_max = refiner.GetLevel(max_level).GetNumFVarValues(channel),
213                           num_values_total = refiner.GetNumFVarValuesTotal(channel);
214                 if (num_values_total <= 0) {
215                         continue;
216                 }
217                 OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner);
218                 if (refiner.IsUniform()) {
219                         /* For uniform we only keep the highest level of refinement. */
220                         fvar_data.resize(fvar_data.size() + num_values_max * fvar_width);
221                         std::vector<FVarVertex> buffer(num_values_total - num_values_max);
222                         FVarVertex *src = &buffer[0];
223                         memcpy(src, &uvs[values_offset], num_values * sizeof(float));
224                         /* Defer the last level to treat separately with its alternate
225                          * destination.
226                          */
227                         for (int level = 1; level < max_level; ++level) {
228                                 FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
229                                 primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
230                                 src = dst;
231                         }
232                         FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
233                         primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel);
234                         fvar_data_offset += num_values_max * fvar_width;
235                 } else {
236                         /* For adaptive we keep all levels. */
237                         fvar_data.resize(fvar_data.size() + num_values_total * fvar_width);
238                         FVarVertex *src = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
239                         memcpy(src, &uvs[values_offset], num_values * sizeof(float));
240                         for (int level = 1; level <= max_level; ++level) {
241                                 FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
242                                 primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
243                                 src = dst;
244                         }
245                         fvar_data_offset += num_values_total * fvar_width;
246                 }
247                 values_offset += num_values;
248         }
249 }
250
251 }  // namespace
252
253 struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner(
254         OpenSubdiv_TopologyRefinerDescr *topology_refiner,
255         int evaluator_type,
256         int level)
257 {
258         using OpenSubdiv::Far::TopologyRefiner;
259
260         MeshBitset bits;
261         /* TODO(sergey): Adaptive subdivisions are not currently
262          * possible because of the lack of tessellation shader.
263          */
264         bits.set(OpenSubdiv::Osd::MeshAdaptive, 0);
265         bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, 0);
266         bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, 1);
267         bits.set(OpenSubdiv::Osd::MeshFVarData, 1);
268         bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1);
269
270         const int num_vertex_elements = 3;
271         const int num_varying_elements = 3;
272
273         GLMeshInterface *mesh = NULL;
274         TopologyRefiner *refiner = topology_refiner->osd_refiner;
275
276         switch(evaluator_type) {
277 #define CHECK_EVALUATOR_TYPE(type, class) \
278                 case OPENSUBDIV_EVALUATOR_ ## type: \
279                         mesh = new class(refiner, \
280                                          num_vertex_elements, \
281                                          num_varying_elements, \
282                                          level, \
283                                          bits); \
284                         break;
285
286                 CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
287
288 #ifdef OPENSUBDIV_HAS_OPENMP
289                 CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
290 #endif
291
292 #ifdef OPENSUBDIV_HAS_OPENCL
293                 CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
294 #endif
295
296 #ifdef OPENSUBDIV_HAS_CUDA
297                 CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
298 #endif
299
300 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
301                 CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
302                                      OsdGLSLTransformFeedbackMesh)
303 #endif
304
305 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
306                 CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
307 #endif
308
309 #undef CHECK_EVALUATOR_TYPE
310         }
311
312         if (mesh == NULL) {
313                 return NULL;
314         }
315
316         OpenSubdiv_GLMesh *gl_mesh =
317                 (OpenSubdiv_GLMesh *) OBJECT_GUARDED_NEW(OpenSubdiv_GLMesh);
318         gl_mesh->evaluator_type = evaluator_type;
319         gl_mesh->descriptor = (OpenSubdiv_GLMeshDescr *) mesh;
320         gl_mesh->topology_refiner = topology_refiner;
321
322         if (refiner->GetNumFVarChannels() > 0) {
323                 std::vector<float> fvar_data;
324                 interpolate_fvar_data(*refiner, topology_refiner->uvs, fvar_data);
325                 openSubdiv_osdGLAllocFVar(topology_refiner, gl_mesh, &fvar_data[0]);
326         }
327         else {
328                 gl_mesh->fvar_data = NULL;
329         }
330
331         return gl_mesh;
332 }
333
334 void openSubdiv_deleteOsdGLMesh(struct OpenSubdiv_GLMesh *gl_mesh)
335 {
336         openSubdiv_osdGLDestroyFVar(gl_mesh);
337         switch (gl_mesh->evaluator_type) {
338 #define CHECK_EVALUATOR_TYPE(type, class) \
339                 case OPENSUBDIV_EVALUATOR_ ## type: \
340                         delete (class *) gl_mesh->descriptor; \
341                         break;
342
343                 CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
344
345 #ifdef OPENSUBDIV_HAS_OPENMP
346                 CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
347 #endif
348
349 #ifdef OPENSUBDIV_HAS_OPENCL
350                 CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
351 #endif
352
353 #ifdef OPENSUBDIV_HAS_CUDA
354                 CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
355 #endif
356
357 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
358                 CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
359                                      OsdGLSLTransformFeedbackMesh)
360 #endif
361
362 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
363                 CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
364 #endif
365
366 #undef CHECK_EVALUATOR_TYPE
367         }
368
369         /* NOTE: OSD refiner was owned by gl_mesh, no need to free it here. */
370         OBJECT_GUARDED_DELETE(gl_mesh->topology_refiner, OpenSubdiv_TopologyRefinerDescr);
371         OBJECT_GUARDED_DELETE(gl_mesh, OpenSubdiv_GLMesh);
372 }
373
374 unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
375 {
376         return ((GLMeshInterface *)gl_mesh->descriptor)->GetPatchTable()->GetPatchIndexBuffer();
377 }
378
379 unsigned int openSubdiv_getOsdGLMeshVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
380 {
381         return ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
382 }
383
384 void openSubdiv_osdGLMeshUpdateVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh,
385                                             const float *vertex_data,
386                                             int start_vertex,
387                                             int num_verts)
388 {
389         ((GLMeshInterface *)gl_mesh->descriptor)->UpdateVertexBuffer(vertex_data,
390                                                                      start_vertex,
391                                                                      num_verts);
392 }
393
394 void openSubdiv_osdGLMeshRefine(struct OpenSubdiv_GLMesh *gl_mesh)
395 {
396         ((GLMeshInterface *)gl_mesh->descriptor)->Refine();
397 }
398
399 void openSubdiv_osdGLMeshSynchronize(struct OpenSubdiv_GLMesh *gl_mesh)
400 {
401         ((GLMeshInterface *)gl_mesh->descriptor)->Synchronize();
402 }
403
404 void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh)
405 {
406         ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
407 }
408
409 const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner(
410         OpenSubdiv_GLMesh *gl_mesh)
411 {
412         return gl_mesh->topology_refiner;
413 }
414
415 int openSubdiv_getVersionHex(void)
416 {
417 #if defined(OPENSUBDIV_VERSION_NUMBER)
418         return OPENSUBDIV_VERSION_NUMBER;
419 #elif defined(OPENSUBDIV_VERSION_MAJOR)
420         return OPENSUBDIV_VERSION_MAJOR * 10000 +
421                OPENSUBDIV_VERSION_MINOR * 100 +
422                OPENSUBDIV_VERSION_PATCH;
423 #elif defined(OPENSUBDIV_VERSION)
424         const char* version = STRINGIFY(OPENSUBDIV_VERSION);
425         if (version[0] == 'v') {
426                 version += 1;
427         }
428         int major = 0, minor = 0, patch = 0;
429         vector<string> tokens;
430         stringSplit(&tokens, version, "_", true);
431         if (tokens.size() == 3) {
432                 major = atoi(tokens[0].c_str());
433                 minor = atoi(tokens[1].c_str());
434                 patch = atoi(tokens[2].c_str());
435         }
436         return major * 10000 + minor * 100 + patch;
437 #else
438         return 0;
439 #endif
440 }