1 2 module ws.gl.material; 3 4 import 5 derelict.opengl3.gl3, 6 std.file, 7 std.utf, 8 std.conv, 9 10 ws.io, 11 ws.string, 12 ws.exception, 13 ws.decode, 14 ws.gl.gl, 15 ws.gl.shader, 16 ws.gl.texture, 17 ws.thread.loader, 18 ws.math.vector, 19 ws.file.obj; 20 21 22 __gshared: 23 24 25 const string VERTEX_BASE = " 26 #version 130 27 28 uniform mat4 mvp; 29 uniform mat4 world; 30 31 in vec4 vertex; 32 33 //uniform float far; 34 //out float logz; 35 36 %s 37 38 void main(){ 39 %s 40 gl_Position = mvp*vertex; 41 //gl_Position.z = log2(max(1e-6, 1.0 + gl_Position.w)) * (2.0 / log2(far + 1.0)) - 1.0; 42 //gl_Position.z *= gl_Position.w; 43 //logz = log2(1.0 + gl_Position.w) / log2(far + 1); 44 }"; 45 46 47 const string FRAGMENT_BASE = " 48 #version 130 49 50 out vec4 outDiffuse; 51 out vec4 outNormal; 52 out vec4 outLightData; 53 54 //in float logz; 55 56 vec4 calcNormal(); 57 vec4 calcDiffuse(); 58 vec4 calcLightData(); 59 60 void main(){ 61 outDiffuse = calcDiffuse(); 62 if(outDiffuse.a < 0) 63 discard; 64 outNormal = calcNormal(); 65 outLightData = calcLightData(); 66 //gl_FragDepth = logz; 67 }"; 68 69 70 class DeferredMaterial: Loadable { 71 72 enum { 73 diffuse, 74 normal, 75 lightInfo 76 } 77 78 enum targetTextures = [ 79 diffuse, 80 normal, 81 lightInfo 82 ]; 83 84 this(string name, string[string] vp=null, string[] fp=null, int[string] attr=null){ 85 this.name = name; 86 shader = new MaterialShader; 87 if(!vp) 88 vp = vp.init; 89 if(!fp) 90 fp = fp.init; 91 foreach(n,f; vp) 92 shader.partsVertex[n] = f; 93 shader.partsFragment = fp.dup; 94 if(!attr) 95 attr = [ 96 "vertex": gl.attributeVertex, 97 "normal": gl.attributeNormal, 98 "texCoord": gl.attributeTexture 99 ]; 100 this.output = output.dup; 101 foreach(n,i; attr) 102 shader.attributes[n] = i; 103 } 104 105 106 void use(Args...)(Args args){ 107 if(!loaded) 108 exception("Trying to use unfinished material \"" ~ name ~ "\""); 109 if(shader.failed) 110 exception("Material \"" ~ name ~ "\": trying to use failed shader " ~ shader.name); 111 shader.use(args); 112 int curtex = 0; 113 foreach(u; globals){ 114 final switch(u.type){ 115 case MaterialUniform.Type.vec: 116 u.set(u.data.vec); 117 break; 118 case MaterialUniform.Type.number: 119 u.set(u.data.number); 120 break; 121 case MaterialUniform.Type.vec4: 122 u.set(u.data.vec4); 123 break; 124 case MaterialUniform.Type.tex: 125 glActiveTexture(GL_TEXTURE0 + curtex); 126 glBindTexture(GL_TEXTURE_2D, u.data.tex.id); 127 u.set(curtex++); 128 break; 129 } 130 } 131 } 132 133 134 void addTexture(string id, string path){ 135 if(loadState != Loaded) 136 onFinish ~= { 137 globals[id] = new MaterialUniform(shader, id, Texture.load(path)); 138 //gl.check(id); 139 }; 140 else { 141 globals[id] = new MaterialUniform(shader, id, Texture.load(path)); 142 //gl.check(id); 143 } 144 } 145 146 147 void addUniform(T)(string id, T u){ 148 if(!shader) 149 onFinish ~= { 150 globals[id] = new MaterialUniform(shader, id, u); 151 //gl.check(id); 152 }; 153 else { 154 globals[id] = new MaterialUniform(shader, id, u); 155 //gl.check(id); 156 } 157 } 158 159 160 void linkVertex(string path, string fn){ 161 shader.partsVertex[path] = fn; 162 } 163 164 165 void linkFragment(string part){ 166 shader.partsFragment ~= part; 167 } 168 169 override void finish(){ 170 if(loadState != Idle) 171 exception("Already finished"); 172 try { 173 loadState = Loadable.Loading; 174 string name = 175 to!string(shader.partsVertex) 176 ~ to!string(shader.partsFragment) 177 ~ to!string(shader.attributes); 178 if(name in shaders) 179 shader = shaders[name]; 180 else { 181 shader.name = name; 182 shader.finish(); 183 shader.bindFrag([diffuse: "outDiffuse", normal: "outNormal", lightInfo: "outLightData"]); 184 shaders[name] = shader; 185 } 186 foreach(f; onFinish) 187 f(); 188 loadState = Loadable.Loaded; 189 }catch(Exception e){ 190 loadState = Loadable.Error; 191 exception("Failed to finish material \"" ~ name ~ "\"", e); 192 } 193 } 194 195 override string toString(){ 196 return name ~ ':' 197 ~ to!string(shader.partsVertex) 198 ~ to!string(shader.partsFragment) 199 ~ to!string(shader.attributes); 200 } 201 202 void activateTextures(){ 203 int curtex = 0; 204 foreach(u; globals){ 205 if(u.type == MaterialUniform.Type.tex){ 206 glActiveTexture(GL_TEXTURE0 + curtex); 207 glBindTexture(GL_TEXTURE_2D, u.data.tex.id); 208 } 209 } 210 } 211 212 protected: 213 214 string name; 215 216 void delegate()[] onFinish; 217 218 string[int] output; 219 MaterialShader shader; 220 MaterialUniform[string] globals; 221 222 static MaterialShader[string] shaders; 223 224 225 static class MaterialShader: Shader { 226 227 void attach(uint type, string path){ 228 try 229 program.attach(new gl.Shader(type, cast(string)read("shaders/parts/"~path))); 230 catch(Exception e) 231 writeln("Failed to compile shader part \"" ~ path ~ "\":\n", e); 232 } 233 234 override void finish(){ 235 try { 236 program = new gl.Program; 237 foreach(part, f; partsVertex) 238 attach(gl.shaderVertex, part ~ ".vp"); 239 program.attach(new gl.Shader(gl.shaderVertex, buildVertex())); 240 foreach(part; partsFragment) 241 attach(gl.shaderFragment, part ~ ".fp"); 242 program.attach(new gl.Shader(gl.shaderFragment, FRAGMENT_BASE)); 243 foreach(s,i; attributes) 244 bindAttr([i: s]); 245 super.finish(); 246 }catch(Exception e) 247 exception("Failed to finish material \"" ~ name ~ "\"", e); 248 } 249 250 string buildVertex(){ 251 string dec; 252 foreach(_, f; partsVertex) 253 dec ~= "void " ~ f ~ "();\n"; 254 string call; 255 foreach(_, f; partsVertex) 256 call ~= '\t' ~ f ~ "();\n"; 257 return VERTEX_BASE.format(dec, call); 258 } 259 260 int[string] attributes; 261 string[string] partsVertex; 262 string[] partsFragment; 263 } 264 265 266 static class MaterialUniform: Shader.Uniform { 267 268 string name; 269 Data data; 270 Type type; 271 272 private this(MaterialShader m, string name){ 273 this.name = name; 274 super(name, m); 275 } 276 this(MaterialShader m, string name, const double n){ 277 this(m, name); 278 this.type = Type.number; 279 data.number = cast(int)n; 280 } 281 this(MaterialShader m, string name, const float[3] v){ 282 this(m, name); 283 this.type = Type.vec; 284 data.vec = v; 285 } 286 this(MaterialShader m, string name, const float[4] v){ 287 this(m, name); 288 this.type = Type.vec4; 289 data.vec4 = v; 290 } 291 this(MaterialShader m, string name, Texture t){ 292 this(m, name); 293 type = Type.tex; 294 data.tex = t; 295 } 296 297 union Data { 298 float[3] vec; 299 float[4] vec4; 300 int number; 301 Texture tex; 302 } 303 304 enum Type { 305 vec, 306 number, 307 vec4, 308 tex 309 }; 310 311 } 312 313 } 314