7103fb0642c95c19f6cf04d5ff2dfb97e40f765e
[vrfileman] / src / sdr.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <stdarg.h>
6 #include <assert.h>
7 #include "opengl.h"
8
9 #if defined(unix) || defined(__unix__)
10 #include <unistd.h>
11 #include <sys/stat.h>
12 #endif  /* unix */
13
14 #include "sdr.h"
15
16 static const char *sdrtypestr(unsigned int sdrtype);
17
18 unsigned int create_vertex_shader(const char *src)
19 {
20         return create_shader(src, GL_VERTEX_SHADER);
21 }
22
23 unsigned int create_pixel_shader(const char *src)
24 {
25         return create_shader(src, GL_FRAGMENT_SHADER);
26 }
27
28 unsigned int create_tessctl_shader(const char *src)
29 {
30 #ifdef GL_TESS_CONTROL_SHADER
31         return create_shader(src, GL_TESS_CONTROL_SHADER);
32 #else
33         return 0;
34 #endif
35 }
36
37 unsigned int create_tesseval_shader(const char *src)
38 {
39 #ifdef GL_TESS_EVALUATION_SHADER
40         return create_shader(src, GL_TESS_EVALUATION_SHADER);
41 #else
42         return 0;
43 #endif
44 }
45
46 unsigned int create_geometry_shader(const char *src)
47 {
48 #ifdef GL_GEOMETRY_SHADER
49         return create_shader(src, GL_GEOMETRY_SHADER);
50 #else
51         return 0;
52 #endif
53 }
54
55 unsigned int create_shader(const char *src, unsigned int sdr_type)
56 {
57         unsigned int sdr;
58         int success, info_len;
59         char *info_str = 0;
60         GLenum err;
61
62         sdr = glCreateShader(sdr_type);
63         assert(glGetError() == GL_NO_ERROR);
64         glShaderSource(sdr, 1, &src, 0);
65         err = glGetError();
66         assert(err == GL_NO_ERROR);
67         glCompileShader(sdr);
68         assert(glGetError() == GL_NO_ERROR);
69
70         glGetShaderiv(sdr, GL_COMPILE_STATUS, &success);
71         assert(glGetError() == GL_NO_ERROR);
72         glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len);
73         assert(glGetError() == GL_NO_ERROR);
74
75         if(info_len) {
76                 if((info_str = malloc(info_len + 1))) {
77                         glGetShaderInfoLog(sdr, info_len, 0, info_str);
78                         assert(glGetError() == GL_NO_ERROR);
79                         info_str[info_len] = 0;
80                 }
81         }
82
83         if(success) {
84                 fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str);
85         } else {
86                 fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str);
87                 glDeleteShader(sdr);
88                 sdr = 0;
89         }
90
91         free(info_str);
92         return sdr;
93 }
94
95 void free_shader(unsigned int sdr)
96 {
97         glDeleteShader(sdr);
98 }
99
100 unsigned int load_vertex_shader(const char *fname)
101 {
102         return load_shader(fname, GL_VERTEX_SHADER);
103 }
104
105 unsigned int load_pixel_shader(const char *fname)
106 {
107         return load_shader(fname, GL_FRAGMENT_SHADER);
108 }
109
110 unsigned int load_tessctl_shader(const char *fname)
111 {
112 #ifdef GL_TESS_CONTROL_SHADER
113         return load_shader(fname, GL_TESS_CONTROL_SHADER);
114 #else
115         return 0;
116 #endif
117 }
118
119 unsigned int load_tesseval_shader(const char *fname)
120 {
121 #ifdef GL_TESS_EVALUATION_SHADER
122         return load_shader(fname, GL_TESS_EVALUATION_SHADER);
123 #else
124         return 0;
125 #endif
126 }
127
128 unsigned int load_geometry_shader(const char *fname)
129 {
130 #ifdef GL_GEOMETRY_SHADER
131         return load_shader(fname, GL_GEOMETRY_SHADER);
132 #else
133         return 0;
134 #endif
135 }
136
137 unsigned int load_shader(const char *fname, unsigned int sdr_type)
138 {
139         unsigned int sdr;
140         size_t filesize;
141         FILE *fp;
142         char *src;
143
144         if(!(fp = fopen(fname, "rb"))) {
145                 fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno));
146                 return 0;
147         }
148
149         fseek(fp, 0, SEEK_END);
150         filesize = ftell(fp);
151         fseek(fp, 0, SEEK_SET);
152
153         if(!(src = malloc(filesize + 1))) {
154                 fclose(fp);
155                 return 0;
156         }
157         fread(src, 1, filesize, fp);
158         src[filesize] = 0;
159         fclose(fp);
160
161         fprintf(stderr, "compiling %s shader: %s... ", sdrtypestr(sdr_type), fname);
162         sdr = create_shader(src, sdr_type);
163
164         free(src);
165         return sdr;
166 }
167
168
169 /* ---- gpu programs ---- */
170
171 unsigned int create_program(void)
172 {
173         unsigned int prog = glCreateProgram();
174         assert(glGetError() == GL_NO_ERROR);
175         return prog;
176 }
177
178 unsigned int create_program_link(unsigned int sdr0, ...)
179 {
180         unsigned int prog, sdr;
181         va_list ap;
182
183         if(!(prog = create_program())) {
184                 return 0;
185         }
186
187         attach_shader(prog, sdr0);
188         if(glGetError()) {
189                 return 0;
190         }
191
192         va_start(ap, sdr0);
193         while((sdr = va_arg(ap, unsigned int))) {
194                 attach_shader(prog, sdr);
195                 if(glGetError()) {
196                         return 0;
197                 }
198         }
199         va_end(ap);
200
201         if(link_program(prog) == -1) {
202                 free_program(prog);
203                 return 0;
204         }
205         return prog;
206 }
207
208 unsigned int create_program_load(const char *vfile, const char *pfile)
209 {
210         unsigned int vs = 0, ps = 0;
211
212         if(vfile && *vfile && !(vs = load_vertex_shader(vfile))) {
213                 return 0;
214         }
215         if(pfile && *pfile && !(ps = load_pixel_shader(pfile))) {
216                 return 0;
217         }
218         return create_program_link(vs, ps, 0);
219 }
220
221 void free_program(unsigned int sdr)
222 {
223         glDeleteProgram(sdr);
224 }
225
226 void attach_shader(unsigned int prog, unsigned int sdr)
227 {
228         int err;
229
230         if(prog && sdr) {
231                 assert(glGetError() == GL_NO_ERROR);
232                 glAttachShader(prog, sdr);
233                 if((err = glGetError()) != GL_NO_ERROR) {
234                         fprintf(stderr, "failed to attach shader %u to program %u (err: 0x%x)\n", sdr, prog, err);
235                         abort();
236                 }
237         }
238 }
239
240 int link_program(unsigned int prog)
241 {
242         int linked, info_len, retval = 0;
243         char *info_str = 0;
244
245         glLinkProgram(prog);
246         assert(glGetError() == GL_NO_ERROR);
247         glGetProgramiv(prog, GL_LINK_STATUS, &linked);
248         assert(glGetError() == GL_NO_ERROR);
249         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len);
250         assert(glGetError() == GL_NO_ERROR);
251
252         if(info_len) {
253                 if((info_str = malloc(info_len + 1))) {
254                         glGetProgramInfoLog(prog, info_len, 0, info_str);
255                         assert(glGetError() == GL_NO_ERROR);
256                         info_str[info_len] = 0;
257                 }
258         }
259
260         if(linked) {
261                 fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str);
262         } else {
263                 fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str);
264                 retval = -1;
265         }
266
267         free(info_str);
268         return retval;
269 }
270
271 int bind_program(unsigned int prog)
272 {
273         GLenum err;
274
275         glUseProgram(prog);
276         if(prog && (err = glGetError()) != GL_NO_ERROR) {
277                 /* maybe the program is not linked, try linking first */
278                 if(err == GL_INVALID_OPERATION) {
279                         if(link_program(prog) == -1) {
280                                 return -1;
281                         }
282                         glUseProgram(prog);
283                         return glGetError() == GL_NO_ERROR ? 0 : -1;
284                 }
285                 return -1;
286         }
287         return 0;
288 }
289
290 /* ugly but I'm not going to write the same bloody code over and over */
291 #define BEGIN_UNIFORM_CODE \
292         int loc, curr_prog; \
293         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \
294         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) { \
295                 return -1; \
296         } \
297         if((loc = glGetUniformLocation(prog, name)) != -1)
298
299 #define END_UNIFORM_CODE \
300         if((unsigned int)curr_prog != prog) { \
301                 bind_program(curr_prog); \
302         } \
303         return loc == -1 ? -1 : 0
304
305 int set_uniform_int(unsigned int prog, const char *name, int val)
306 {
307         BEGIN_UNIFORM_CODE {
308                 glUniform1i(loc, val);
309         }
310         END_UNIFORM_CODE;
311 }
312
313 int set_uniform_float(unsigned int prog, const char *name, float val)
314 {
315         BEGIN_UNIFORM_CODE {
316                 glUniform1f(loc, val);
317         }
318         END_UNIFORM_CODE;
319 }
320
321 int set_uniform_float2(unsigned int prog, const char *name, float x, float y)
322 {
323         BEGIN_UNIFORM_CODE {
324                 glUniform2f(loc, x, y);
325         }
326         END_UNIFORM_CODE;
327 }
328
329 int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z)
330 {
331         BEGIN_UNIFORM_CODE {
332                 glUniform3f(loc, x, y, z);
333         }
334         END_UNIFORM_CODE;
335 }
336
337 int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w)
338 {
339         BEGIN_UNIFORM_CODE {
340                 glUniform4f(loc, x, y, z, w);
341         }
342         END_UNIFORM_CODE;
343 }
344
345 int set_uniform_matrix4(unsigned int prog, const char *name, float *mat)
346 {
347         BEGIN_UNIFORM_CODE {
348                 glUniformMatrix4fv(loc, 1, GL_FALSE, mat);
349         }
350         END_UNIFORM_CODE;
351 }
352
353 int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat)
354 {
355         BEGIN_UNIFORM_CODE {
356                 glUniformMatrix4fv(loc, 1, GL_TRUE, mat);
357         }
358         END_UNIFORM_CODE;
359 }
360
361 int get_attrib_loc(unsigned int prog, const char *name)
362 {
363         int loc, curr_prog;
364
365         glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog);
366         if((unsigned int)curr_prog != prog && bind_program(prog) == -1) {
367                 return -1;
368         }
369
370         loc = glGetAttribLocation(prog, (char*)name);
371
372         if((unsigned int)curr_prog != prog) {
373                 bind_program(curr_prog);
374         }
375         return loc;
376 }
377
378 void set_attrib_float3(int attr_loc, float x, float y, float z)
379 {
380         glVertexAttrib3f(attr_loc, x, y, z);
381 }
382
383 static const char *sdrtypestr(unsigned int sdrtype)
384 {
385         switch(sdrtype) {
386         case GL_VERTEX_SHADER:
387                 return "vertex";
388         case GL_FRAGMENT_SHADER:
389                 return "pixel";
390 #ifdef GL_TESS_CONTROL_SHADER
391         case GL_TESS_CONTROL_SHADER:
392                 return "tessellation control";
393 #endif
394 #ifdef GL_TESS_EVALUATION_SHADER
395         case GL_TESS_EVALUATION_SHADER:
396                 return "tessellation evaluation";
397 #endif
398 #ifdef GL_GEOMETRY_SHADER
399         case GL_GEOMETRY_SHADER:
400                 return "geometry";
401 #endif
402
403         default:
404                 break;
405         }
406         return "<unknown>";
407 }