24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * POSSIBILITY OF SUCH DAMAGE. |
25 * POSSIBILITY OF SUCH DAMAGE. |
26 */ |
26 */ |
27 |
27 |
28 #include "ascension/shader.h" |
28 #include "ascension/shader.h" |
29 #include "ascension/files.h" |
|
30 #include "ascension/error.h" |
29 #include "ascension/error.h" |
31 |
30 |
32 #include <GL/glew.h> |
31 #include <GL/glew.h> |
33 #include <string.h> |
32 #include <string.h> |
34 |
33 |
35 AscShader asc_shader_compile(unsigned int type, |
34 /** |
36 char const *code, |
35 * Compiles a shader from the given source code. |
37 int length) { |
36 * |
|
37 * The ID of the returned shader will be zero when something went wrong. |
|
38 * |
|
39 * @param type the shader type (use the GL enum) |
|
40 * @param code the source code |
|
41 * @return the compiled shader |
|
42 */ |
|
43 static unsigned asc_shader_compile(unsigned int type, char const *code) { |
38 GLuint id = glCreateShader(type); |
44 GLuint id = glCreateShader(type); |
39 if (id == 0) { |
45 if (id == 0) { |
40 asc_error("glCreateShader failed"); |
46 asc_error("glCreateShader failed"); |
41 return (AscShader) {0}; |
47 return 0; |
42 } |
48 } |
43 |
49 |
44 GLint success; |
50 GLint success; |
|
51 int length = (int) strlen(code); // must be int because of OpenGL API |
45 glShaderSource(id, 1, &code, &length); |
52 glShaderSource(id, 1, &code, &length); |
46 glCompileShader(id); |
53 glCompileShader(id); |
47 glGetShaderiv(id, GL_COMPILE_STATUS, &success); |
54 glGetShaderiv(id, GL_COMPILE_STATUS, &success); |
48 if (success) { |
55 if (success) { |
49 asc_dprintf("Shader %u compiled", id); |
56 asc_dprintf("Shader %u compiled", id); |
50 return (AscShader) {id}; |
57 return id; |
51 } else { |
58 } else { |
52 char *log = malloc(1024); |
59 char *log = malloc(1024); |
53 glGetShaderInfoLog(id, 1024, NULL, log); |
60 glGetShaderInfoLog(id, 1024, NULL, log); |
54 glDeleteShader(id); |
61 glDeleteShader(id); |
55 asc_error(log); |
62 asc_error(log); |
56 free(log); |
63 free(log); |
57 return (AscShader) {0}; |
64 return 0; |
58 } |
65 } |
59 } |
66 } |
60 |
67 |
61 AscShader asc_shader_compilef(unsigned int type, |
68 /** |
62 char const *filename) { |
69 * This function links shaders into a program. |
63 asc_file code = asc_file_mmap_rdonly(filename); |
70 * |
64 if (code.ptr == NULL) { |
71 * The ID of the returned program will be zero when something went wrong. |
65 asc_error("Mapping shader file into memory failed"); |
72 * |
66 return (AscShader) {0}; |
73 * @param shader the shader IDs to link |
67 } |
74 * @param n the number of shaders |
68 AscShader ret = asc_shader_compile(type, code.ptr, code.length); |
75 * @return a compiled program |
69 asc_file_unmap(code); |
76 */ |
70 return ret; |
77 static AscShaderProgram asc_shader_link(unsigned shader[], unsigned n) { |
71 } |
|
72 |
|
73 AscShaderProgram asc_shader_link(AscShader vertex, |
|
74 AscShader fragment) { |
|
75 if (vertex.id == 0 || fragment.id == 0) { |
|
76 asc_dprintf("Skip linking shader program - shaders have not been loaded correctly."); |
|
77 return (AscShaderProgram) {0}; |
|
78 } |
|
79 |
|
80 GLint success; |
78 GLint success; |
81 GLint id = glCreateProgram(); |
79 GLint id = glCreateProgram(); |
82 if (id <= 0) { |
80 if (id <= 0) { |
83 asc_error("glCreateProgram failed"); |
81 asc_error("glCreateProgram failed"); |
84 return (AscShaderProgram) {0}; |
82 return (AscShaderProgram) {0}; |
85 } |
83 } |
86 glAttachShader(id, vertex.id); |
84 for (unsigned i = 0; i < n; i++) { |
87 glAttachShader(id, fragment.id); |
85 glAttachShader(id, shader[i]); |
|
86 } |
88 glLinkProgram(id); |
87 glLinkProgram(id); |
89 glGetProgramiv(id, GL_LINK_STATUS, &success); |
88 glGetProgramiv(id, GL_LINK_STATUS, &success); |
90 glDetachShader(id, vertex.id); |
89 for (unsigned i = 0; i < n; i++) { |
91 glDetachShader(id, fragment.id); |
90 glDeleteShader(shader[i]); |
|
91 } |
92 if (success) { |
92 if (success) { |
93 asc_dprintf("Shader Program %u linked (vtf: %u, frag: %u)", id, vertex.id, fragment.id); |
93 asc_dprintf("Shader Program %u linked.", id); |
94 AscShaderProgram prog; |
94 AscShaderProgram prog; |
95 prog.id = id; |
95 prog.id = id; |
|
96 // TODO: maybe not every program has the same uniforms |
96 prog.model = glGetUniformLocation(id, "model"); |
97 prog.model = glGetUniformLocation(id, "model"); |
97 prog.view = glGetUniformLocation(id, "view"); |
98 prog.view = glGetUniformLocation(id, "view"); |
98 prog.projection = glGetUniformLocation(id, "projection"); |
99 prog.projection = glGetUniformLocation(id, "projection"); |
99 return prog; |
100 return prog; |
100 } else { |
101 } else { |
101 char *log = malloc(1024); |
102 char *log = malloc(1024); |
102 glGetProgramInfoLog(id, 1024, NULL, log); |
103 glGetProgramInfoLog(id, 1024, NULL, log); |
103 glDeleteShader(id); |
104 glDeleteProgram(id); |
104 asc_error(log); |
105 asc_error(log); |
105 free(log); |
106 free(log); |
106 return (AscShaderProgram) {0}; |
107 return (AscShaderProgram) {0}; |
107 } |
108 } |
108 } |
|
109 |
|
110 void asc_shader_destroy(AscShader shader) { |
|
111 if (shader.id > 0) { |
|
112 asc_dprintf("Delete Shader %u", shader.id); |
|
113 glDeleteShader(shader.id); |
|
114 } |
|
115 shader.id = 0; |
|
116 } |
109 } |
117 |
110 |
118 void asc_shader_program_destroy(AscShaderProgram program) { |
111 void asc_shader_program_destroy(AscShaderProgram program) { |
119 if (program.id > 0) { |
112 if (program.id > 0) { |
120 asc_dprintf("Delete Shader Program %u", program.id); |
113 asc_dprintf("Delete Shader Program %u", program.id); |
121 glDeleteProgram(program.id); |
114 glDeleteProgram(program.id); |
122 } |
115 } |
123 program.id = 0; |
116 program.id = 0; |
124 } |
117 } |
125 |
118 |
126 AscShaderProgram asc_shader_easy_compile_and_link( |
119 AscShaderProgram asc_shader_program_create(AscShaderCodes codes) { |
127 char const *vtxName, char const *fragName) { |
120 unsigned shader[4]; |
128 AscShader font_vtx = asc_shader_compilef(GL_VERTEX_SHADER, vtxName); |
121 unsigned n = 0; |
129 AscShader font_frag = asc_shader_compilef(GL_FRAGMENT_SHADER, fragName); |
122 if (codes.vtx) { |
130 AscShaderProgram prog = asc_shader_link(font_vtx, font_frag); |
123 shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx); |
131 asc_shader_destroy(font_vtx); |
124 } |
132 asc_shader_destroy(font_frag); |
125 if (codes.frag) { |
|
126 shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag); |
|
127 } |
|
128 const AscShaderProgram prog = asc_shader_link(shader, n); |
|
129 for (unsigned i = 0; i < n; i++) { |
|
130 glDeleteShader(shader[i]); |
|
131 } |
133 return prog; |
132 return prog; |
134 } |
133 } |