1 module prova.assets.shaders.shaderprogram; 2 3 import prova.graphics; 4 import prova.assets; 5 import prova.math; 6 import std.conv; 7 import std.file; 8 import std.stdio; 9 import std.string; 10 11 /// 12 enum DrawMode : uint { 13 POINTS = 0, 14 LINES = 1, 15 LINE_LOOP = 2, 16 LINE_STRIP = 3, 17 TRIANGLES = 4, 18 TRIANGLE_STRIP = 5, 19 TRIANGLE_FAN = 6 20 } 21 22 /// 23 class ShaderProgram : Asset 24 { 25 private static uint currentId = -1; 26 private uint[] shaders; 27 private int _id; 28 29 /// 30 this() 31 { 32 _id = glCreateProgram(); 33 } 34 35 /// 36 final @property int id() 37 { 38 return _id; 39 } 40 41 private void useProgram() 42 { 43 if(currentId == _id) 44 return; 45 46 currentId = _id; 47 glUseProgram(_id); 48 } 49 50 /// 51 final uint getAttribute(string name) 52 { 53 const uint location = glGetAttribLocation(_id, toStringz(name)); 54 55 assert(location != -1, "Attribute " ~ name ~ " should have a location in shader"); 56 57 return location; 58 } 59 60 /// 61 final uint getUniform(string name) 62 { 63 const uint location = glGetUniformLocation(_id, toStringz(name)); 64 65 assert(location != -1, "Uniform " ~ name ~ " should have a location in shader"); 66 67 return location; 68 } 69 70 /// 71 final void setVector2(string name, Vector2 vector) 72 { 73 useProgram(); 74 75 const uint location = getUniform(name); 76 glUniform2f(location, vector.x, vector.y); 77 } 78 79 /// 80 final void setVector3(string name, Vector3 vector) 81 { 82 useProgram(); 83 84 const uint location = getUniform(name); 85 glUniform3f(location, vector.x, vector.y, vector.z); 86 } 87 88 /// 89 final void setVector4(string name, Vector4 vector) 90 { 91 useProgram(); 92 93 const uint location = getUniform(name); 94 glUniform4f(location, vector.x, vector.y, vector.z, vector.w); 95 } 96 97 /// 98 final void setVector4(string name, Rect rect) 99 { 100 useProgram(); 101 102 const uint location = getUniform(name); 103 glUniform4f(location, rect.left, rect.top, rect.width, rect.height); 104 } 105 106 /// 107 final void setVector4(string name, Color color) 108 { 109 useProgram(); 110 111 const uint location = getUniform(name); 112 glUniform4f(location, color.r, color.g, color.b, color.a); 113 } 114 115 /// 116 final void setMatrix(string name, Matrix matrix) 117 { 118 useProgram(); 119 120 const uint location = getUniform(name); 121 glUniformMatrix4fv(location, 1, true, matrix.array[0].ptr); 122 } 123 124 /// 125 final void setTexture(int sampler, Texture texture) 126 { 127 useProgram(); 128 129 glActiveTexture(GL_TEXTURE0 + sampler); 130 texture.bind(); 131 } 132 133 /// 134 final void setTexture(int sampler, uint texture) 135 { 136 useProgram(); 137 138 glActiveTexture(GL_TEXTURE0 + sampler); 139 Texture.bind(texture); 140 } 141 142 /// 143 final void setTexture(string name, Texture texture) 144 { 145 setTexture(name, texture.id); 146 } 147 148 /// 149 final void setTexture(string name, uint texture) 150 { 151 useProgram(); 152 153 const uint location = getUniform(name); 154 glActiveTexture(location); 155 Texture.bind(texture); 156 } 157 158 /// 159 final void drawMesh(Mesh mesh, RenderTarget target, DrawMode mode) 160 { 161 target.bindFrameBuffer(); 162 useProgram(); 163 164 glBindVertexArray(mesh.VAO); 165 glDrawElements(mode, mesh.indexCount, GL_UNSIGNED_INT, null); 166 } 167 168 /// 169 final void drawMesh(Mesh mesh, uint frameBufferId, DrawMode mode) 170 { 171 RenderTarget.bindFrameBuffer(frameBufferId); 172 useProgram(); 173 174 glBindVertexArray(mesh.VAO); 175 glDrawElements(mode, mesh.indexCount, GL_UNSIGNED_INT, null); 176 } 177 178 /// 179 final void loadVertexShader(string sourceFile) 180 { 181 loadShader(GL_VERTEX_SHADER, sourceFile); 182 } 183 184 /// 185 final void loadFragmentShader(string sourceFile) 186 { 187 loadShader(GL_FRAGMENT_SHADER, sourceFile); 188 } 189 190 /// 191 final void attachVertexShader(string source) 192 { 193 compileShader(GL_VERTEX_SHADER, source); 194 } 195 196 /// 197 final void attachFragmentShader(string source) 198 { 199 compileShader(GL_FRAGMENT_SHADER, source); 200 } 201 202 /// 203 final void link() 204 { 205 glLinkProgram(_id); 206 207 //Check for errors 208 int programSuccess; 209 glGetProgramiv(_id, GL_LINK_STATUS, &programSuccess); 210 211 if(programSuccess == true) 212 return; 213 214 printProgramLog(); 215 throw new Exception("Error linking program"); 216 } 217 218 /// 219 final void loadShader(uint shaderType, string sourceFile) 220 { 221 string contents = readText(sourceFile); 222 223 compileShader(shaderType, contents); 224 } 225 226 private void compileShader(uint shaderType, string source) 227 { 228 uint shader = glCreateShader(shaderType); 229 230 const char*[] shaderSource = [toStringz(source)]; 231 232 glShaderSource(shader, 1, shaderSource.ptr, null); 233 glCompileShader(shader); 234 235 int successfulCompilation; 236 glGetShaderiv(shader, GL_COMPILE_STATUS, &successfulCompilation); 237 238 if(successfulCompilation != true) { 239 printShaderLog(shader); 240 throw new Exception("Unable to compile shader"); 241 } 242 243 glAttachShader(_id, shader); 244 245 shaders ~= shader; 246 } 247 248 /// 249 final void printProgramLog() 250 { 251 int infoLogLength = 0; 252 int maxLength = 0; 253 254 glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &maxLength); 255 256 char[] infoLog; 257 infoLog.length = maxLength; 258 259 glGetProgramInfoLog(_id, maxLength, &infoLogLength, infoLog.ptr); 260 261 if(infoLogLength > 0) 262 writeln(infoLog); 263 } 264 265 /// 266 final void printShaderLog(uint shader) 267 { 268 int infoLogLength = 0; 269 int maxLength = 0; 270 271 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); 272 273 char[] infoLog; 274 infoLog.length = maxLength; 275 276 glGetShaderInfoLog(shader, maxLength, &infoLogLength, infoLog.ptr); 277 278 if(infoLogLength > 0) 279 writeln(infoLog); 280 } 281 282 ~this() 283 { 284 foreach(uint shader; shaders) { 285 glDetachShader(_id, shader); 286 glDeleteShader(shader); 287 } 288 289 glDeleteProgram(_id); 290 } 291 }