nkeynes@405 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
nkeynes@405 | 3 | *
|
nkeynes@1130 | 4 | * Tool to take an input .glsl file and write out a corresponding .c and .h
|
nkeynes@1130 | 5 | * file based on the content. The .glsl file contains a number of shaders
|
nkeynes@1130 | 6 | * marked with either #fragment <name> or #vertex <name>
|
nkeynes@1130 | 7 | * a C file with appropriate escaping, as well as program definitions
|
nkeynes@1130 | 8 | * written as #program <name> = <shader1> <shader2> ... <shaderN>
|
nkeynes@405 | 9 | *
|
nkeynes@1130 | 10 | * Copyright (c) 2007-2010 Nathan Keynes.
|
nkeynes@405 | 11 | *
|
nkeynes@405 | 12 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@405 | 13 | * it under the terms of the GNU General Public License as published by
|
nkeynes@405 | 14 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@405 | 15 | * (at your option) any later version.
|
nkeynes@405 | 16 | *
|
nkeynes@405 | 17 | * This program is distributed in the hope that it will be useful,
|
nkeynes@405 | 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@405 | 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@405 | 20 | * GNU General Public License for more details.
|
nkeynes@405 | 21 | */
|
nkeynes@405 | 22 |
|
nkeynes@1130 | 23 | #include <assert.h>
|
nkeynes@1130 | 24 | #include <errno.h>
|
nkeynes@405 | 25 | #include <stdio.h>
|
nkeynes@405 | 26 | #include <stdlib.h>
|
nkeynes@1130 | 27 | #include <string.h>
|
nkeynes@1130 | 28 | #include <glib/gstrfuncs.h>
|
nkeynes@1130 | 29 | #include <glib/glist.h>
|
nkeynes@1130 | 30 |
|
nkeynes@1130 | 31 | #define MAX_LINE 4096
|
nkeynes@1130 | 32 | #define DEF_ALLOC_SIZE 4096
|
nkeynes@1130 | 33 | #define MAX_SHADERS 128
|
nkeynes@1130 | 34 |
|
nkeynes@1130 | 35 | typedef enum {
|
nkeynes@1130 | 36 | VERTEX_SHADER = 0,
|
nkeynes@1130 | 37 | FRAGMENT_SHADER = 1
|
nkeynes@1130 | 38 | } shader_type_t;
|
nkeynes@1130 | 39 |
|
nkeynes@1151 | 40 | typedef struct uniform {
|
nkeynes@1151 | 41 | const char *name;
|
nkeynes@1151 | 42 | const char *var_name;
|
nkeynes@1151 | 43 | } *uniform_t;
|
nkeynes@1151 | 44 |
|
nkeynes@1130 | 45 | typedef struct shader {
|
nkeynes@1130 | 46 | shader_type_t type;
|
nkeynes@1130 | 47 | const char *name;
|
nkeynes@1130 | 48 | char *body;
|
nkeynes@1151 | 49 | GList *uniforms;
|
nkeynes@1130 | 50 | } *shader_t;
|
nkeynes@1130 | 51 |
|
nkeynes@1130 | 52 | typedef struct program {
|
nkeynes@1130 | 53 | const char *name;
|
nkeynes@1130 | 54 | gchar **shader_names;
|
nkeynes@1130 | 55 | } *program_t;
|
nkeynes@1130 | 56 |
|
nkeynes@1130 | 57 | typedef struct glsldata {
|
nkeynes@1130 | 58 | const char *filename;
|
nkeynes@1130 | 59 | unsigned max_shaders;
|
nkeynes@1130 | 60 | GList *shaders;
|
nkeynes@1130 | 61 | GList *programs;
|
nkeynes@1130 | 62 | } *glsldata_t;
|
nkeynes@1130 | 63 |
|
nkeynes@1130 | 64 | static struct glsldata *readInput( const char *filename )
|
nkeynes@1130 | 65 | {
|
nkeynes@1130 | 66 | char buf[MAX_LINE];
|
nkeynes@1130 | 67 | size_t current_size = 0, current_posn = 0;
|
nkeynes@1130 | 68 | unsigned i;
|
nkeynes@1130 | 69 |
|
nkeynes@1130 | 70 | FILE *f = fopen( filename, "ro" );
|
nkeynes@1130 | 71 | if( f == NULL ) {
|
nkeynes@1130 | 72 | fprintf( stderr, "Error: unable to open input file '%s': %s\n", filename, strerror(errno) );
|
nkeynes@1130 | 73 | exit(1);
|
nkeynes@1130 | 74 | }
|
nkeynes@1130 | 75 |
|
nkeynes@1130 | 76 | shader_t shader = NULL;
|
nkeynes@1130 | 77 | glsldata_t result = malloc(sizeof(struct glsldata));
|
nkeynes@1130 | 78 | assert( result != NULL );
|
nkeynes@1130 | 79 | result->filename = strdup(filename);
|
nkeynes@1130 | 80 | result->shaders = NULL;
|
nkeynes@1130 | 81 | result->programs = NULL;
|
nkeynes@1130 | 82 | result->max_shaders = 0;
|
nkeynes@1130 | 83 |
|
nkeynes@1130 | 84 | while( fgets(buf, sizeof(buf), f) != NULL ) {
|
nkeynes@1130 | 85 | if( strlen(buf) == 0 )
|
nkeynes@1130 | 86 | continue;
|
nkeynes@1130 | 87 |
|
nkeynes@1130 | 88 | if( strncmp(buf, "#vertex ", 8) == 0 ) {
|
nkeynes@1130 | 89 | shader = malloc(sizeof(struct shader));
|
nkeynes@1130 | 90 | assert( shader != NULL );
|
nkeynes@1130 | 91 | shader->type = VERTEX_SHADER;
|
nkeynes@1130 | 92 | shader->name = strdup(g_strstrip(buf+8));
|
nkeynes@1130 | 93 | shader->body = malloc(DEF_ALLOC_SIZE);
|
nkeynes@1130 | 94 | shader->body[0] = '\0';
|
nkeynes@1151 | 95 | shader->uniforms = 0;
|
nkeynes@1130 | 96 | current_size = DEF_ALLOC_SIZE;
|
nkeynes@1130 | 97 | current_posn = 0;
|
nkeynes@1130 | 98 | result->shaders = g_list_append(result->shaders, shader);
|
nkeynes@1130 | 99 | } else if( strncmp( buf, "#fragment ", 10 ) == 0 ) {
|
nkeynes@1130 | 100 | shader = malloc(sizeof(struct shader));
|
nkeynes@1130 | 101 | assert( shader != NULL );
|
nkeynes@1130 | 102 | shader->type = FRAGMENT_SHADER;
|
nkeynes@1130 | 103 | shader->name = strdup(g_strstrip(buf+10));
|
nkeynes@1130 | 104 | shader->body = malloc(DEF_ALLOC_SIZE);
|
nkeynes@1130 | 105 | shader->body[0] = '\0';
|
nkeynes@1151 | 106 | shader->uniforms = 0;
|
nkeynes@1130 | 107 | current_size = DEF_ALLOC_SIZE;
|
nkeynes@1130 | 108 | current_posn = 0;
|
nkeynes@1130 | 109 | result->shaders = g_list_append(result->shaders, shader);
|
nkeynes@1130 | 110 | } else if( strncmp( buf, "#program ", 9 ) == 0 ) {
|
nkeynes@1130 | 111 | shader = NULL;
|
nkeynes@1130 | 112 | program_t program = malloc(sizeof(struct program));
|
nkeynes@1151 | 113 | assert( program != NULL );
|
nkeynes@1130 | 114 | char *rest = buf+9;
|
nkeynes@1130 | 115 | char *equals = strchr(rest, '=');
|
nkeynes@1130 | 116 | if( equals == NULL ) {
|
nkeynes@1130 | 117 | fprintf( stderr, "Error: invalid program line %s\n", buf );
|
nkeynes@1130 | 118 | exit(2);
|
nkeynes@1130 | 119 | }
|
nkeynes@1130 | 120 | *equals = '\0';
|
nkeynes@1130 | 121 | program->name = g_strdup(g_strstrip(rest));
|
nkeynes@1130 | 122 | program->shader_names = g_strsplit_set(g_strstrip(equals+1), " \t\r,", 0);
|
nkeynes@1130 | 123 | result->programs = g_list_append(result->programs, program);
|
nkeynes@1130 | 124 | for(i=0;program->shader_names[i] != NULL; i++ );
|
nkeynes@1130 | 125 | if( i > result->max_shaders )
|
nkeynes@1130 | 126 | result->max_shaders = i;
|
nkeynes@1151 | 127 | } else if( strncmp( buf, "#uniform ", 9 ) == 0 ) {
|
nkeynes@1151 | 128 | uniform_t uniform = malloc(sizeof(struct uniform));
|
nkeynes@1151 | 129 | assert( uniform != NULL );
|
nkeynes@1151 | 130 | if( shader == NULL ) {
|
nkeynes@1151 | 131 | fprintf( stderr, "Error: #uniform specified outside of shader: %s\n", buf );
|
nkeynes@1151 | 132 | exit(2);
|
nkeynes@1151 | 133 | }
|
nkeynes@1151 | 134 | char *rest = buf+9;
|
nkeynes@1151 | 135 | char *equals = strchr(rest, '=');
|
nkeynes@1151 | 136 | if( equals == NULL ) {
|
nkeynes@1151 | 137 | fprintf( stderr, "Error: invalid program line %s\n", buf );
|
nkeynes@1151 | 138 | exit(2);
|
nkeynes@1151 | 139 | }
|
nkeynes@1151 | 140 | *equals = '\0';
|
nkeynes@1151 | 141 | uniform->name = g_strdup(g_strstrip(rest));
|
nkeynes@1151 | 142 | uniform->var_name = g_strdup(g_strstrip(equals+1));
|
nkeynes@1151 | 143 | shader->uniforms = g_list_append(shader->uniforms, uniform);
|
nkeynes@1130 | 144 | } else if( shader != NULL ) {
|
nkeynes@1130 | 145 | size_t len = strlen(buf);
|
nkeynes@1130 | 146 | if( current_posn + len > current_size ) {
|
nkeynes@1130 | 147 | shader->body = realloc(shader->body, current_size*2);
|
nkeynes@1130 | 148 | assert( shader->body != NULL );
|
nkeynes@1130 | 149 | current_size *= 2;
|
nkeynes@1130 | 150 | }
|
nkeynes@1130 | 151 | strcpy( shader->body + current_posn, buf );
|
nkeynes@1130 | 152 | current_posn += len;
|
nkeynes@1130 | 153 | }
|
nkeynes@1130 | 154 | }
|
nkeynes@1130 | 155 |
|
nkeynes@1130 | 156 | fclose(f);
|
nkeynes@1130 | 157 | return result;
|
nkeynes@1130 | 158 | }
|
nkeynes@405 | 159 |
|
nkeynes@405 | 160 | /**
|
nkeynes@405 | 161 | * Copy input to output, quoting " characters as we go.
|
nkeynes@405 | 162 | */
|
nkeynes@1130 | 163 | static void writeCString( FILE *out, const char *str )
|
nkeynes@405 | 164 | {
|
nkeynes@1130 | 165 | const char *p = str;
|
nkeynes@405 | 166 |
|
nkeynes@1130 | 167 | while( *p != 0 ) {
|
nkeynes@1130 | 168 | if( *p == '\"' ) {
|
nkeynes@736 | 169 | fputc( '\\', out );
|
nkeynes@1130 | 170 | } else if( *p == '\n' ) {
|
nkeynes@736 | 171 | fputs( "\\n\\", out );
|
nkeynes@736 | 172 | }
|
nkeynes@1130 | 173 | fputc( *p, out );
|
nkeynes@1130 | 174 | p++;
|
nkeynes@1130 | 175 | }
|
nkeynes@1130 | 176 | }
|
nkeynes@1130 | 177 |
|
nkeynes@1151 | 178 | static uniform_t find_uniform_name( GList *list, const char *name )
|
nkeynes@1151 | 179 | {
|
nkeynes@1151 | 180 | GList *ptr;
|
nkeynes@1151 | 181 | for( ptr = list; ptr != NULL; ptr = ptr->next ) {
|
nkeynes@1151 | 182 | uniform_t data = (uniform_t)list->data;
|
nkeynes@1151 | 183 | if( strcmp( data->name, name ) == 0 )
|
nkeynes@1151 | 184 | return data;
|
nkeynes@1151 | 185 | }
|
nkeynes@1151 | 186 | return NULL;
|
nkeynes@1151 | 187 | }
|
nkeynes@1151 | 188 |
|
nkeynes@1130 | 189 | static void writeHeader( FILE *out, glsldata_t data )
|
nkeynes@1130 | 190 | {
|
nkeynes@1130 | 191 | fprintf( out, "/*\n * This file automatically generated by genglsl from %s\n */\n", data->filename );
|
nkeynes@1130 | 192 | }
|
nkeynes@1130 | 193 |
|
nkeynes@1130 | 194 | static void writeInterface( const char *filename, glsldata_t data )
|
nkeynes@1130 | 195 | {
|
nkeynes@1130 | 196 | FILE *f = fopen(filename, "wo");
|
nkeynes@1130 | 197 | if( f == NULL ) {
|
nkeynes@1130 | 198 | fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
|
nkeynes@1130 | 199 | exit(1);
|
nkeynes@1130 | 200 | }
|
nkeynes@1130 | 201 |
|
nkeynes@1130 | 202 | writeHeader( f, data );
|
nkeynes@1130 | 203 | fprintf( f, "#ifndef lxdream_glsl_H\n#define lxdream_glsl_H 1\n\n" );
|
nkeynes@1130 | 204 |
|
nkeynes@1130 | 205 | fprintf( f, "typedef enum {\n" );
|
nkeynes@1130 | 206 | const char *last_name = NULL;
|
nkeynes@1130 | 207 | int count = 0;
|
nkeynes@1151 | 208 | GList *unique_uniforms;
|
nkeynes@1151 | 209 | int uniform_count = 0;
|
nkeynes@1130 | 210 | GList *shader_ptr;
|
nkeynes@1130 | 211 | for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
|
nkeynes@1130 | 212 | count++;
|
nkeynes@1130 | 213 | shader_t shader = (shader_t)shader_ptr->data;
|
nkeynes@1130 | 214 | fprintf( f, " %s,\n", shader->name );
|
nkeynes@1130 | 215 | last_name = shader->name;
|
nkeynes@1151 | 216 | GList *uniform_ptr;
|
nkeynes@1151 | 217 | for( uniform_ptr = shader->uniforms; uniform_ptr != NULL; uniform_ptr = uniform_ptr->next ) {
|
nkeynes@1151 | 218 | uniform_t uniform = (uniform_t)uniform_ptr->data;
|
nkeynes@1151 | 219 | if( find_uniform_name(unique_uniforms, uniform->name) == NULL ) {
|
nkeynes@1151 | 220 | unique_uniforms = g_list_append( unique_uniforms, uniform );
|
nkeynes@1151 | 221 | uniform_count++;
|
nkeynes@1151 | 222 | }
|
nkeynes@1151 | 223 | }
|
nkeynes@1130 | 224 | }
|
nkeynes@1130 | 225 | fprintf( f, "} shader_id;\n\n" );
|
nkeynes@1130 | 226 |
|
nkeynes@1130 | 227 | if( last_name == NULL )
|
nkeynes@1130 | 228 | last_name = "NULL";
|
nkeynes@1130 | 229 | fprintf( f, "#define GLSL_LAST_SHADER %s\n", last_name );
|
nkeynes@1130 | 230 | fprintf( f, "#define GLSL_NUM_SHADERS %d\n", count );
|
nkeynes@1130 | 231 | fprintf( f, "#define GLSL_NO_SHADER -1\n\n" );
|
nkeynes@1130 | 232 | fprintf( f, "#define GLSL_VERTEX_SHADER 1\n" );
|
nkeynes@1130 | 233 | fprintf( f, "#define GLSL_FRAGMENT_SHADER 2\n" );
|
nkeynes@1151 | 234 | fprintf( f, "#define GLSL_NUM_UNIFORMS %d\n", uniform_count );
|
nkeynes@1130 | 235 |
|
nkeynes@1130 | 236 | fprintf( f, "typedef enum {\n" );
|
nkeynes@1130 | 237 | last_name = NULL;
|
nkeynes@1130 | 238 | count = 0;
|
nkeynes@1130 | 239 | GList *program_ptr;
|
nkeynes@1130 | 240 | for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
|
nkeynes@1130 | 241 | count++;
|
nkeynes@1130 | 242 | program_t program = (program_t)program_ptr->data;
|
nkeynes@1130 | 243 | fprintf( f, " %s,\n", program->name );
|
nkeynes@1130 | 244 | last_name = program->name;
|
nkeynes@1130 | 245 | }
|
nkeynes@1130 | 246 | fprintf( f, "} program_id;\n\n" );
|
nkeynes@1130 | 247 |
|
nkeynes@1130 | 248 | if( last_name == NULL )
|
nkeynes@1130 | 249 | last_name = "NULL";
|
nkeynes@1130 | 250 | fprintf( f, "#define GLSL_LAST_PROGRAM %s\n", last_name );
|
nkeynes@1130 | 251 | fprintf( f, "#define GLSL_NUM_PROGRAMS %d\n", count );
|
nkeynes@1130 | 252 | fprintf( f, "#define GLSL_NO_PROGRAM -1\n\n" );
|
nkeynes@1130 | 253 |
|
nkeynes@1130 | 254 | fprintf( f, "int glsl_load_programs();\n" );
|
nkeynes@1130 | 255 | fprintf( f, "void glsl_use_program_id( program_id );\n" );
|
nkeynes@1130 | 256 |
|
nkeynes@1130 | 257 | fprintf( f, "#endif /* !lxdream_glsl_H */\n" );
|
nkeynes@1130 | 258 |
|
nkeynes@1130 | 259 | fclose(f);
|
nkeynes@1130 | 260 | }
|
nkeynes@1130 | 261 |
|
nkeynes@1130 | 262 | static void writeSource( const char *filename, glsldata_t data )
|
nkeynes@1130 | 263 | {
|
nkeynes@1130 | 264 | FILE *f = fopen(filename, "wo");
|
nkeynes@1130 | 265 | if( f == NULL ) {
|
nkeynes@1130 | 266 | fprintf( stderr, "Error: Unable to write interface file '%s': %s\n", filename, strerror(errno) );
|
nkeynes@1130 | 267 | exit(1);
|
nkeynes@1130 | 268 | }
|
nkeynes@1130 | 269 |
|
nkeynes@1130 | 270 | writeHeader( f, data );
|
nkeynes@1130 | 271 | fprintf( f, "struct shader_def {\n int type;\n const char *source;\n};\n" );
|
nkeynes@1130 | 272 |
|
nkeynes@1130 | 273 | fprintf( f, "const struct shader_def shader_source[] = {\n" );
|
nkeynes@1130 | 274 | GList *shader_ptr;
|
nkeynes@1130 | 275 | for( shader_ptr = data->shaders; shader_ptr != NULL; shader_ptr = shader_ptr->next ) {
|
nkeynes@1130 | 276 | shader_t shader = (shader_t)shader_ptr->data;
|
nkeynes@1130 | 277 | fprintf( f, " {%s,\"", (shader->type == VERTEX_SHADER ? "GLSL_VERTEX_SHADER" : "GLSL_FRAGMENT_SHADER") );
|
nkeynes@1130 | 278 | writeCString( f, shader->body );
|
nkeynes@1130 | 279 | fprintf( f, "\"},\n" );
|
nkeynes@1130 | 280 | }
|
nkeynes@1130 | 281 | fprintf( f, " {GLSL_NO_SHADER,NULL}};\n\n" );
|
nkeynes@1130 | 282 |
|
nkeynes@1130 | 283 | fprintf( f, "const int program_list[][%d] = {\n", data->max_shaders+1 );
|
nkeynes@1130 | 284 | GList *program_ptr;
|
nkeynes@1130 | 285 | unsigned i;
|
nkeynes@1130 | 286 | for( program_ptr = data->programs; program_ptr != NULL; program_ptr = program_ptr->next ) {
|
nkeynes@1130 | 287 | program_t program = (program_t)program_ptr->data;
|
nkeynes@1130 | 288 | fprintf( f, " {" );
|
nkeynes@1130 | 289 | for( i=0; program->shader_names[i] != NULL; i++ ) {
|
nkeynes@1130 | 290 | fprintf(f, "%s,", program->shader_names[i] );
|
nkeynes@1130 | 291 | }
|
nkeynes@1130 | 292 | fprintf( f, "GLSL_NO_SHADER},\n" );
|
nkeynes@1130 | 293 | }
|
nkeynes@1130 | 294 | fprintf( f, " {GLSL_NO_SHADER}};\n" );
|
nkeynes@1130 | 295 |
|
nkeynes@1130 | 296 | fclose(f);
|
nkeynes@1130 | 297 | }
|
nkeynes@1130 | 298 |
|
nkeynes@1130 | 299 | const char *makeExtension(const char *basename, const char *ext)
|
nkeynes@1130 | 300 | {
|
nkeynes@1130 | 301 | const char *oldext = strrchr(basename, '.');
|
nkeynes@1130 | 302 | if( oldext == NULL ) {
|
nkeynes@1130 | 303 | return g_strdup_printf("%s%s", basename, ext);
|
nkeynes@1130 | 304 | } else {
|
nkeynes@1130 | 305 | return g_strdup_printf("%.*s%s", oldext-basename, basename, ext);
|
nkeynes@405 | 306 | }
|
nkeynes@405 | 307 | }
|
nkeynes@405 | 308 |
|
nkeynes@405 | 309 | int main( int argc, char *argv[] )
|
nkeynes@405 | 310 | {
|
nkeynes@1130 | 311 | if( argc < 2 ) {
|
nkeynes@1130 | 312 | fprintf( stderr, "Usage: genglsl <glsl-source-file> [output.c [output.h]]\n");
|
nkeynes@736 | 313 | exit(1);
|
nkeynes@405 | 314 | }
|
nkeynes@405 | 315 |
|
nkeynes@1130 | 316 | glsldata_t data = readInput(argv[1]);
|
nkeynes@1130 | 317 |
|
nkeynes@1130 | 318 | const char *sourcefile, *ifacefile;
|
nkeynes@1130 | 319 | if( argc > 2 ) {
|
nkeynes@1130 | 320 | sourcefile = argv[2];
|
nkeynes@1130 | 321 | } else {
|
nkeynes@1130 | 322 | sourcefile = makeExtension(argv[1], ".def");
|
nkeynes@405 | 323 | }
|
nkeynes@405 | 324 |
|
nkeynes@1130 | 325 | if( argc > 3 ) {
|
nkeynes@1130 | 326 | ifacefile = argv[3];
|
nkeynes@1130 | 327 | } else {
|
nkeynes@1130 | 328 | ifacefile = makeExtension(sourcefile, ".h");
|
nkeynes@405 | 329 | }
|
nkeynes@405 | 330 |
|
nkeynes@1130 | 331 | writeSource( sourcefile, data );
|
nkeynes@1130 | 332 | writeInterface( ifacefile, data );
|
nkeynes@405 | 333 | return 0;
|
nkeynes@405 | 334 | }
|