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