1 module ws.gl.shader; 2 3 4 import 5 file = std.file, 6 std.string, 7 ws.string, 8 ws.math.vector, 9 ws.math.matrix, 10 ws.log, 11 ws.exception, 12 ws.thread.loader, 13 ws.gl.gl; 14 15 16 __gshared: 17 18 19 class Shader: Loadable { 20 protected: 21 this(){}; 22 ~this(){}; 23 24 public: 25 26 27 static Shader load(string dir, string[uint] attr, string[uint] frag=null){ 28 string id = dir ~ tostring(attr) ~ tostring(frag); 29 if(id in shaderContainer) 30 return shaderContainer[id]; 31 auto shader = prepare(id); 32 shader.start(dir); 33 shader.bindAttr(attr); 34 if(frag) 35 shader.bindFrag(frag); 36 shader.finish(); 37 return shader; 38 } 39 40 static Shader load(string name, string[uint] attr, string[uint] frag, string vertex, string fragment){ 41 string id = name ~ tostring(attr) ~ tostring(frag); 42 if(id in shaderContainer) 43 return shaderContainer[id]; 44 auto shader = prepare(id); 45 shader.start(name, vertex, fragment); 46 shader.bindAttr(attr); 47 if(frag) 48 shader.bindFrag(frag); 49 shader.finish(); 50 return shader; 51 } 52 53 54 static Shader prepare(string id){ 55 if(id in shaderContainer) 56 return shaderContainer[id]; 57 auto shader = new Shader; 58 shaderContainer[id] = shader; 59 return shader; 60 } 61 62 63 Uniform opIndex(string name){ 64 if(name in uniforms) 65 return uniforms[name]; 66 uniforms[name] = new Uniform(name, this); 67 return uniforms[name]; 68 } 69 70 71 void applyUniform(Args...)(Args args){ 72 static if(args.length){ 73 opIndex(args[0]).set(args[1]); 74 //gl.check(args[0]); 75 applyUniform(args[2..$]); 76 } 77 } 78 79 80 void use(Args...)(Args args){ 81 if(!valid || failed) 82 exception("Trying to use unfinished shader " ~ name); 83 program.use(); 84 try { 85 applyUniform(args); 86 }catch(Exception e){ 87 Log.error("Failed to activate shader: %s".format(e)); 88 } 89 } 90 91 92 void start(string folder){ 93 start( 94 folder, 95 cast(string)file.read("shaders/" ~ folder ~ "/vertex.vp"), 96 cast(string)file.read("shaders/" ~ folder ~ "/fragment.fp") 97 ); 98 } 99 100 101 void start(string name, string vertex, string fragment){ 102 assert(!valid); 103 try { 104 if(failed) return; 105 this.name = name; 106 shaderVertex = new gl.Shader(gl.shaderVertex, vertex); 107 shaderFragment = new gl.Shader(gl.shaderFragment, fragment); 108 program = new gl.Program; 109 program.attach(shaderVertex); 110 program.attach(shaderFragment); 111 }catch(Exception e){ 112 exception("Failed to load shader \"" ~ name ~ "\"", e); 113 } 114 } 115 116 117 override void finish(){ 118 assert(gl.active()); 119 if(failed) 120 return; 121 program.link(); 122 valid = true; 123 foreach(Uniform u; uniforms) 124 u.update(); 125 } 126 127 128 void bindAttr(string[uint] attrs){ 129 foreach(i, name; attrs) 130 glBindAttribLocation(program.program, i, name.toStringz); 131 } 132 133 134 void bindFrag(string[uint] frags){ 135 foreach(i, name; frags) 136 glBindFragDataLocation(program.program, i, name.toStringz); 137 } 138 139 140 gl.Program program; 141 142 gl.Shader shaderVertex; 143 gl.Shader shaderGeometry; 144 gl.Shader shaderFragment; 145 146 bool valid = false; 147 bool failed = false; 148 149 string name; 150 151 Uniform[string] uniforms; 152 153 static Shader[string] shaderContainer; 154 155 156 static class Uniform { 157 158 this(string n, Shader s){ 159 if(!s) 160 exception("Shader is null"); 161 name = n; 162 shader = s; 163 s.uniforms[n] = this; 164 if(s.valid && !s.failed) 165 update(); 166 else 167 valid = false; 168 }; 169 170 /*~this(){ 171 shader.uniforms.remove(name); 172 }*/ 173 174 void set(T)(T r){ 175 if(valid && shader && shader.valid && !shader.failed){ 176 if(gltype!T() == type) 177 shader.program.uniform(location, r); 178 else 179 throw new Exception("Wrong uniform type for %s: %s (%s), expected %s)" 180 .format(name, gltype!T(), typeid(T).toString(), type)); 181 } 182 } 183 184 protected: 185 186 template Tuple(E...){ 187 alias Tuple = E; 188 } 189 190 alias UniformTypes = Tuple!( 191 GL_FLOAT, float, 192 GL_FLOAT_VEC2, float[2], 193 GL_FLOAT_VEC3, float[3], 194 GL_FLOAT_VEC4, float[4], 195 GL_FLOAT_VEC2, Vector!2, 196 GL_FLOAT_VEC3, Vector!3, 197 GL_FLOAT_VEC4, Vector!4, 198 //GL_INT, GLint, 199 //GL_INT_VEC2, GLint[2], 200 //GL_INT_VEC3, GLint[3], 201 //GL_INT_VEC4, GLint[4], 202 GL_BOOL, GLboolean, 203 GL_FLOAT_MAT3, Matrix!(3,3), 204 GL_FLOAT_MAT4, Matrix!(4,4), 205 //GL_BOOL_VEC2, GLboolean[2], 206 //GL_BOOL_VEC3, GLboolean[3], 207 //GL_BOOL_VEC4, GLboolean[4], 208 GL_SAMPLER_2D, GLint 209 //GL_FLOAT_MAT2, GL_FLOAT_MAT3, GL_FLOAT_MAT4, 210 //GL_SAMPLER_2D, GL_SAMPLER_CUBE 211 ); 212 213 GLenum gltype(T)(){ 214 foreach(i, u; UniformTypes) 215 static if(is(u == T)) 216 return UniformTypes[i-1]; 217 assert(false, "%s not an accepted uniform type".format(typeid(T).stringof)); 218 } 219 220 int location; 221 string name; 222 Shader shader; 223 GLenum type; 224 bool valid; 225 226 void update(bool dead = false){ 227 if(!dead && shader.valid){ 228 location = shader.program.getUniform(name); 229 if(location < 0){ 230 dead = true; 231 Log.warning("Shader \"" ~ shader.name ~ "\": could not find uniform \"" ~ name ~ "\" (optimized out?)"); 232 }else{ 233 char[256] name; 234 GLsizei length; 235 GLint size; 236 glGetActiveUniform( 237 shader.program.program, location, 256, 238 &length, &size, &type, name.ptr 239 ); 240 } 241 } 242 valid = !dead; 243 } 244 } 245 246 } 247