1 // Copyright (c) 2015 Sergio Gonzalez. All rights reserved.
2 // License: https://github.com/serge-rgb/milton#license
3 
4 
5 #include "gl_helpers.h"
6 #include "gl.h"
7 
8 #include "memory.h"
9 #include "platform.h"
10 
11 
12 
13 #if defined(_WIN32)
14     // Declaring glMinSampleShadingARB because we have a different path for loading it.
15     typedef void glMinSampleShadingARBProc(GLclampf value); glMinSampleShadingARBProc* glMinSampleShadingARB;
16 #endif  //_WIN32
17 
18 
19 // Global variable that keeps track of Milton's GL configuration. See GLHelperFlags.
20 static int g_gl_helper_flags;
21 
22 namespace gl {
23 
24 // Static helpers
25 static void
query_error(const char * expr,const char * file,int line)26 query_error (const char* expr, const char* file, int line)
27 {
28     GLenum err = glGetError();
29     const char* str = "";
30     if ( err != GL_NO_ERROR ) {
31         char buffer[256];
32         switch( err ) {
33 #ifdef GL_INVALID_ENUM
34         case GL_INVALID_ENUM:
35             str = "GL_INVALID_ENUM";
36             break;
37 #endif
38 #ifdef GL_INVALID_VALUE
39         case GL_INVALID_VALUE:
40             str = "GL_INVALID_VALUE";
41             break;
42 #endif
43 #ifdef GL_INVALID_OPERATION
44         case GL_INVALID_OPERATION:
45             str = "GL_INVALID_OPERATION";
46             break;
47 #endif
48 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION
49         case GL_INVALID_FRAMEBUFFER_OPERATION:
50             str = "GL_INVALID_FRAMEBUFFER_OPERATION";
51             break;
52 #endif
53 #ifdef GL_OUT_OF_MEMORY
54         case GL_OUT_OF_MEMORY:
55             str = "GL_OUT_OF_MEMORY";
56             break;
57 #endif
58 #ifdef GL_STACK_OVERFLOW
59         case GL_STACK_OVERFLOW:
60             str = "GL_STACK_OVERFLOW";
61             break;
62 #endif
63 #ifdef GL_STACK_UNDERFLOW
64         case GL_STACK_UNDERFLOW:
65             str = "GL_STACK_UNDERFLOW";
66             break;
67 #endif
68         default:
69             str = "SOME GL ERROR";
70             break;
71         }
72         snprintf(buffer, 256, "%s in: %s:%d\n", str, file, line);
73         gl::log(buffer);
74         snprintf(buffer, 256, "   ---- Expression: %s\n", expr);
75         gl::log(buffer);
76     }
77 }
78 
79 static void
set_flags(int flags)80 set_flags (int flags)
81 {
82     g_gl_helper_flags |= flags;
83 }
84 
85 bool
check_flags(int flags)86 check_flags (int flags)
87 {
88     bool result = g_gl_helper_flags & flags;
89     return result;
90 }
91 
92 bool
load()93 load ()
94 {
95 #define X(ret, func, ...) func = (decltype(func)) platform_get_gl_proc(#func);
96     GL_FUNCTIONS
97 #undef X
98 
99     bool ok = true;
100     // Extension checking.
101 
102 #if MULTISAMPLING_ENABLED
103     i64 num_extensions = 0;
104     glGetIntegerv(GL_NUM_EXTENSIONS, (GLint*)&num_extensions);
105 
106     if ( num_extensions > 0 ) {
107         for ( i64 extension_i = 0; extension_i < num_extensions; ++extension_i ) {
108             char* extension_string = (char*)glGetStringi(GL_EXTENSIONS, (GLuint)extension_i);
109 
110                 if ( strcmp(extension_string, "GL_ARB_sample_shading") == 0 ) {
111                     gl::set_flags(GLHelperFlags_SAMPLE_SHADING);
112                 }
113                 if ( strcmp(extension_string, "GL_ARB_texture_multisample") == 0 ) {
114                     gl::set_flags(GLHelperFlags_TEXTURE_MULTISAMPLE);
115                 }
116         }
117     }
118     // glGetStringi probably does not handle GL_EXTENSIONS
119     else if ( num_extensions == 0 ) {
120         const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
121         #define MAX_EXTENSION_LEN 256
122         char ext[MAX_EXTENSION_LEN] = {};
123         const char* begin = extensions;
124         for ( const char* end = extensions;
125               *end != '\0';
126               ++end ) {
127             if ( *end == ' ' ) {
128                 size_t len = (size_t)end - (size_t)begin;
129 
130                 if ( len < MAX_EXTENSION_LEN ) {
131                     memcpy((void*)ext, (void*)begin, len);
132                     ext[len]='\0';
133                         if ( strcmp(ext, "GL_ARB_sample_shading") == 0 ) {
134                             gl::set_flags(GLHelperFlags_SAMPLE_SHADING);
135                         }
136                         if ( strcmp(ext, "GL_ARB_texture_multisample") == 0 ) {
137                             gl::set_flags(GLHelperFlags_TEXTURE_MULTISAMPLE);
138                         }
139                     begin = end+1;
140                 }
141                 else {
142                     milton_log("WARNING: Extension too large (%d)\n", len);
143                 }
144             }
145         }
146     }
147 #endif
148 
149 #if defined(_WIN32)
150 #pragma warning(push, 0)
151     if ( !check_flags(GLHelperFlags_SAMPLE_SHADING) ) {
152         glMinSampleShadingARB = NULL;
153     }
154 #pragma warning(pop)
155 #undef GETADDRESS
156 #endif
157     return ok;
158 }
159 
160 void
log(char * str)161 log (char* str)
162 {
163 #ifdef _WIN32
164     OutputDebugStringA(str);
165 #else
166     fprintf(stderr, "%s", str);
167 #endif
168 }
169 
170 GLuint
compile_shader(const char * in_src,GLuint type,char * config,char * variation_config)171 compile_shader (const char* in_src, GLuint type, char* config, char* variation_config)
172 {
173     const char* sources[] = {
174         #if USE_GL_3_2
175             "#version 330 \n",
176         #else
177             "#version 120\n",
178             //"#extension GL_ARB_gpu_shader5 : disable \n",
179             // "#extension GL_ARB_gpu_shader4 : enable \n",
180             (type == GL_VERTEX_SHADER) ? "#define in attribute \n#define out varying\n"
181                                        : "#define in varying   \n#define out\n#define out_color gl_FragColor\n",
182             "#define texture texture2D\n",
183         #endif
184         #if STROKE_DEBUG_VIZ
185             "#define STROKE_DEBUG_VIZ 1\n",
186         #else
187             "#define STROKE_DEBUG_VIZ 0\n",
188         #endif
189         (check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE)) ? "#define HAS_TEXTURE_MULTISAMPLE 1\n"
190                                                                     : "#define HAS_TEXTURE_MULTISAMPLE 0\n",
191         "#if HAS_TEXTURE_MULTISAMPLE\n",
192         "#extension GL_ARB_sample_shading : enable\n",
193         //" #extension GL_ARB_texture_multisample : enable\n",
194         "#endif\n",
195 #if USE_GL_3_2
196         (type == GL_FRAGMENT_SHADER) ? "out vec4 out_color; \n" : "\n",
197 #endif
198 
199         config,
200         variation_config,
201         in_src
202     };
203 
204     GLuint obj = glCreateShader(type);
205 
206     glShaderSource(obj, array_count(sources), sources, NULL);
207     glCompileShader(obj);
208     // ERROR CHECKING
209     int res = 0;
210     //glGetObjectParameteriv(obj, GL_COMPILE_STATUS, &res);
211     glGetShaderiv(obj, GL_COMPILE_STATUS, &res);
212 
213     GLint length;
214     glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &length);
215     if ( !res && length > 0 ) {
216         if ( !res ) {
217             milton_log("SHADER SOURCE:\n%s\n", sources[2]);
218         }
219         char* log = (char*)mlt_calloc(1, (size_t)length, "Strings");
220         GLsizei written_len;
221         // glGetShaderInfoLog(obj, length, &written_len, log);
222         glGetShaderInfoLog (obj, length, &written_len, (GLchar*)log);
223         gl::log("Shader compilation info. \n    ---- Info log:\n");
224         gl::log(log);
225 
226         if ( !res ) {
227             milton_die_gracefully("Shader compilation error\n");
228         }
229 
230         mlt_free(log, "Strings");
231     }
232     return obj;
233 }
234 
235 #if defined(__MACH__)
236 #undef glShaderSourceARB
237 #undef glCompileShaderARB
238 #undef glGetObjectParameterivARB
239 #undef glGetInfoLogARB
240 #define glGetObjectParameterivARB glGetProgramiv
241 #define glGetInfoLogARB glGetProgramInfoLog
242 #define glAttachObjectARB glAttachShader
243 #define glLinkProgramARB glLinkProgram
244 #define glValidateProgramARB glValidateProgram
245 #define glUseProgramObjectARB glUseProgram
246 #endif
247 
248 void
link_program(GLuint obj,GLuint shaders[],int64_t num_shaders)249 link_program (GLuint obj, GLuint shaders[], int64_t num_shaders)
250 {
251     mlt_assert(glIsProgram (obj));
252     for ( int i = 0; i < num_shaders; ++i ) {
253         mlt_assert(glIsShader(shaders[i]));
254 
255         glAttachShader(obj, shaders[i]);
256     }
257     glLinkProgram(obj);
258 
259     // ERROR CHECKING
260     int res = 0;
261     glGetProgramiv(obj, GL_LINK_STATUS, &res);
262     if ( !res ) {
263         gl::log("ERROR: program did not link.\n");
264         GLint len;
265         glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &len);
266         GLsizei written_len;
267         char* log = (char*)mlt_calloc(1, (size_t)len, "Strings");
268         glGetProgramInfoLog(obj, (GLsizei)len, &written_len, (GLchar*)log);
269         //glGetInfoLog(obj, (GLsizei)len, &written_len, log);
270         gl::log(log);
271         mlt_free(log, "Strings");
272         mlt_assert(!"program linking error");
273     }
274     glValidateProgram(obj);
275 }
276 #if defined(__MACH__)
277 #undef glGetObjectParameterivARB
278 #undef glGetInfoLogARB
279 #undef glAttachObjectARB
280 #undef glLinkProgramARB
281 #undef glValidateProgramARB
282 #undef glUseProgramObjectARB
283 #endif
284 
285 bool
set_attribute_vec2(GLuint program,char * name,GLfloat * data,size_t data_sz)286 set_attribute_vec2(GLuint program, char* name, GLfloat* data, size_t data_sz)
287 {
288     bool ok = true;
289     GLint loc = glGetAttribLocation(program, (GLchar*)name);
290     ok = loc >= 0;
291     if ( ok ) {
292         glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)data_sz, data, GL_STATIC_DRAW);
293     }
294 
295     return ok;
296 }
297 
298 void
use_program(GLuint program)299 use_program(GLuint program)
300 {
301     static GLuint cached_program = 0;
302     if (program != cached_program) {
303         glUseProgram(program);
304         cached_program = program;
305     }
306 }
307 
308 bool
set_uniform_vec4(GLuint program,char * name,size_t count,float * vals)309 set_uniform_vec4(GLuint program, char* name, size_t count, float* vals)
310 {
311     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
312     use_program(program);
313     bool ok = true;
314     GLint loc = glGetUniformLocation(program, (GLchar*)name);
315     ok = loc >= 0;
316 
317     if ( ok ) {
318         glUniform4fv(loc, (GLsizei)count, vals);
319     }
320     use_program(last_program);
321     return ok;
322 }
323 
324 bool
set_uniform_vec3i(GLuint program,char * name,size_t count,i32 * vals)325 set_uniform_vec3i(GLuint program, char* name, size_t count, i32* vals)
326 {
327     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
328     use_program(program);
329     bool ok = true;
330     GLint loc = glGetUniformLocation(program, (GLchar*)name);
331     ok = loc >= 0;
332 
333     if ( ok ) {
334         glUniform3iv(loc, (GLsizei)count, vals);
335     }
336     use_program(last_program);
337     return ok;
338 }
339 
340 bool
set_uniform_vec3(GLuint program,char * name,size_t count,float * vals)341 set_uniform_vec3(GLuint program, char* name, size_t count, float* vals)
342 {
343     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
344     use_program(program);
345     bool ok = true;
346     GLint loc = glGetUniformLocation(program, (GLchar*)name);
347     ok = loc >= 0;
348 
349     if ( ok ) {
350         glUniform3fv(loc, (GLsizei)count, vals);
351     }
352     use_program(last_program);
353     return ok;
354 }
355 
356 bool
set_uniform_vec2(GLuint program,char * name,size_t count,float * vals)357 set_uniform_vec2(GLuint program, char* name, size_t count, float* vals)
358 {
359     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
360     use_program(program);
361     bool ok = true;
362     GLint loc = glGetUniformLocation(program, (GLchar*)name);
363     ok = loc >= 0;
364     if ( ok ) {
365         glUniform2fv(loc, (GLsizei)count, vals);
366     }
367     use_program(last_program);
368     return ok;
369 }
370 
371 bool
set_uniform_vec2(GLuint program,char * name,float x,float y)372 set_uniform_vec2(GLuint program, char* name, float x, float y)
373 {
374     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
375     use_program(program);
376     bool ok = true;
377     GLint loc = glGetUniformLocation(program, (GLchar*)name);
378     ok = loc >= 0;
379     if ( ok ) {
380         glUniform2f(loc, x, y);
381     }
382     use_program(last_program);
383     return ok;
384 }
385 
386 bool
set_uniform_vec2i(GLuint program,char * name,size_t count,i32 * vals)387 set_uniform_vec2i(GLuint program, char* name, size_t count, i32* vals)
388 {
389     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
390     use_program(program);
391     bool ok = true;
392     GLint loc = glGetUniformLocation(program, (GLchar*)name);
393     ok = loc >= 0;
394     if ( ok ) {
395         glUniform2iv(loc, (GLsizei)count, vals);
396     }
397     use_program(last_program);
398     return ok;
399 }
400 
401 bool
set_uniform_f(GLuint program,char * name,float val)402 set_uniform_f(GLuint program, char* name, float val)
403 {
404     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
405     use_program(program);
406     bool ok = true;
407     GLint loc = glGetUniformLocation(program, (GLchar*)name);
408     ok = loc >= 0;
409     if ( ok ) {
410         glUniform1f(loc, val);
411     }
412     use_program(last_program);
413     return ok;
414 }
415 
416 bool
set_uniform_i(GLuint program,char * name,i32 val)417 set_uniform_i(GLuint program, char* name, i32 val)
418 {
419     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
420     use_program(program);
421     bool ok = true;
422     GLint loc = glGetUniformLocation(program, (GLchar*)name);
423     ok = loc >= 0;
424     if ( ok ) {
425         glUniform1i(loc, val);
426     }
427     use_program(last_program);
428     return ok;
429 }
430 
431 bool
set_uniform_vec2i(GLuint program,char * name,i32 x,i32 y)432 set_uniform_vec2i(GLuint program, char* name, i32 x, i32 y)
433 {
434     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
435     use_program(program);
436     bool ok = true;
437     GLint loc = glGetUniformLocation(program, (GLchar*)name);
438     ok = loc >= 0;
439     if ( ok ) {
440         glUniform2i(loc, x, y);
441     }
442     use_program(last_program);
443     return ok;
444 }
445 
446 bool
set_uniform_mat2(GLuint program,char * name,f32 * vals)447 set_uniform_mat2 (GLuint program, char* name, f32* vals)
448 {
449     GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
450     use_program(program);
451     bool ok = true;
452     GLint loc = glGetUniformLocation(program, (GLchar*)name);
453     ok = loc >= 0;
454     if ( ok ) {
455         glUniformMatrix2fv(loc, 1, /*transpose*/false, vals);
456     }
457     use_program(last_program);
458     return ok;
459 }
460 
461 
462 GLuint
new_color_texture(int w,int h)463 new_color_texture(int w, int h)
464 {
465     GLuint t = 0;
466     glGenTextures(1, &t);
467     glBindTexture(GL_TEXTURE_2D, t);
468     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
469     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
470     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
471     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
472     glTexImage2D(GL_TEXTURE_2D, /*level = */ 0, /*internal_format = */ GL_RGBA8,
473                  /*width, height = */ w, h,
474                  /*border = */ 0,
475                  /*format = */ GL_RGBA, /*type = */ GL_FLOAT,
476                  /*data = */ NULL);
477     glBindTexture(GL_TEXTURE_2D, 0);
478     return t;
479 }
480 
481 GLuint
new_depth_stencil_texture(int w,int h)482 new_depth_stencil_texture(int w, int h)
483 {
484     GLuint t = 0;
485     glGenTextures(1, &t);
486     glBindTexture(GL_TEXTURE_2D, t);
487     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
488     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
489     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
490     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
491     //glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
492     // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
493     // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
494     glTexImage2D(GL_TEXTURE_2D, /*level = */ 0, /*internal_format = */ GL_DEPTH24_STENCIL8,
495                  /*width, height = */ w,h,
496                  /*border = */ 0,
497                  /*format = */ GL_DEPTH_STENCIL, /*type = */ GL_UNSIGNED_INT_24_8,
498                  /*data = */ NULL);
499     glBindTexture(GL_TEXTURE_2D, 0);
500     return t;
501 }
502 
503 GLuint
new_fbo(GLuint color_attachment,GLuint depth_stencil_attachment,GLenum texture_target)504 new_fbo(GLuint color_attachment, GLuint depth_stencil_attachment, GLenum texture_target)
505 {
506     GLuint fbo = 0;
507     glGenFramebuffersEXT(1, &fbo);
508     glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
509 
510 
511     glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
512                               color_attachment, 0);
513     if ( depth_stencil_attachment ) {
514         glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target,
515                                   depth_stencil_attachment, 0);
516     }
517 
518     glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
519     return fbo;
520 }
521 
522 
523 GLuint
new_color_texture_multisample(int w,int h)524 new_color_texture_multisample(int w, int h)
525 {
526 #if MULTISAMPLING_ENABLED
527     GLuint t = 0;
528     glGenTextures(1, &t);
529     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, t);
530 
531     glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA_NUM_SAMPLES,
532                             GL_RGBA,
533                             w,h,
534                             GL_TRUE);
535     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
536 
537     return t;
538 #else
539     return 0;
540 #endif
541 }
542 
543 GLuint
new_depth_stencil_texture_multisample(int w,int h)544 new_depth_stencil_texture_multisample(int w, int h)
545 {
546 #if MULTISAMPLING_ENABLED
547     GLuint t = 0;
548     glGenTextures(1, &t);
549     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, t);
550 
551     glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA_NUM_SAMPLES,
552                             /*internalFormat, num of components*/GL_DEPTH24_STENCIL8,
553                             w,h,
554                             GL_TRUE);
555 
556 
557     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
558 
559     return t;
560 #else
561     return 0;
562 #endif
563 }
564 
565 void
resize_color_texture_multisample(GLuint t,int w,int h)566 resize_color_texture_multisample(GLuint t, int w, int h)
567 {
568 #if MULTISAMPLING_ENABLED
569     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, t);
570     glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA_NUM_SAMPLES,
571                             GL_RGBA,
572                             w,h,
573                             GL_TRUE);
574 #endif
575 }
576 
577 void
resize_depth_stencil_texture_multisample(GLuint t,int w,int h)578 resize_depth_stencil_texture_multisample(GLuint t, int w, int h)
579 {
580 #if MULTISAMPLING_ENABLED
581     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, t);
582     glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA_NUM_SAMPLES,
583                             GL_DEPTH24_STENCIL8,
584                             w,h,
585                             GL_TRUE);
586 
587 #endif
588 }
589 
590 void
resize_color_texture(GLuint t,int w,int h)591 resize_color_texture(GLuint t, int w, int h)
592 {
593     glBindTexture(GL_TEXTURE_2D, t);
594     glTexImage2D(GL_TEXTURE_2D, /*level = */ 0, /*internal_format = */ GL_RGBA8,
595                  /*width, height = */ w,h,
596                  /*border = */ 0,
597                  /*format = */ GL_RGBA, /*type = */ GL_UNSIGNED_BYTE,
598                  /*data = */ NULL);
599 }
600 
601 
602 void
resize_depth_stencil_texture(GLuint t,int w,int h)603 resize_depth_stencil_texture(GLuint t, int w, int h)
604 {
605     glBindTexture(GL_TEXTURE_2D, t);
606     glTexImage2D(GL_TEXTURE_2D, /*level = */ 0, /*internal_format = */ GL_DEPTH24_STENCIL8,
607                  /*width, height = */ w,h,
608                  /*border = */ 0,
609                  /*format = */ GL_DEPTH_STENCIL, /*type = */ GL_UNSIGNED_INT_24_8,
610                  /*data = */ NULL);
611 }
612 
613 void
vertex_attrib_v3f(GLuint program,char * name,GLuint vbo)614 vertex_attrib_v3f(GLuint program, char* name, GLuint vbo)
615 {
616     GLint loc = glGetAttribLocation(program, name);
617     if (loc >= 0) {
618         glBindBuffer(GL_ARRAY_BUFFER, vbo);
619         glEnableVertexAttribArray((GLuint)loc);
620         glVertexAttribPointer(/*attrib location*/ (GLuint)loc,
621                               /*size*/ 3, GL_FLOAT, /*normalize*/ GL_FALSE,
622                               /*stride*/ 0, /*ptr*/ 0);
623     }
624 }
625 
626 void
vertex_attrib_v2f(GLuint program,char * name,GLuint vbo)627 vertex_attrib_v2f(GLuint program, char* name, GLuint vbo)
628 {
629     GLint loc = glGetAttribLocation(program, name);
630     if (loc >= 0) {
631         glBindBuffer(GL_ARRAY_BUFFER, vbo);
632         glEnableVertexAttribArray((GLuint)loc);
633         glVertexAttribPointer(/*attrib location*/ (GLuint)loc,
634                               /*size*/ 2, GL_FLOAT, /*normalize*/ GL_FALSE,
635                               /*stride*/ 0, /*ptr*/ 0);
636     }
637 }
638 
639 
640 }  // namespace gl
641 
642 #ifdef _WIN32
643 
644 #endif  // _WIN32
645 
646