Cleanup: fix compiler warnings.
[blender.git] / intern / opensubdiv / opensubdiv_gpu_capi.cc
1 /*
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.
6  *
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.
11  *
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.
15  *
16  * The Original Code is Copyright (C) 2013 Blender Foundation.
17  * All rights reserved.
18  */
19
20 #include "opensubdiv_capi.h"
21
22 #ifdef _MSC_VER
23 #  include "iso646.h"
24 #endif
25
26 #include <cstdio>
27 #include <cmath>
28 #include <GL/glew.h>
29
30 #include <opensubdiv/osd/glMesh.h>
31
32 #ifdef OPENSUBDIV_HAS_CUDA
33 #  include <opensubdiv/osd/cudaGLVertexBuffer.h>
34 #endif  /* OPENSUBDIV_HAS_CUDA */
35
36 #include <opensubdiv/osd/cpuGLVertexBuffer.h>
37 #include <opensubdiv/osd/cpuEvaluator.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "opensubdiv_capi.h"
42 #include "opensubdiv_topology_refiner.h"
43
44 using OpenSubdiv::Osd::GLMeshInterface;
45
46 extern "C" char datatoc_gpu_shader_opensubdiv_vertex_glsl[];
47 extern "C" char datatoc_gpu_shader_opensubdiv_geometry_glsl[];
48 extern "C" char datatoc_gpu_shader_opensubdiv_fragment_glsl[];
49
50 /* TODO(sergey): This is bit of bad level calls :S */
51 extern "C" {
52 void copy_m3_m3(float m1[3][3], float m2[3][3]);
53 void copy_m3_m4(float m1[3][3], float m2[4][4]);
54 void adjoint_m3_m3(float m1[3][3], float m[3][3]);
55 float determinant_m3_array(float m[3][3]);
56 bool invert_m3_m3(float m1[3][3], float m2[3][3]);
57 bool invert_m3(float m[3][3]);
58 void transpose_m3(float mat[3][3]);
59 }
60
61 #define MAX_LIGHTS 8
62 #define SUPPORT_COLOR_MATERIAL
63
64 typedef struct Light {
65         float position[4];
66         float ambient[4];
67         float diffuse[4];
68         float specular[4];
69         float spot_direction[4];
70 #ifdef SUPPORT_COLOR_MATERIAL
71         float constant_attenuation;
72         float linear_attenuation;
73         float quadratic_attenuation;
74         float spot_cutoff;
75         float spot_exponent;
76         float spot_cos_cutoff;
77         float pad, pad2;
78 #endif
79 } Light;
80
81 typedef struct Lighting {
82         Light lights[MAX_LIGHTS];
83         int num_enabled;
84 } Lighting;
85
86 typedef struct Transform {
87         float projection_matrix[16];
88         float model_view_matrix[16];
89         float normal_matrix[9];
90 } Transform;
91
92 static bool g_use_osd_glsl = false;
93 static int g_active_uv_index = 0;
94
95 static GLuint g_flat_fill_solid_program = 0;
96 static GLuint g_flat_fill_texture2d_program = 0;
97 static GLuint g_smooth_fill_solid_program = 0;
98 static GLuint g_smooth_fill_texture2d_program = 0;
99
100 static GLuint g_flat_fill_solid_shadeless_program = 0;
101 static GLuint g_flat_fill_texture2d_shadeless_program = 0;
102 static GLuint g_smooth_fill_solid_shadeless_program = 0;
103 static GLuint g_smooth_fill_texture2d_shadeless_program = 0;
104
105 static GLuint g_wireframe_program = 0;
106
107 static GLuint g_lighting_ub = 0;
108 static Lighting g_lighting_data;
109 static Transform g_transform;
110
111 struct OpenSubdiv_GLMeshFVarData
112 {
113         OpenSubdiv_GLMeshFVarData() :
114                 texture_buffer(0), offset_buffer(0) {
115         }
116
117         ~OpenSubdiv_GLMeshFVarData()
118         {
119                 Release();
120         }
121
122         void Release()
123         {
124                 if (texture_buffer) {
125                         glDeleteTextures(1, &texture_buffer);
126                 }
127                 if (offset_buffer) {
128                         glDeleteTextures(1, &offset_buffer);
129                 }
130                 texture_buffer = 0;
131                 offset_buffer = 0;
132                 fvar_width = 0;
133                 channel_offsets.clear();
134         }
135
136         void Create(const OpenSubdiv::Far::TopologyRefiner *refiner,
137                     const OpenSubdiv::Far::PatchTable *patch_table,
138                     int fvar_width,
139                     const float *fvar_src_data)
140         {
141                 Release();
142
143                 this->fvar_width = fvar_width;
144
145                 /* Expand fvar data to per-patch array */
146                 const int max_level = refiner->GetMaxLevel();
147                 const int num_channels = patch_table->GetNumFVarChannels();
148                 std::vector<float> data;
149                 int fvar_data_offset = 0;
150                 channel_offsets.resize(num_channels);
151                 for (int channel = 0; channel < num_channels; ++channel) {
152                         OpenSubdiv::Far::ConstIndexArray indices =
153                                 patch_table->GetFVarValues(channel);
154
155                         channel_offsets[channel] = data.size();
156                         data.reserve(data.size() + indices.size() * fvar_width);
157
158                         for (int fvert = 0; fvert < (int)indices.size(); ++fvert) {
159                                 int index = indices[fvert] * fvar_width;
160                                 for (int i = 0; i < fvar_width; ++i) {
161                                         data.push_back(fvar_src_data[fvar_data_offset + index++]);
162                                 }
163                         }
164                         if (refiner->IsUniform()) {
165                                 const int num_values_max = refiner->GetLevel(max_level).GetNumFVarValues(channel);
166                                 fvar_data_offset += num_values_max * fvar_width;
167                         } else {
168                                 const int num_values_total = refiner->GetNumFVarValuesTotal(channel);
169                                 fvar_data_offset += num_values_total * fvar_width;
170                         }
171                 }
172
173                 GLuint buffer;
174                 glGenBuffers(1, &buffer);
175                 glBindBuffer(GL_ARRAY_BUFFER, buffer);
176                 glBufferData(GL_ARRAY_BUFFER, data.size()*sizeof(float),
177                              &data[0], GL_STATIC_DRAW);
178
179                 glGenTextures(1, &texture_buffer);
180                 glBindTexture(GL_TEXTURE_BUFFER, texture_buffer);
181                 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, buffer);
182
183                 glDeleteBuffers(1, &buffer);
184
185                 glGenBuffers(1, &buffer);
186                 glBindBuffer(GL_ARRAY_BUFFER, buffer);
187                 glBufferData(GL_ARRAY_BUFFER, channel_offsets.size()*sizeof(int),
188                              &channel_offsets[0], GL_STATIC_DRAW);
189
190                 glGenTextures(1, &offset_buffer);
191                 glBindTexture(GL_TEXTURE_BUFFER, offset_buffer);
192                 glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, buffer);
193                 glBindTexture(GL_TEXTURE_BUFFER, 0);
194                 glBindBuffer(GL_ARRAY_BUFFER, 0);
195         }
196         GLuint texture_buffer;
197         GLuint offset_buffer;
198         std::vector<int> channel_offsets;
199         int fvar_width;
200 };
201
202 namespace {
203
204 GLuint compileShader(GLenum shaderType,
205                      const char *version,
206                      const char *define,
207                      const char *source)
208 {
209         const char *sources[] = {
210                 version,
211                 define,
212 #ifdef SUPPORT_COLOR_MATERIAL
213                 "#define SUPPORT_COLOR_MATERIAL\n",
214 #else
215                 "",
216 #endif
217                 source,
218         };
219
220         GLuint shader = glCreateShader(shaderType);
221         glShaderSource(shader, 4, sources, NULL);
222         glCompileShader(shader);
223
224         GLint status;
225         glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
226         if (status == GL_FALSE) {
227                 GLchar emsg[1024];
228                 glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
229                 fprintf(stderr, "Error compiling GLSL: %s\n", emsg);
230                 fprintf(stderr, "Version: %s\n", version);
231                 fprintf(stderr, "Defines: %s\n", define);
232                 fprintf(stderr, "Source: %s\n", source);
233                 return 0;
234         }
235
236         return shader;
237 }
238
239 GLuint linkProgram(const char *version, const char *define)
240 {
241         GLuint vertexShader = compileShader(GL_VERTEX_SHADER,
242                                             version,
243                                             define,
244                                             datatoc_gpu_shader_opensubdiv_vertex_glsl);
245         if (vertexShader == 0) {
246                 return 0;
247         }
248         GLuint geometryShader = compileShader(GL_GEOMETRY_SHADER,
249                                               version,
250                                               define,
251                                               datatoc_gpu_shader_opensubdiv_geometry_glsl);
252         if (geometryShader == 0) {
253                 return 0;
254         }
255         GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER,
256                                               version,
257                                               define,
258                                               datatoc_gpu_shader_opensubdiv_fragment_glsl );
259         if (fragmentShader == 0) {
260                 return 0;
261         }
262
263         GLuint program = glCreateProgram();
264
265         glAttachShader(program, vertexShader);
266         glAttachShader(program, geometryShader);
267         glAttachShader(program, fragmentShader);
268
269         glBindAttribLocation(program, 0, "position");
270         glBindAttribLocation(program, 1, "normal");
271
272
273         if (!GLEW_VERSION_3_2) {
274                 /* provide input/output layout info */
275                 glProgramParameteriEXT(program,
276                                        GL_GEOMETRY_INPUT_TYPE_EXT,
277                                        GL_LINES_ADJACENCY_EXT);
278
279                 bool wireframe = strstr(define, "WIREFRAME") != NULL;
280
281                 glProgramParameteriEXT(program,
282                                        GL_GEOMETRY_OUTPUT_TYPE_EXT,
283                                        wireframe ? GL_LINE_STRIP : GL_TRIANGLE_STRIP);
284
285                 glProgramParameteriEXT(program,
286                                        GL_GEOMETRY_VERTICES_OUT_EXT,
287                                        8);
288         }
289
290         glLinkProgram(program);
291
292         glDeleteShader(vertexShader);
293         glDeleteShader(geometryShader);
294         glDeleteShader(fragmentShader);
295
296         GLint status;
297         glGetProgramiv(program, GL_LINK_STATUS, &status);
298         if (status == GL_FALSE) {
299                 GLchar emsg[1024];
300                 glGetProgramInfoLog(program, sizeof(emsg), 0, emsg);
301                 fprintf(stderr, "Error linking GLSL program : %s\n", emsg);
302                 fprintf(stderr, "Defines: %s\n", define);
303                 glDeleteProgram(program);
304                 return 0;
305         }
306
307         glUniformBlockBinding(program,
308                               glGetUniformBlockIndex(program, "Lighting"),
309                               0);
310
311         glProgramUniform1i(program,
312                            glGetUniformLocation(program, "texture_buffer"),
313                            0);  /* GL_TEXTURE0 */
314
315         glProgramUniform1i(program,
316                            glGetUniformLocation(program, "FVarDataOffsetBuffer"),
317                            30);  /* GL_TEXTURE30 */
318
319         glProgramUniform1i(program,
320                            glGetUniformLocation(program, "FVarDataBuffer"),
321                            31);  /* GL_TEXTURE31 */
322
323         return program;
324 }
325
326 void bindProgram(OpenSubdiv_GLMesh *gl_mesh, int program)
327 {
328         glUseProgram(program);
329
330         /* Matrices */
331         glUniformMatrix4fv(glGetUniformLocation(program, "modelViewMatrix"),
332                            1, false,
333                            g_transform.model_view_matrix);
334         glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"),
335                            1, false,
336                            g_transform.projection_matrix);
337         glUniformMatrix3fv(glGetUniformLocation(program, "normalMatrix"),
338                            1, false,
339                            g_transform.normal_matrix);
340
341         /* Lighting */
342         glBindBuffer(GL_UNIFORM_BUFFER, g_lighting_ub);
343         glBufferSubData(GL_UNIFORM_BUFFER,
344                         0, sizeof(g_lighting_data), &g_lighting_data);
345         glBindBuffer(GL_UNIFORM_BUFFER, 0);
346
347         glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_lighting_ub);
348
349         /* Color */
350         GLboolean use_lighting;
351         glGetBooleanv(GL_LIGHTING, &use_lighting);
352
353         if (use_lighting) {
354                 float color[4];
355                 glGetMaterialfv(GL_FRONT, GL_DIFFUSE, color);
356                 glUniform4fv(glGetUniformLocation(program, "diffuse"), 1, color);
357
358                 glGetMaterialfv(GL_FRONT, GL_SPECULAR, color);
359                 glUniform4fv(glGetUniformLocation(program, "specular"), 1, color);
360
361                 glGetMaterialfv(GL_FRONT, GL_SHININESS, color);
362                 glUniform1f(glGetUniformLocation(program, "shininess"), color[0]);
363         }
364         else {
365                 float color[4];
366                 glGetFloatv(GL_CURRENT_COLOR, color);
367                 glUniform4fv(glGetUniformLocation(program, "diffuse"), 1, color);
368         }
369
370         /* Face-vertex data */
371         if (gl_mesh->fvar_data != NULL) {
372                 if (gl_mesh->fvar_data->texture_buffer) {
373                         glActiveTexture(GL_TEXTURE31);
374                         glBindTexture(GL_TEXTURE_BUFFER, gl_mesh->fvar_data->texture_buffer);
375                         glActiveTexture(GL_TEXTURE0);
376                 }
377
378                 if (gl_mesh->fvar_data->offset_buffer) {
379                         glActiveTexture(GL_TEXTURE30);
380                         glBindTexture(GL_TEXTURE_BUFFER, gl_mesh->fvar_data->offset_buffer);
381                         glActiveTexture(GL_TEXTURE0);
382                 }
383
384                 glUniform1i(glGetUniformLocation(program, "osd_fvar_count"),
385                             gl_mesh->fvar_data->fvar_width);
386                 if (gl_mesh->fvar_data->channel_offsets.size() > 0 &&
387                     g_active_uv_index >= 0)
388                 {
389                         glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"),
390                                     gl_mesh->fvar_data->channel_offsets[g_active_uv_index]);
391                 } else {
392                         glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0);
393                 }
394         } else {
395                 glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), 0);
396                 glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0);
397         }
398 }
399
400 }  /* namespace */
401
402 bool openSubdiv_osdGLDisplayInit(void)
403 {
404         static bool need_init = true;
405         static bool init_success = false;
406         if (need_init) {
407
408                 if (!openSubdiv_supportGPUDisplay()) {
409                         return false;
410                 }
411
412                 const char *version = "";
413                 if (GLEW_VERSION_3_2) {
414                         version = "#version 150 compatibility\n";
415                 }
416                 else if (GLEW_VERSION_3_1) {
417                         version = "#version 140\n"
418                                   "#extension GL_ARB_compatibility: enable\n";
419                 }
420                 else {
421                         version = "#version 130\n";
422                         /* minimum supported for OpenSubdiv */
423                 }
424
425                 g_flat_fill_solid_program = linkProgram(
426                         version,
427                         "#define USE_COLOR_MATERIAL\n"
428                         "#define USE_LIGHTING\n"
429                         "#define FLAT_SHADING\n");
430                 g_flat_fill_texture2d_program = linkProgram(
431                         version,
432                         "#define USE_COLOR_MATERIAL\n"
433                         "#define USE_LIGHTING\n"
434                         "#define USE_TEXTURE_2D\n"
435                         "#define FLAT_SHADING\n");
436                 g_smooth_fill_solid_program = linkProgram(
437                         version,
438                         "#define USE_COLOR_MATERIAL\n"
439                         "#define USE_LIGHTING\n"
440                         "#define SMOOTH_SHADING\n");
441                 g_smooth_fill_texture2d_program = linkProgram(
442                         version,
443                         "#define USE_COLOR_MATERIAL\n"
444                         "#define USE_LIGHTING\n"
445                         "#define USE_TEXTURE_2D\n"
446                         "#define SMOOTH_SHADING\n");
447
448                 g_flat_fill_solid_shadeless_program = linkProgram(
449                         version,
450                         "#define USE_COLOR_MATERIAL\n"
451                         "#define FLAT_SHADING\n");
452                 g_flat_fill_texture2d_shadeless_program = linkProgram(
453                         version,
454                         "#define USE_COLOR_MATERIAL\n"
455                         "#define USE_TEXTURE_2D\n"
456                         "#define FLAT_SHADING\n");
457                 g_smooth_fill_solid_shadeless_program = linkProgram(
458                         version,
459                         "#define USE_COLOR_MATERIAL\n"
460                         "#define SMOOTH_SHADING\n");
461                 g_smooth_fill_texture2d_shadeless_program = linkProgram(
462                         version,
463                         "#define USE_COLOR_MATERIAL\n"
464                         "#define USE_TEXTURE_2D\n"
465                         "#define SMOOTH_SHADING\n");
466
467                 g_wireframe_program = linkProgram(
468                         version,
469                         "#define WIREFRAME\n");
470
471                 glGenBuffers(1, &g_lighting_ub);
472                 glBindBuffer(GL_UNIFORM_BUFFER, g_lighting_ub);
473                 glBufferData(GL_UNIFORM_BUFFER,
474                              sizeof(g_lighting_data), NULL, GL_STATIC_DRAW);
475
476                 need_init = false;
477                 init_success = g_flat_fill_solid_program != 0 &&
478                                g_flat_fill_texture2d_program != 0 &&
479                                g_smooth_fill_solid_program != 0 &&
480                                g_smooth_fill_texture2d_program != 0 &&
481                                g_wireframe_program;
482         }
483         return init_success;
484 }
485
486 void openSubdiv_osdGLDisplayDeinit(void)
487 {
488         if (g_lighting_ub != 0) {
489                 glDeleteBuffers(1, &g_lighting_ub);
490         }
491 #define SAFE_DELETE_PROGRAM(program) \
492         do { \
493                 if (program) { \
494                         glDeleteProgram(program); \
495                 } \
496         } while (false)
497
498         SAFE_DELETE_PROGRAM(g_flat_fill_solid_program);
499         SAFE_DELETE_PROGRAM(g_flat_fill_texture2d_program);
500         SAFE_DELETE_PROGRAM(g_smooth_fill_solid_program);
501         SAFE_DELETE_PROGRAM(g_smooth_fill_texture2d_program);
502         SAFE_DELETE_PROGRAM(g_flat_fill_solid_shadeless_program);
503         SAFE_DELETE_PROGRAM(g_flat_fill_texture2d_shadeless_program);
504         SAFE_DELETE_PROGRAM(g_smooth_fill_solid_shadeless_program);
505         SAFE_DELETE_PROGRAM(g_smooth_fill_texture2d_shadeless_program);
506         SAFE_DELETE_PROGRAM(g_wireframe_program);
507
508 #undef SAFE_DELETE_PROGRAM
509 }
510
511 void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
512                                         int active_uv_index)
513 {
514         g_active_uv_index = active_uv_index;
515         g_use_osd_glsl = (use_osd_glsl != 0);
516
517         /* Update transformation matrices. */
518         glGetFloatv(GL_PROJECTION_MATRIX, g_transform.projection_matrix);
519         glGetFloatv(GL_MODELVIEW_MATRIX, g_transform.model_view_matrix);
520
521         copy_m3_m4((float (*)[3])g_transform.normal_matrix,
522                    (float (*)[4])g_transform.model_view_matrix);
523         invert_m3((float (*)[3])g_transform.normal_matrix);
524         transpose_m3((float (*)[3])g_transform.normal_matrix);
525
526         /* Update OpenGL lights positions, colors etc. */
527         g_lighting_data.num_enabled = 0;
528         for (int i = 0; i < MAX_LIGHTS; ++i) {
529                 GLboolean enabled;
530                 glGetBooleanv(GL_LIGHT0 + i, &enabled);
531                 if (enabled) {
532                         g_lighting_data.num_enabled++;
533                 }
534
535                 glGetLightfv(GL_LIGHT0 + i,
536                              GL_POSITION,
537                              g_lighting_data.lights[i].position);
538                 glGetLightfv(GL_LIGHT0 + i,
539                              GL_AMBIENT,
540                              g_lighting_data.lights[i].ambient);
541                 glGetLightfv(GL_LIGHT0 + i,
542                              GL_DIFFUSE,
543                              g_lighting_data.lights[i].diffuse);
544                 glGetLightfv(GL_LIGHT0 + i,
545                              GL_SPECULAR,
546                              g_lighting_data.lights[i].specular);
547                 glGetLightfv(GL_LIGHT0 + i,
548                              GL_SPOT_DIRECTION,
549                              g_lighting_data.lights[i].spot_direction);
550 #ifdef SUPPORT_COLOR_MATERIAL
551                 glGetLightfv(GL_LIGHT0 + i,
552                              GL_CONSTANT_ATTENUATION,
553                              &g_lighting_data.lights[i].constant_attenuation);
554                 glGetLightfv(GL_LIGHT0 + i,
555                              GL_LINEAR_ATTENUATION,
556                              &g_lighting_data.lights[i].linear_attenuation);
557                 glGetLightfv(GL_LIGHT0 + i,
558                              GL_QUADRATIC_ATTENUATION,
559                              &g_lighting_data.lights[i].quadratic_attenuation);
560                 glGetLightfv(GL_LIGHT0 + i,
561                              GL_SPOT_CUTOFF,
562                              &g_lighting_data.lights[i].spot_cutoff);
563                 glGetLightfv(GL_LIGHT0 + i,
564                              GL_SPOT_EXPONENT,
565                              &g_lighting_data.lights[i].spot_exponent);
566                 g_lighting_data.lights[i].spot_cos_cutoff =
567                         cos(g_lighting_data.lights[i].spot_cutoff);
568 #endif
569         }
570 }
571
572 static GLuint prepare_patchDraw(OpenSubdiv_GLMesh *gl_mesh,
573                                 bool fill_quads)
574 {
575         GLint program = 0;
576         if (!g_use_osd_glsl) {
577                 glGetIntegerv(GL_CURRENT_PROGRAM, &program);
578                 if (program) {
579                         GLint model;
580                         glGetIntegerv(GL_SHADE_MODEL, &model);
581
582                         GLint location = glGetUniformLocation(program, "osd_flat_shading");
583                         if (location != -1) {
584                                 glUniform1i(location, model == GL_FLAT);
585                         }
586
587                         /* Face-vertex data */
588                         if (gl_mesh->fvar_data != NULL) {
589                                 if (gl_mesh->fvar_data->texture_buffer) {
590                                         glActiveTexture(GL_TEXTURE31);
591                                         glBindTexture(GL_TEXTURE_BUFFER,
592                                                       gl_mesh->fvar_data->texture_buffer);
593                                         glActiveTexture(GL_TEXTURE0);
594                                 }
595
596                                 if (gl_mesh->fvar_data->offset_buffer) {
597                                         glActiveTexture(GL_TEXTURE30);
598                                         glBindTexture(GL_TEXTURE_BUFFER,
599                                                       gl_mesh->fvar_data->offset_buffer);
600                                         glActiveTexture(GL_TEXTURE0);
601                                 }
602
603                                 GLint location = glGetUniformLocation(program, "osd_fvar_count");
604                                 if (location != -1) {
605                                         glUniform1i(location, gl_mesh->fvar_data->fvar_width);
606                                 }
607
608                                 location = glGetUniformLocation(program, "osd_active_uv_offset");
609                                 if (location != -1) {
610                                         if (gl_mesh->fvar_data->channel_offsets.size() > 0 &&
611                                             g_active_uv_index >= 0)
612                                         {
613                                                 glUniform1i(location,
614                                                             gl_mesh->fvar_data->channel_offsets[g_active_uv_index]);
615                                         } else {
616                                                 glUniform1i(location, 0);
617                                         }
618                                 }
619                         } else {
620                                 glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), 0);
621                                 glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0);
622                         }
623                 }
624                 return program;
625         }
626
627         if (fill_quads) {
628                 int model;
629                 GLboolean use_texture_2d, use_lighting;
630                 glGetIntegerv(GL_SHADE_MODEL, &model);
631                 glGetBooleanv(GL_TEXTURE_2D, &use_texture_2d);
632                 glGetBooleanv(GL_LIGHTING, &use_lighting);
633                 if (model == GL_FLAT) {
634                         if (use_texture_2d) {
635                                 program = use_lighting
636                                                   ? g_flat_fill_texture2d_program
637                                                   : g_flat_fill_texture2d_shadeless_program;
638                         }
639                         else {
640                                 program = use_lighting
641                                                   ? g_flat_fill_solid_program
642                                                   : g_flat_fill_solid_shadeless_program;
643                         }
644                 }
645                 else {
646                         if (use_texture_2d) {
647                                 program = use_lighting
648                                                   ? g_smooth_fill_texture2d_program
649                                                   : g_smooth_fill_texture2d_shadeless_program;
650                         }
651                         else {
652                                 program = use_lighting
653                                                   ? g_smooth_fill_solid_program
654                                                   : g_smooth_fill_solid_shadeless_program;
655                         }
656                 }
657         }
658         else {
659                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
660                 program = g_wireframe_program;
661         }
662
663         bindProgram(gl_mesh, program);
664
665         return program;
666 }
667
668 static void perform_drawElements(GLuint program,
669                                  int patch_index,
670                                  int num_elements,
671                                  int start_element)
672 {
673         if (program) {
674                 glUniform1i(glGetUniformLocation(program, "PrimitiveIdBase"),
675                             patch_index);
676         }
677         glDrawElements(GL_LINES_ADJACENCY,
678                        num_elements,
679                        GL_UNSIGNED_INT,
680                        (void *)(start_element * sizeof(unsigned int)));
681 }
682
683 static void finish_patchDraw(bool fill_quads)
684 {
685         /* TODO(sergey): Some of the stuff could be done once after the whole
686          * mesh is displayed.
687          */
688
689         /* Restore state. */
690         if (!fill_quads) {
691                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
692         }
693         glBindVertexArray(0);
694
695         if (g_use_osd_glsl) {
696                 /* TODO(sergey): Store previously used program and roll back to it? */
697                 glUseProgram(0);
698         }
699 }
700
701 static void draw_partition_patches_range(GLMeshInterface *mesh,
702                                          GLuint program,
703                                          int start_patch,
704                                          int num_patches)
705 {
706         int traversed_patches = 0, num_remained_patches = num_patches;
707         const OpenSubdiv::Osd::PatchArrayVector& patches =
708                 mesh->GetPatchTable()->GetPatchArrays();
709         for (int i = 0; i < (int)patches.size(); ++i) {
710                 const OpenSubdiv::Osd::PatchArray& patch = patches[i];
711                 OpenSubdiv::Far::PatchDescriptor desc = patch.GetDescriptor();
712                 OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType();
713
714                 if (patchType == OpenSubdiv::Far::PatchDescriptor::QUADS) {
715                         const int num_block_patches = patch.GetNumPatches();
716                         if (start_patch >= traversed_patches &&
717                             start_patch < traversed_patches + num_block_patches)
718                         {
719                                 const int num_control_verts = desc.GetNumControlVertices();
720                                 const int start_draw_patch = start_patch - traversed_patches;
721                                 const int num_draw_patches = std::min(num_remained_patches,
722                                                                       num_block_patches - start_draw_patch);
723                                 perform_drawElements(program,
724                                                      i + start_draw_patch,
725                                                      num_draw_patches * num_control_verts,
726                                                      patch.GetIndexBase() + start_draw_patch * num_control_verts);
727                                 num_remained_patches -= num_draw_patches;
728                         }
729                         if (num_remained_patches == 0) {
730                                 break;
731                         }
732                         traversed_patches += num_block_patches;
733                 }
734     }
735 }
736
737 static void draw_all_patches(GLMeshInterface *mesh,
738                              GLuint program)
739 {
740         const OpenSubdiv::Osd::PatchArrayVector& patches =
741                 mesh->GetPatchTable()->GetPatchArrays();
742         for (int i = 0; i < (int)patches.size(); ++i) {
743                 const OpenSubdiv::Osd::PatchArray& patch = patches[i];
744                 OpenSubdiv::Far::PatchDescriptor desc = patch.GetDescriptor();
745                 OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType();
746
747                 if (patchType == OpenSubdiv::Far::PatchDescriptor::QUADS) {
748                         perform_drawElements(program,
749                                              i,
750                                              patch.GetNumPatches() * desc.GetNumControlVertices(),
751                                              patch.GetIndexBase());
752                 }
753     }
754 }
755
756 void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
757                                  int fill_quads,
758                                  int start_patch,
759                                  int num_patches)
760 {
761         GLMeshInterface *mesh =
762                 (GLMeshInterface *)(gl_mesh->descriptor);
763
764         /* Make sure all global invariants are initialized. */
765         if (!openSubdiv_osdGLDisplayInit()) {
766                 return;
767         }
768
769         /* Setup GLSL/OpenGL to draw patches in current context. */
770         GLuint program = prepare_patchDraw(gl_mesh, fill_quads != 0);
771
772         if (start_patch != -1) {
773                 draw_partition_patches_range(mesh,
774                                              program,
775                                              start_patch,
776                                              num_patches);
777         }
778         else {
779                 draw_all_patches(mesh, program);
780         }
781
782         /* Finish patch drawing by restoring all changes to the OpenGL context. */
783         finish_patchDraw(fill_quads != 0);
784 }
785
786 void openSubdiv_osdGLAllocFVar(OpenSubdiv_TopologyRefinerDescr *topology_refiner,
787                                OpenSubdiv_GLMesh *gl_mesh,
788                                const float *fvar_data)
789 {
790         GLMeshInterface *mesh =
791                 (GLMeshInterface *)(gl_mesh->descriptor);
792         gl_mesh->fvar_data = OBJECT_GUARDED_NEW(OpenSubdiv_GLMeshFVarData);
793         gl_mesh->fvar_data->Create(topology_refiner->osd_refiner,
794                                    mesh->GetFarPatchTable(),
795                                    2,
796                                    fvar_data);
797 }
798
799 void openSubdiv_osdGLDestroyFVar(OpenSubdiv_GLMesh *gl_mesh)
800 {
801         if (gl_mesh->fvar_data != NULL) {
802                 OBJECT_GUARDED_DELETE(gl_mesh->fvar_data, OpenSubdiv_GLMeshFVarData);
803         }
804 }