4 * Tool to take an input .glsl file and write out a corresponding .c and .h
5 * file based on the content. The .glsl file contains a number of shaders
6 * marked with either #fragment <name> or #vertex <name>
7 * a C file with appropriate escaping, as well as program definitions
8 * written as #program <name> = <shader1> <shader2> ... <shaderN>
10 * Copyright (c) 2007-2010 Nathan Keynes.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
29 #include <glib/gstrfuncs.h>
30 #include <glib/glist.h>
33 #define DEF_ALLOC_SIZE 4096
34 #define MAX_SHADERS 128
41 typedef struct variable {
42 gboolean uniform; /* TRUE = uniform, FALSE = attribute */
47 typedef struct shader {
54 typedef struct program {
61 typedef struct glsldata {
68 #define isident(c) (isalnum(c)||(c)=='_')
70 static void parseVarDecl( shader_t shader, gboolean uniform, char *input )
73 char *p = g_strstrip(input);
74 for( i=0; isident(p[i]); i++)
76 fprintf( stderr, "Error: unable to parse variable decl '%s'\n", p );
77 return; /* incomplete decl? */
79 char *type = g_strndup(p, i);
80 p = g_strstrip(input+i);
81 for( i=0; isident(p[i]); i++)
83 fprintf( stderr, "Error: unable to parse variable decl '%s'\n", p );
84 return; /* incomplete decl? */
86 char *name = g_strndup(p, i);
87 variable_t var = g_malloc0(sizeof(struct variable));
88 var->uniform = uniform;
91 shader->variables = g_list_append(shader->variables,var);
94 static shader_t findShader( GList *shaders, const char *name )
97 while( ptr != NULL ) {
98 shader_t shader = ptr->data;
99 if( strcmp(shader->name, name) == 0 )
106 static gboolean addProgramVariable( program_t program, variable_t variable )
108 GList *ptr = program->variables;
109 while( ptr != NULL ) {
110 variable_t varp = ptr->data;
111 if( strcmp(varp->name, variable->name) == 0 ) {
112 if( varp->uniform == variable->uniform && strcmp(varp->type, variable->type) == 0 )
113 return TRUE; /* All ok */
114 fprintf( stderr, "Error: Variable type mismatch on '%s'\n", variable->name );
119 program->variables = g_list_append(program->variables, variable);
123 static void linkPrograms( glsldata_t data )
125 GList *program_ptr = data->programs;
127 while( program_ptr != NULL ) {
128 program_t program = program_ptr->data;
129 for( i=0; program->shader_names[i] != NULL; i++ ) {
130 shader_t shader = findShader(data->shaders, program->shader_names[i]);
131 if( shader == NULL ) {
132 fprintf( stderr, "Error: unable to resolve shader '%s'\n", program->shader_names[i] );\
134 GList *varptr = shader->variables;
135 while( varptr != NULL ) {
136 addProgramVariable(program, varptr->data);
137 varptr = varptr->next;
141 program_ptr = program_ptr->next;
147 static struct glsldata *readInput( const char *filename )
150 size_t current_size = 0, current_posn = 0;
153 FILE *f = fopen( filename, "ro" );
155 fprintf( stderr, "Error: unable to open input file '%s': %s\n", filename, strerror(errno) );
159 shader_t shader = NULL;
160 glsldata_t result = g_malloc0(sizeof(struct glsldata));
161 assert( result != NULL );
162 result->filename = strdup(filename);
164 while( fgets(buf, sizeof(buf), f) != NULL ) {
165 if( strlen(buf) == 0 )
168 if( strncmp(buf, "#vertex ", 8) == 0 ) {
169 shader = g_malloc0(sizeof(struct shader));
170 assert( shader != NULL );
171 shader->type = VERTEX_SHADER;
172 shader->name = strdup(g_strstrip(buf+8));
173 shader->body = malloc(DEF_ALLOC_SIZE);
174 shader->body[0] = '\0';
175 current_size = DEF_ALLOC_SIZE;
177 result->shaders = g_list_append(result->shaders, shader);
178 } else if( strncmp( buf, "#fragment ", 10 ) == 0 ) {
179 shader = g_malloc0(sizeof(struct shader));
180 assert( shader != NULL );
181 shader->type = FRAGMENT_SHADER;
182 shader->name = strdup(g_strstrip(buf+10));
183 shader->body = malloc(DEF_ALLOC_SIZE);
184 shader->body[0] = '\0';
185 current_size = DEF_ALLOC_SIZE;
187 result->shaders = g_list_append(result->shaders, shader);
188 } else if( strncmp( buf, "#program ", 9 ) == 0 ) {
190 program_t program = g_malloc0(sizeof(struct program));
192 char *equals = strchr(rest, '=');
193 if( equals == NULL ) {
194 fprintf( stderr, "Error: invalid program line %s\n", buf );
198 program->name = g_strdup(g_strstrip(rest));
199 program->shader_names = g_strsplit_set(g_strstrip(equals+1), " \t\r,", 0);
200 result->programs = g_list_append(result->programs, program);
201 for(i=0;program->shader_names[i] != NULL; i++ );
202 if( i > result->max_shaders )
203 result->max_shaders = i;
204 } else if( shader != NULL ) {
205 size_t len = strlen(buf);
206 if( current_posn + len > current_size ) {
207 shader->body = realloc(shader->body, current_size*2);
208 assert( shader->body != NULL );
211 strcpy( shader->body + current_posn, buf );
213 char *line = g_strstrip(buf);
214 if( strncmp( line, "uniform ", 8 ) == 0 ) {
215 parseVarDecl(shader, TRUE, line+8);
216 } else if( strncmp( line, "attribute ", 10 ) == 0 ) {
217 parseVarDecl(shader, FALSE, line+10);
223 linkPrograms(result);
228 * Copy input to output, quoting " characters as we go.
230 static void writeCString( FILE *out, const char *str )
237 } else if( *p == '\n' ) {
238 fputs( "\\n\\", out );
245 static const char *sl_type_map[][3] = {
246 {"int", "int", "int *"},
247 {"short", "short", "short *"},
248 {"sampler", "int", "int *"},
249 {"vec", "GLfloat *", "GLfloat *"},
250 {"mat", "GLfloat *", "GLfloat *"},
254 static const char *getCType( const char *sl_type, gboolean isUniform ) {
255 for( unsigned i=0; sl_type_map[i][0] != NULL; i++ ) {
256 if( strncmp(sl_type_map[i][0], sl_type, strlen(sl_type_map[i][0])) == 0 ) {
258 return sl_type_map[i][1];
260 return sl_type_map[i][2];
267 static void writeHeader( FILE *out, glsldata_t data )
269 fprintf( out, "/*\n * This file automatically generated by genglsl from %s\n */\n", data->filename );
272 static void writeInterface( const char *filename, glsldata_t data )
274 FILE *f = fopen(filename, "wo");
276 fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
280 writeHeader( f, data );
281 fprintf( f, "#ifndef lxdream_glsl_H\n#define lxdream_glsl_H 1\n\n" );
283 fprintf( f, "typedef enum {\n" );
284 const char *last_name = NULL;
287 for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
289 shader_t shader = (shader_t)shader_ptr->data;
290 fprintf( f, " %s,\n", shader->name );
291 last_name = shader->name;
293 fprintf( f, "} shader_id;\n\n" );
295 if( last_name == NULL )
297 fprintf( f, "#define GLSL_LAST_SHADER %s\n", last_name );
298 fprintf( f, "#define GLSL_NUM_SHADERS %d\n", count );
299 fprintf( f, "#define GLSL_NO_SHADER -1\n\n" );
300 fprintf( f, "#define GLSL_VERTEX_SHADER 1\n" );
301 fprintf( f, "#define GLSL_FRAGMENT_SHADER 2\n" );
305 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
308 fprintf( f, "#define GLSL_NUM_PROGRAMS %d\n", count );
310 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
311 program_t program = program_ptr->data;
313 fprintf( f, "void glsl_use_%s();\n", program->name );
314 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
315 variable_t var = var_ptr->data;
317 fprintf( f, "void glsl_set_%s_%s(%s value); /* uniform %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name );
319 fprintf( f, "void glsl_set_%s_%s_pointer(%s ptr, GLint stride); /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
320 if( strcmp(var->type,"vec4") == 0 ) { /* Special case */
321 fprintf( f, "void glsl_set_%s_%s_vec3_pointer(%s ptr, GLint stride); /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
327 fprintf( f, "#endif /* !lxdream_glsl_H */\n" );
332 static void writeSource( const char *filename, glsldata_t data )
334 FILE *f = fopen(filename, "wo");
336 fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
340 writeHeader( f, data );
341 fprintf( f, "struct shader_def {\n int type;\n const char *source;\n};\n" );
343 fprintf( f, "const struct shader_def shader_source[] = {\n" );
345 for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
346 shader_t shader = (shader_t)shader_ptr->data;
347 fprintf( f, " {%s,\"", (shader->type == VERTEX_SHADER ? "GLSL_VERTEX_SHADER" : "GLSL_FRAGMENT_SHADER") );
348 writeCString( f, shader->body );
349 fprintf( f, "\"},\n" );
351 fprintf( f, " {GLSL_NO_SHADER,NULL}};\n\n" );
353 fprintf( f, "const int program_list[][%d] = {\n", data->max_shaders+1 );
357 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
358 program_t program = (program_t)program_ptr->data;
360 for( i=0; program->shader_names[i] != NULL; i++ ) {
361 fprintf(f, "%s,", program->shader_names[i] );
363 fprintf( f, "GLSL_NO_SHADER},\n" );
365 fprintf( f, " {GLSL_NO_SHADER}};\n" );
367 /* per-program functions */
368 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
369 program_t program = program_ptr->data;
370 fprintf( f, "\nstatic gl_program_t prog_%s_id;\n",program->name );
371 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
372 variable_t var = var_ptr->data;
373 fprintf( f, "static GLint var_%s_%s_loc;\n", program->name, var->name);
377 for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
378 program_t program = program_ptr->data;
379 fprintf( f, "\nstatic void glsl_cleanup_%s() {\n", program->name );
380 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
381 variable_t var = var_ptr->data;
382 if( !var->uniform ) {
383 fprintf( f, " glsl_disable_attrib(var_%s_%s_loc);\n", program->name, var->name );
388 fprintf( f, "\nvoid glsl_use_%s() {\n", program->name );
389 fprintf( f, " glsl_use_program(prog_%s_id);\n", program->name );
390 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
391 variable_t var = var_ptr->data;
392 if( !var->uniform ) {
393 fprintf( f, " glsl_enable_attrib(var_%s_%s_loc);\n", program->name, var->name );
396 fprintf( f, " glsl_set_cleanup_fn(glsl_cleanup_%s);\n", program->name );
400 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
401 variable_t var = var_ptr->data;
403 fprintf( f, "void glsl_set_%s_%s(%s value){ /* uniform %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name );
404 fprintf( f, " glsl_set_uniform_%s(var_%s_%s_loc,value);\n}\n", var->type, program->name, var->name );
406 fprintf( f, "void glsl_set_%s_%s_pointer(%s ptr, GLsizei stride){ /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
407 fprintf( f, " glsl_set_attrib_%s(var_%s_%s_loc,stride, ptr);\n}\n", var->type, program->name, var->name );
408 if( strcmp(var->type,"vec4") == 0 ) { /* Special case to load vec3 arrays into a vec4 */
409 fprintf( f, "void glsl_set_%s_%s_vec3_pointer(%s ptr, GLsizei stride){ /* attribute %s %s */ \n", program->name, var->name, getCType(var->type,var->uniform), var->type, var->name);
410 fprintf( f, " glsl_set_attrib_vec3(var_%s_%s_loc,stride, ptr);\n}\n", program->name, var->name );
416 fprintf( f, "\nstatic void glsl_init_programs( gl_program_t *ids ) {\n" );
417 for( program_ptr = data->programs, i=0; program_ptr != NULL; program_ptr = program_ptr->next, i++ ) {
418 program_t program = program_ptr->data;
420 fprintf( f, " prog_%s_id = ids[%d];\n\n", program->name, i );
421 for( var_ptr = program->variables; var_ptr != NULL; var_ptr = var_ptr->next ) {
422 variable_t var = var_ptr->data;
424 fprintf( f, " var_%s_%s_loc = glsl_get_uniform_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
426 fprintf( f, " var_%s_%s_loc = glsl_get_attrib_location(prog_%s_id, \"%s\");\n", program->name, var->name, program->name, var->name );
435 const char *makeExtension(const char *basename, const char *ext)
437 const char *oldext = strrchr(basename, '.');
438 if( oldext == NULL ) {
439 return g_strdup_printf("%s%s", basename, ext);
441 return g_strdup_printf("%.*s%s", (int)(oldext-basename), basename, ext);
445 int main( int argc, char *argv[] )
448 fprintf( stderr, "Usage: genglsl <glsl-source-file> [output.c [output.h]]\n");
452 glsldata_t data = readInput(argv[1]);
454 const char *sourcefile, *ifacefile;
456 sourcefile = argv[2];
458 sourcefile = makeExtension(argv[1], ".def");
464 ifacefile = makeExtension(sourcefile, ".h");
467 writeSource( sourcefile, data );
468 writeInterface( ifacefile, data );
.