| 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 } |