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