naev 0.11.5
opengl_shader.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
5#include <ctype.h>
6
7#include "naev.h"
10#include "conf.h"
11#include "log.h"
12#include "ndata.h"
13#include "nstring.h"
14#include "opengl.h"
15
16#define GLSL_VERSION "#version 150\n\n"
17#define GLSL_SUBROUTINE "#define HAS_GL_ARB_shader_subroutine 1\n"
19/*
20 * Prototypes.
21 */
22static char* gl_shader_preprocess( size_t *size, const char *fbuf, size_t fbufsize, const char *prepend, const char *filename );
23static char* gl_shader_loadfile( const char *filename, size_t *size, const char *prepend );
24static GLuint gl_shader_compile( GLuint type, const char *buf,
25 GLint length, const char *filename);
26static int gl_program_link( GLuint program );
27static GLuint gl_program_make( GLuint vertex_shader, GLuint fragment_shader, GLuint geometry_shader );
28static int gl_log_says_anything( const char* log );
29
38static char* gl_shader_loadfile( const char *filename, size_t *size, const char *prepend )
39{
40 size_t fbufsize;
41 char *buf, *fbuf;
42 char path[PATH_MAX];
43
44 /* Load base file. */
45 *size = 0;
46 snprintf(path, sizeof(path), GLSL_PATH "%s", filename);
47 fbuf = ndata_read(path, &fbufsize);
48 if (fbuf == NULL) {
49 WARN( _("Shader '%s' not found."), path);
50 return NULL;
51 }
52 buf = gl_shader_preprocess( size, fbuf, fbufsize, prepend, filename );
53 free( fbuf );
54 return buf;
55}
56
67static char* gl_shader_preprocess( size_t *size, const char *fbuf, size_t fbufsize, const char *prepend, const char *filename )
68{
69 size_t i, bufsize, ibufsize;
70 char *buf, *ibuf, *newbuf;
71 char include[PATH_MAX-sizeof(GLSL_PATH)-1];
72 const char *substart, *subs, *subss, *keyword;
73 int offset, len;
74
75 /* Prepend useful information if available. */
76 if (prepend != NULL) {
77 bufsize = SDL_asprintf( &buf, "%s%s", prepend, fbuf ) + 1 /* the null byte */;
78 }
79 else {
80 bufsize = fbufsize;
81 buf = strdup(fbuf);
82 }
83
84 /* Preprocess for #include.
85 * GLSL Compilers support most preprocessor things like #define and #ifdef,
86 * however, #include is not supported. For this purpose, we do a very simple
87 * preprocessing to handle #includes. */
88 /* TODO Actually handle this by processing line-by-line so that #include
89 * can't be anywhere in the code. Extra whitespace should also be handled. */
90 keyword = "#include";
91 subs = buf;
92 while ((substart = strnstr( subs, keyword, bufsize-(subs-buf) ))!=NULL) {
93 subs = substart+strlen(keyword)+1;
94 i = 0;
95 /* Find the argument - we only support " atm. */
96 subss = strnstr( subs, "\"", bufsize-(subs-buf));
97 if (subss == NULL) {
98 WARN(_("Invalid #include syntax in '%s%s'!"), GLSL_PATH, filename);
99 continue;
100 }
101 subs = subss+1;
102 while (isprint(*subs) && (i<sizeof(include)) && (*subs!='"')) {
103 include[i++] = *subs;
104 subs++;
105 }
106 if (*subs != '"') {
107 WARN(_("Invalid #include syntax in '%s%s'!"), GLSL_PATH, filename);
108 continue;
109 }
110 include[i] = '\0'; /* Last character should be " or > */
111
112 /* Recursive loading and handling of #includes. */
113 ibuf = gl_shader_loadfile( include, &ibufsize, NULL );
114
115 /* Move data over. */
116 newbuf = malloc( bufsize+ibufsize );
117 offset = 0;
118 len = substart-buf;
119 strncpy( &newbuf[offset], buf, len );
120 offset += len;
121 len = ibufsize;
122 strncpy( &newbuf[offset], ibuf, len );
123 offset += len;
124 subs = subs+1;
125 len = bufsize-(subs-buf);
126 strncpy( &newbuf[offset], subs, bufsize-(subs-buf) );
127 offset += len;
128 newbuf[offset] = '\0';
129
130 /* Reset the pointers. */
131 subs = &newbuf[subs-buf];
132
133 /* Swap buffers. */
134 free(buf);
135 buf = newbuf;
136 bufsize = offset;
137
138 /* Clean up. */
139 free(ibuf);
140 }
141
142 *size = bufsize;
143 return buf;
144}
145
149static GLuint gl_shader_compile( GLuint type, const char *buf,
150 GLint length, const char *filename)
151{
152 GLuint shader;
153 GLint compile_status, log_length;
154
155 /* Compile it. */
156 shader = glCreateShader(type);
157 glShaderSource(shader, 1, (const char**)&buf, &length);
158 glCompileShader(shader);
159
160 /* Check for compile error */
161 glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
162 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
163 if (log_length > 0) {
164 char *log = malloc(log_length + 1);
165 glGetShaderInfoLog(shader, log_length, &log_length, log);
166 if (gl_log_says_anything( log )) {
168 WARN("compile_status==%d: %s: [[\n%s\n]]", compile_status, filename, log);
169 }
170 free(log);
171 if (compile_status == GL_FALSE)
172 shader = 0;
173 }
174 gl_checkErr();
175 return shader;
176}
177
184static int gl_program_link( GLuint program )
185{
186 GLint link_status, log_length;
187
188 glLinkProgram(program);
189
190 /* Check for linking error */
191 glGetProgramiv(program, GL_LINK_STATUS, &link_status);
192 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
193 if (log_length > 0) {
194 char *log = malloc(log_length + 1);
195 glGetProgramInfoLog(program, log_length, &log_length, log);
196 if (gl_log_says_anything( log ))
197 WARN("link_status==%d: [[\n%s\n]]", link_status, log);
198 free(log);
199 if (link_status == GL_FALSE)
200 return -1;
201 }
202
203 return 0;
204}
205
214GLuint gl_program_vert_frag( const char *vertfile, const char *fragfile, const char *geomfile )
215{
216 char *vert_str, *frag_str, prepend[STRMAX];
217 size_t vert_size, frag_size;
218 GLuint vertex_shader, fragment_shader, geometry_shader, program;
219
220 strncpy( prepend, GLSL_VERSION, sizeof(prepend)-1 );
221 if (gl_has( OPENGL_SUBROUTINES ))
222 strncat( prepend, GLSL_SUBROUTINE, sizeof(prepend)-strlen(prepend)-1 );
223
224 vert_str = gl_shader_loadfile( vertfile, &vert_size, prepend );
225 frag_str = gl_shader_loadfile( fragfile, &frag_size, prepend );
226
227 vertex_shader = gl_shader_compile( GL_VERTEX_SHADER, vert_str, vert_size, vertfile );
228 fragment_shader = gl_shader_compile( GL_FRAGMENT_SHADER, frag_str, frag_size, fragfile );
229
230 free( vert_str );
231 free( frag_str );
232
233 if (geomfile != NULL) {
234 size_t geom_size;
235 char *geom_str = gl_shader_loadfile( geomfile, &geom_size, prepend );
236 geometry_shader = gl_shader_compile( GL_GEOMETRY_SHADER, geom_str, geom_size, geomfile );
237 free( geom_str );
238 }
239 else
240 geometry_shader = 0;
241
242 program = gl_program_make( vertex_shader, fragment_shader, geometry_shader );
243 if (program==0)
244 WARN(_("Failed to link vertex shader '%s' and fragment shader '%s'!"), vertfile, fragfile);
245
246 return program;
247}
248
258GLuint gl_program_vert_frag_string( const char *vert, size_t vert_size, const char *frag, size_t frag_size )
259{
260 GLuint vertex_shader, fragment_shader;
261 char *vbuf, *fbuf;
262 size_t vlen, flen;
263
264 vbuf = gl_shader_preprocess( &vlen, vert, vert_size, NULL, NULL );
265 fbuf = gl_shader_preprocess( &flen, frag, frag_size, NULL, NULL );
266
267 /* Compile the shaders. */
268 vertex_shader = gl_shader_compile( GL_VERTEX_SHADER, vbuf, vlen, NULL );
269 fragment_shader = gl_shader_compile( GL_FRAGMENT_SHADER, fbuf, flen, NULL );
270
271 /* Clean up. */
272 free( vbuf );
273 free( fbuf );
274
275 /* Link. */
276 return gl_program_make( vertex_shader, fragment_shader, 0 );
277}
278
287static GLuint gl_program_make( GLuint vertex_shader, GLuint fragment_shader, GLuint geometry_shader )
288{
289 GLuint program = 0;
290 if (vertex_shader != 0 && fragment_shader != 0) {
291 program = glCreateProgram();
292 glAttachShader(program, vertex_shader);
293 glAttachShader(program, fragment_shader);
294 if (geometry_shader != 0)
295 glAttachShader(program, geometry_shader);
296
297 if (gl_program_link(program) == -1) {
298 /* Spec specifies 0 as failure value for glCreateProgram() */
299 program = 0;
300 }
301
302 glDeleteShader(vertex_shader);
303 glDeleteShader(fragment_shader);
304 if (geometry_shader != 0)
305 glDeleteShader(geometry_shader);
306 }
307
308 gl_checkErr();
309
310 return program;
311}
312
313void gl_uniformColour(GLint location, const glColour *c)
314{
315 glUniform4f(location, c->r, c->g, c->b, c->a);
316}
317
318void gl_uniformAColour(GLint location, const glColour *c, GLfloat a)
319{
320 glUniform4f(location, c->r, c->g, c->b, a);
321}
322
323void gl_uniformMat4( GLint location, const mat4 *m )
324{
325 glUniformMatrix4fv(location, 1, GL_FALSE, m->ptr );
326}
327
332static int gl_log_says_anything( const char* log )
333{
334 const char *junk[] = {
335 "No errors.", /* Renderer: Intel(R) HD Graphics 3000; Version: 3.1.0 - Build 9.17.10.4229 */
336 };
337 while (*log) {
338 int progress = 0;
339 if (isspace(*log)) {
340 log += 1;
341 progress = 1;
342 }
343 for (size_t i = 0; i*sizeof(junk[0]) < sizeof(junk); i++)
344 if (!strncmp(log, junk[i], strlen(junk[i]))) {
345 log += strlen(junk[i]);
346 progress = 1;
347 }
348 if (!progress)
349 return 1;
350 }
351 return 0;
352}
Header file with generic functions and naev-specifics.
#define PATH_MAX
Definition naev.h:50
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:154
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
static const double c[]
Definition rng.c:264
Definition mat4.h:10