naev 0.12.5
opengl_shader.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
5#include <ctype.h>
6
7#include "naev.h"
9
10#include "log.h"
11#include "ndata.h"
12#include "nstring.h"
13#include "opengl.h"
14
15/*
16 * Prototypes.
17 */
18static char *gl_shader_preprocess( size_t *size, const char *fbuf,
19 size_t fbufsize, const char *prepend,
20 const char *filename );
21static char *gl_shader_loadfile( const char *filename, size_t *size,
22 const char *prepend );
23static GLuint gl_shader_compile( GLuint type, const char *buf, GLint length,
24 const char *filename );
25static int gl_program_link( GLuint program );
26static GLuint gl_program_make( GLuint vertex_shader, GLuint fragment_shader,
27 GLuint geometry_shader );
28static int gl_log_says_anything( const char *log );
29
39static char *gl_shader_loadfile( const char *filename, size_t *size,
40 const char *prepend )
41{
42 size_t fbufsize;
43 char *buf, *fbuf;
44 char path[PATH_MAX];
45
46 /* Load base file. */
47 *size = 0;
48 snprintf( path, sizeof( path ), GLSL_PATH "%s", filename );
49 fbuf = ndata_read( path, &fbufsize );
50 if ( fbuf == NULL ) {
51 WARN( _( "Shader '%s' not found." ), path );
52 return NULL;
53 }
54 buf = gl_shader_preprocess( size, fbuf, fbufsize, prepend, filename );
55 free( fbuf );
56 return buf;
57}
58
70static char *gl_shader_preprocess( size_t *size, const char *fbuf,
71 size_t fbufsize, const char *prepend,
72 const char *filename )
73{
74 size_t i, bufsize, ibufsize;
75 char *buf, *ibuf, *newbuf;
76 char include[PATH_MAX - sizeof( GLSL_PATH ) - 1];
77 const char *substart, *subs, *subss, *keyword;
78 int offset, len;
79
80 /* Prepend useful information if available. */
81 if ( prepend != NULL ) {
82 bufsize =
83 SDL_asprintf( &buf, "%s%s", prepend, fbuf ) + 1 /* the null byte */;
84 } else {
85 bufsize = fbufsize;
86 buf = strdup( fbuf );
87 }
88
89 /* Preprocess for #include.
90 * GLSL Compilers support most preprocessor things like #define and #ifdef,
91 * however, #include is not supported. For this purpose, we do a very simple
92 * preprocessing to handle #includes. */
93 /* TODO Actually handle this by processing line-by-line so that #include
94 * can't be anywhere in the code. Extra whitespace should also be handled. */
95 keyword = "#include";
96 subs = buf;
97 while ( ( substart = strnstr( subs, keyword, bufsize - ( subs - buf ) ) ) !=
98 NULL ) {
99 subs = substart + strlen( keyword ) + 1;
100 /* Allow whitespace infront of #include, but not other characters. */
101 int whitespaceonly = 1;
102 int off = 0;
103 while ( &substart[off] != buf ) {
104 off -= 1;
105 if ( ( substart[off] == '\n' ) || ( substart[off] == '\r' ) )
106 break;
107 else if ( isspace( substart[off] ) )
108 continue;
109 else {
110 whitespaceonly = 0;
111 break;
112 }
113 }
114 if ( !whitespaceonly ) {
115 continue;
116 }
117 i = 0;
118 /* Find the argument - we only support " atm. */
119 subss = strnstr( subs, "\"", bufsize - ( subs - buf ) );
120 if ( subss == NULL ) {
121 WARN( _( "Invalid #include syntax in '%s%s'!" ), GLSL_PATH, filename );
122 continue;
123 }
124 subs = subss + 1;
125 while ( isprint( *subs ) && ( i < sizeof( include ) ) &&
126 ( *subs != '"' ) ) {
127 include[i++] = *subs;
128 subs++;
129 }
130 if ( *subs != '"' ) {
131 WARN( _( "Invalid #include syntax in '%s%s'!" ), GLSL_PATH, filename );
132 continue;
133 }
134 include[i] = '\0'; /* Last character should be " or > */
135
136 /* Recursive loading and handling of #includes. */
137 ibuf = gl_shader_loadfile( include, &ibufsize, NULL );
138
139 /* Move data over. */
140 newbuf = malloc( bufsize + ibufsize );
141 offset = 0;
142 len = substart - buf;
143 strncpy( &newbuf[offset], buf, len );
144 offset += len;
145 len = ibufsize;
146 strncpy( &newbuf[offset], ibuf, len );
147 offset += len;
148 subs = subs + 1;
149 len = bufsize - ( subs - buf );
150 strncpy( &newbuf[offset], subs, bufsize - ( subs - buf ) );
151 offset += len;
152 newbuf[offset] = '\0';
153
154 /* Reset the pointers. */
155 subs = &newbuf[subs - buf];
156
157 /* Swap buffers. */
158 free( buf );
159 buf = newbuf;
160 bufsize = offset;
161
162 /* Clean up. */
163 free( ibuf );
164 }
165
166 *size = bufsize;
167 return buf;
168}
169
173static GLuint gl_shader_compile( GLuint type, const char *buf, GLint length,
174 const char *filename )
175{
176 GLuint shader;
177 GLint compile_status, log_length;
178
179 /* Compile it. */
180 shader = glCreateShader( type );
181 glShaderSource( shader, 1, (const char **)&buf, &length );
182 glCompileShader( shader );
183
184 /* Check for compile error */
185 glGetShaderiv( shader, GL_COMPILE_STATUS, &compile_status );
186 glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &log_length );
187 if ( log_length > 0 ) {
188 char *log = malloc( log_length + 1 );
189 glGetShaderInfoLog( shader, log_length, &log_length, log );
190 if ( gl_log_says_anything( log ) ) {
191 logprintf( stderr, 0, _( "File: %s" ), filename );
193 WARN( "compile_status==%d: %s: [[\n%s\n]]", compile_status, filename,
194 log );
195 }
196 free( log );
197 if ( compile_status == GL_FALSE )
198 shader = 0;
199 }
200 gl_checkErr();
201 return shader;
202}
203
210static int gl_program_link( GLuint program )
211{
212 GLint link_status, log_length;
213
214 glLinkProgram( program );
215
216 /* Check for linking error */
217 glGetProgramiv( program, GL_LINK_STATUS, &link_status );
218 glGetProgramiv( program, GL_INFO_LOG_LENGTH, &log_length );
219 if ( log_length > 0 ) {
220 char *log = malloc( log_length + 1 );
221 glGetProgramInfoLog( program, log_length, &log_length, log );
222 if ( gl_log_says_anything( log ) )
223 WARN( "link_status==%d: [[\n%s\n]]", link_status, log );
224 free( log );
225 if ( link_status == GL_FALSE )
226 return -1;
227 }
228
229 return 0;
230}
231
232GLuint gl_program_vert_frag_geom( const char *vert, const char *frag,
233 const char *geom )
234{
235 return gl_program_backend( vert, frag, geom, NULL );
236}
237
238GLuint gl_program_vert_frag( const char *vert, const char *frag )
239{
240 return gl_program_backend( vert, frag, NULL, NULL );
241}
242
251GLuint gl_program_backend( const char *vertfile, const char *fragfile,
252 const char *geomfile, const char *prependtext )
253{
254 char *vert_str, *frag_str, prepend[STRMAX];
255 size_t vert_size, frag_size;
256 GLuint vertex_shader, fragment_shader, geometry_shader, program;
257
258 snprintf( prepend, sizeof( prepend ) - 1,
259 "#version %d\n\n#define GLSL_VERSION %d\n", gl_screen.glsl,
260 gl_screen.glsl );
261 if ( gl_has( OPENGL_SUBROUTINES ) )
262 strncat( prepend, "#define HAS_GL_ARB_shader_subroutine 1\n",
263 sizeof( prepend ) - strlen( prepend ) - 1 );
264 if ( prependtext != NULL )
265 strncat( prepend, prependtext,
266 sizeof( prepend ) - strlen( prepend ) - 1 );
267
268 vert_str = gl_shader_loadfile( vertfile, &vert_size, prepend );
269 frag_str = gl_shader_loadfile( fragfile, &frag_size, prepend );
270
271 vertex_shader =
272 gl_shader_compile( GL_VERTEX_SHADER, vert_str, vert_size, vertfile );
273 fragment_shader =
274 gl_shader_compile( GL_FRAGMENT_SHADER, frag_str, frag_size, fragfile );
275
276 free( vert_str );
277 free( frag_str );
278
279 if ( geomfile != NULL ) {
280 size_t geom_size;
281 char *geom_str = gl_shader_loadfile( geomfile, &geom_size, prepend );
282 geometry_shader =
283 gl_shader_compile( GL_GEOMETRY_SHADER, geom_str, geom_size, geomfile );
284 free( geom_str );
285 } else
286 geometry_shader = 0;
287
288 program = gl_program_make( vertex_shader, fragment_shader, geometry_shader );
289 if ( program == 0 )
290 WARN( _( "Failed to link vertex shader '%s' and fragment shader '%s'!" ),
291 vertfile, fragfile );
292
293 return program;
294}
295
305GLuint gl_program_vert_frag_string( const char *vert, size_t vert_size,
306 const char *frag, size_t frag_size )
307{
308 GLuint vertex_shader, fragment_shader;
309 char *vbuf, *fbuf;
310 size_t vlen, flen;
311
312 vbuf = gl_shader_preprocess( &vlen, vert, vert_size, NULL, NULL );
313 fbuf = gl_shader_preprocess( &flen, frag, frag_size, NULL, NULL );
314
315 /* Compile the shaders. */
316 vertex_shader = gl_shader_compile( GL_VERTEX_SHADER, vbuf, vlen, NULL );
317 fragment_shader = gl_shader_compile( GL_FRAGMENT_SHADER, fbuf, flen, NULL );
318
319 /* Clean up. */
320 free( vbuf );
321 free( fbuf );
322
323 /* Link. */
324 return gl_program_make( vertex_shader, fragment_shader, 0 );
325}
326
335static GLuint gl_program_make( GLuint vertex_shader, GLuint fragment_shader,
336 GLuint geometry_shader )
337{
338 GLuint program = 0;
339 if ( vertex_shader != 0 && fragment_shader != 0 ) {
340 program = glCreateProgram();
341 glAttachShader( program, vertex_shader );
342 glAttachShader( program, fragment_shader );
343 if ( geometry_shader != 0 )
344 glAttachShader( program, geometry_shader );
345
346 if ( gl_program_link( program ) == -1 ) {
347 /* Spec specifies 0 as failure value for glCreateProgram() */
348 program = 0;
349 }
350
351 glDeleteShader( vertex_shader );
352 glDeleteShader( fragment_shader );
353 if ( geometry_shader != 0 )
354 glDeleteShader( geometry_shader );
355 }
356
357 gl_checkErr();
358
359 return program;
360}
361
362void gl_uniformColour( GLint location, const glColour *c )
363{
364 glUniform4f( location, c->r, c->g, c->b, c->a );
365}
366
367void gl_uniformAColour( GLint location, const glColour *c, GLfloat a )
368{
369 glUniform4f( location, c->r, c->g, c->b, a );
370}
371
372void gl_uniformMat4( GLint location, const mat4 *m )
373{
374 glUniformMatrix4fv( location, 1, GL_FALSE, m->ptr );
375}
376
382static int gl_log_says_anything( const char *log )
383{
384 const char *junk[] = {
385 "No errors.", /* Renderer: Intel(R) HD Graphics 3000; Version: 3.1.0 -
386 Build 9.17.10.4229 */
387 };
388 while ( *log ) {
389 int progress = 0;
390 if ( isspace( *log ) ) {
391 log += 1;
392 progress = 1;
393 }
394 for ( size_t i = 0; i * sizeof( junk[0] ) < sizeof( junk ); i++ )
395 if ( !strncmp( log, junk[i], strlen( junk[i] ) ) ) {
396 log += strlen( junk[i] );
397 progress = 1;
398 }
399 if ( !progress )
400 return 1;
401 }
402 return 0;
403}
int logprintf(FILE *stream, int newline, const char *fmt,...)
Like fprintf, but automatically teed to log files (and line-terminated if newline is true).
Definition log.c:116
Header file with generic functions and naev-specifics.
#define PATH_MAX
Definition naev.h:57
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
void print_with_line_numbers(const char *str)
Prints to stderr with line numbers.
Definition nstring.c:175
char * strnstr(const char *haystack, const char *needle, size_t size)
A bounded version of strstr. Conforms to BSD semantics.
Definition nstring.c:26
glInfo gl_screen
Definition opengl.c:47
static const double c[]
Definition rng.c:256
Definition mat4.h:12