1 /* 2 Copyright (c) 2013 Andrey Penechko 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license the "Software" to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module anchovy.graphics.shaderprogram; 30 31 import std.exception; 32 import std.stdio; 33 import std.string; 34 import std.variant; 35 36 public import derelict.opengl3.gl3; 37 38 39 struct Uniform 40 { 41 uint location; 42 uint type; 43 uint num; 44 float[4] lastValue; 45 } 46 47 class ShaderParameters 48 { 49 50 } 51 52 53 class ShaderProgram 54 { 55 56 public: 57 58 /++ 59 + Creates an empty shader program in OpenGL state. 60 + OpenGL must be initialized before creating. 61 +/ 62 this() 63 { 64 programHandler = glCreateProgram(); 65 } 66 67 this(in string vertShaderSource, in string fragShaderSource) 68 { 69 programHandler = glCreateProgram(); 70 71 attachShader(GL_VERTEX_SHADER, vertShaderSource); 72 attachShader(GL_FRAGMENT_SHADER, fragShaderSource); 73 } 74 75 ~this() 76 { 77 glDeleteProgram(programHandler); 78 } 79 80 /++ 81 + Binds shader to the current OpenGL state. 82 + Must be used only after attaching all the shaders and compilation. 83 + If compilation fails, no shader will be used. 84 +/ 85 void bind() 86 { 87 glUseProgram(programHandler); 88 } 89 90 static void unbind() 91 { 92 glUseProgram(0); 93 } 94 95 /++ 96 + Compiles shader source of the given type and attaches it to the shader program. 97 + If shader compilation fails, writes compilation info to the error log of the shader program (_errorLog) 98 + 99 + Params: 100 + shaderType = GL_VERTEX_SHADER or GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER 101 + shaderSource = the string that contains the source of given shader 102 +/ 103 void attachShader(in GLenum shaderType, in string shaderSource) 104 { 105 106 GLuint shader = glCreateShader(shaderType); 107 108 const char* fileData=toStringz(shaderSource); 109 glShaderSource(shader, 1, &fileData, null); 110 111 glCompileShader(shader); 112 113 int status, length; 114 glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 115 116 if (status == GL_FALSE) 117 { 118 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); 119 120 char[] error=new char[length]; 121 glGetShaderInfoLog(shader, length, null, cast(char*)error); 122 123 string shaderTypeString; 124 switch(shaderType) 125 { 126 case GL_VERTEX_SHADER: shaderTypeString = "vertex"; break; 127 case GL_GEOMETRY_SHADER: shaderTypeString = "geometry"; break; 128 case GL_FRAGMENT_SHADER: shaderTypeString = "fragment"; break; 129 default: break; 130 } 131 132 _errorLog ~= "Compile failure in " ~ shaderTypeString ~ " shader:\n" ~ error~"\n"; 133 } 134 glAttachShader(programHandler, shader); 135 } 136 137 /++ 138 + Compiles shaderProgram. If any error occurs, writes log to _errorLog. 139 + After compile detaches shaders from program. 140 + See_Also: errorLog 141 +/ 142 bool compile() 143 { 144 145 glLinkProgram(programHandler); 146 147 GLint linkStatus; 148 149 glGetProgramiv(programHandler, GL_LINK_STATUS, &linkStatus); 150 151 /++ 152 + Detaches all the shaders after compilation 153 +/ 154 scope(exit) 155 { 156 GLuint[3] shaders; 157 GLsizei count; 158 159 glGetAttachedShaders(programHandler, 3, &count, cast(uint*)shaders); 160 161 for( uint i=0; i<count; ++i ) 162 { 163 glDetachShader(programHandler, shaders[i]); 164 glDeleteShader(shaders[i]); 165 } 166 } 167 168 if ( linkStatus == GL_FALSE ) 169 { 170 GLint infoLogLength; 171 glGetProgramiv(programHandler, GL_INFO_LOG_LENGTH, &infoLogLength); 172 173 char[] strInfoLog = new char[infoLogLength]; 174 glGetProgramInfoLog(programHandler, infoLogLength, null, cast(char*)strInfoLog); 175 _errorLog ~= "Linker failure: " ~ strInfoLog ~ "\n"; 176 177 return false; 178 } 179 180 int total = -1; 181 glGetProgramiv(programHandler, GL_ACTIVE_UNIFORMS, &total); 182 for(int i=0; i<total; ++i) 183 { 184 int name_len = -1, num = -1; 185 GLenum type = GL_ZERO; 186 char[100] name; 187 glGetActiveUniform(programHandler, i, name.length-1, 188 &name_len, &num, &type, name.ptr); 189 190 name[name_len] = 0; 191 GLuint location = glGetUniformLocation(programHandler, name.ptr); 192 string uniformName = name[0..name_len].idup; 193 uniforms[uniformName] = Uniform(location, type, num); 194 } 195 // Magic bug. Cannot remove this statement 196 writef("",uniforms); 197 uniforms.rehash; 198 return true; 199 } 200 201 /++ 202 + Returns: empty string if the shader was compiled without errors, or error log otherwise. 203 + Examples: 204 + --- 205 + if ( !myShaderProgram.compile ){ 206 + writeln( myShaderProgram.errorLog ); //Writes error log to console 207 + } 208 + --- 209 + See_Also: compile 210 +/ 211 @property string errorLog() 212 { 213 return _errorLog; 214 } 215 216 private static string getParType(T)() 217 { 218 string res; 219 if (is(T : int) || is(T : byte) || is(T : short)) 220 { 221 res = "i"; 222 } 223 if (is(T : uint) || is(T : ubyte) || is(T : ushort)) 224 { 225 res = "ui"; 226 } 227 else if (is(T : float)) 228 { 229 res = "f"; 230 } 231 return res; 232 } 233 234 private static uint getGlType(T)() 235 { 236 uint type; 237 static if (is(T : byte)) type = GL_BYTE; 238 else if (is(T : ubyte)) type = GL_UNSIGNED_BYTE; 239 else if (is(T : short)) type = GL_SHORT; 240 else if (is(T : ushort)) type = GL_UNSIGNED_SHORT; 241 else if (is(T : int)) type = GL_INT; 242 else if (is(T : uint)) type = GL_UNSIGNED_INT; 243 else if (is(T : float)) type = GL_FLOAT; 244 else if (is(T : double)) type = GL_DOUBLE; 245 return type; 246 } 247 //glUniform{1|2|3|4}{f|i|ui}v 248 void setUniform(T)(string uniName, T t1) 249 { 250 Uniform* uni = &getUniform(uniName); 251 if (uni.lastValue[0] != t1) 252 { 253 uni.lastValue[0] = t1; 254 mixin("glUniform1"~getParType!T~"(uni.location, t1);"); 255 } 256 /+for(uint i = 0; i<t.length-1; ++i) 257 { 258 params ~= "t["~to!string(i)~"], "; 259 } 260 params ~= "t["~to!string(t.length-1)~"]"; 261 //Uniform* uni = uniName in uniforms; 262 assert(uni !is null, "Unknown uniform name"); 263 assert(uni.type == getGlType(t[0])(), "Wrong uniform type"); 264 265 writeln("glUniform1" ~ 266 getParType!(typeof(t[0]))() ~ 267 "(" ~ 268 "uniName, " ~ 269 params ~ 270 ");");+/ 271 } 272 273 void setUniform2(T)(string uniName, T t1, T t2) 274 { 275 Uniform* uni = &getUniform(uniName); 276 if (uni.lastValue[0..2] != [t1,t2]) 277 { 278 uni.lastValue[0..2] = [t1,t2]; 279 mixin("glUniform2"~getParType!T~"(uni.location, t1, t2);"); 280 } 281 } 282 283 void setUniform3(T)(string uniName, T t1, T t2, T t3) 284 { 285 Uniform* uni = &getUniform(uniName); 286 if (uni.lastValue[0..3] != [t1, t2, t3]) 287 { 288 uni.lastValue[0..3] = [t1, t2, t3]; 289 mixin("glUniform3"~getParType!T~"(uni.location, t1, t2, t3);"); 290 } 291 } 292 293 void setUniform4(T)(string uniName, T t1, T t2, T t3, T t4) 294 { 295 Uniform* uni = &getUniform(uniName); 296 if (uni.lastValue[0..4] != [t1, t2, t3, t4]) 297 { 298 uni.lastValue[0..4] = [t1, t2, t3, t4]; 299 mixin("glUniform4"~getParType!T~"(uni.location, t1, t2, t3, t4);"); 300 } 301 } 302 303 uint getUniformLoc(string name) 304 { 305 //assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'"); 306 return uniforms[name].location; 307 } 308 309 ref Uniform getUniform(string name) 310 { 311 //assert((name in uniforms) !is null, "Unknown uniform: '"~name~"'"); 312 return uniforms[name]; 313 } 314 315 /+ 316 + Returns: shader program pointer of type GLuint, the result of glCreateProgram(); 317 + Can be used in some OpenGL functions. 318 + Examples: 319 + --- 320 + myShaderProgram = new CShaderProgram(); 321 + --- 322 + ... 323 + --- 324 + GLuint myShaderAttribute = glGetUniformLocation( myShaderProgram.program, "myShaderAttribute" ); 325 + --- 326 +/ 327 @property GLuint program(){ return programHandler; } 328 329 //void setUniform(T)(string unifName, 330 //TODO: Add methods for uniforms setting 331 332 private: 333 334 /++ 335 + The result of glCreateProgram() get in constructor 336 + 337 + See_Also: this 338 +/ 339 GLuint programHandler = 0; 340 Uniform[string] uniforms; 341 342 /++ 343 + The result of: 344 + --- 345 + glGetShaderiv( _shaderPointer, GL_COMPILE_STATUS, _compileStatus ); 346 + --- 347 +/ 348 GLuint _compileStatus; 349 350 /++ 351 + Contains full compile log if compile error. 352 +/ 353 string _errorLog; 354 355 } 356 357 static const string[uint] glTypes; 358 359 static this() 360 { 361 glTypes = [ 362 0x1400 : "GL_BYTE", 363 0x1401 : "GL_UNSIGNED_BYTE", 364 0x1402 : "GL_SHORT", 365 0x1403 : "GL_UNSIGNED_SHORT", 366 0x1404 : "GL_INT", 367 0x1405 : "GL_UNSIGNED_INT", 368 0x1406 : "GL_FLOAT", 369 0x140A : "GL_DOUBLE",]; 370 glTypes.rehash; 371 }