1 /** 2 * Defines Shader class and the Shaders collection for loading, binding, and setting values in GLSL shaders 3 */ 4 module dash.graphics.shaders.shaders; 5 import dash.core, dash.components, dash.graphics, dash.utility; 6 import dash.graphics.shaders.glsl; 7 8 import derelict.opengl3.gl3; 9 10 import std..string, std.traits, std.algorithm, std.array, std.regex; 11 12 /* 13 * String constants for our shader uniforms 14 */ 15 private enum ShaderUniform 16 { 17 /// Matrices 18 World = "world", 19 WorldProj = "worldProj", // used this for scaling & orthogonal UI drawing 20 WorldView = "worldView", 21 WorldViewProjection = "worldViewProj", 22 InverseProjection = "invProj", 23 LightProjectionView = "lightProjView", 24 CameraView = "cameraView", 25 /// Floats 26 ProjectionConstants = "projectionConstants", 27 /// Textures 28 UITexture = "uiTexture", 29 DiffuseTexture = "diffuseTexture", 30 NormalTexture = "normalTexture", 31 SpecularTexture = "specularTexture", 32 DepthTexture = "depthTexture", 33 ShadowMap = "shadowMap", 34 /// Lights 35 LightDirection = "light.direction", 36 LightColor = "light.color", 37 LightRadius = "light.radius", 38 LightFalloffRate = "light.falloffRate", 39 LightPosition = "light.pos_v", 40 LightShadowless = "light.shadowless", 41 EyePosition = "eyePosition_w", 42 /// Animations 43 Bones = "bones", 44 /// Object data 45 ObjectId = "objectId", 46 } 47 48 /** 49 * A constant string representing immutable uint fields for each ShaderUniform enum values 50 */ 51 enum ShaderUniformFields = reduce!( ( a, b ) => a ~ "immutable uint " ~ b ~ ";\n" )( "", [__traits(allMembers,ShaderUniform )] ); 52 53 /** 54 * Loads necessary shaders into variables, and any custom user shaders into an associative array 55 */ 56 final abstract class Shaders 57 { 58 static: 59 private: 60 Shader[string] shaders; 61 62 public: 63 /// Geometry Shader 64 Shader geometry; 65 /// Animated Geometry Shader 66 Shader animatedGeometry; 67 /// Ambient Lighting Shader 68 Shader ambientLight; 69 /// Directional Lighting shader 70 Shader directionalLight; 71 /// Point Lighting shader 72 Shader pointLight; 73 /// User Interface shader 74 Shader userInterface; 75 /// Shader for depth of inanimate objects. 76 Shader shadowMap; 77 /// Shader for depth of animated objects. 78 Shader animatedShadowMap; 79 80 /** 81 * Loads the field-shaders first, then any additional shaders in the Shaders folder 82 */ 83 final void initialize() 84 { 85 geometry = new Shader( "Geometry", geometryVS, geometryFS, true ); 86 animatedGeometry = new Shader( "AnimatedGeometry", animatedGeometryVS, geometryFS, true ); // Only VS changed, FS stays the same 87 ambientLight = new Shader( "AmbientLight", ambientlightVS, ambientlightFS, true ); 88 directionalLight = new Shader( "DirectionalLight", directionallightVS, directionallightFS, true ); 89 pointLight = new Shader( "PointLight", pointlightVS, pointlightFS, true ); 90 userInterface = new Shader( "UserInterface", userinterfaceVS, userinterfaceFS, true ); 91 shadowMap = new Shader( "ShadowMap", shadowmapVS, shadowmapFS, true ); 92 animatedShadowMap = new Shader( "AnimatedShadowMap", animatedshadowmapVS, shadowmapFS, true ); 93 94 foreach( file; scanDirectory( Resources.Shaders, "*.fs.glsl" ) ) 95 { 96 // Strip .fs from file name 97 string name = file.baseFileName[ 0..$-3 ]; 98 shaders[ name ] = new Shader( name, file.directory ~ "\\" ~ name ~ ".vs.glsl", file.fullPath ); 99 } 100 101 shaders.rehash(); 102 } 103 104 /** 105 * Empties the array of shaders and calls their Shutdown function 106 */ 107 final void shutdown() 108 { 109 foreach_reverse( index; 0 .. shaders.length ) 110 { 111 auto name = shaders.keys[ index ]; 112 shaders[ name ].shutdown(); 113 shaders.remove( name ); 114 } 115 } 116 117 /** 118 * Returns a Shader based on its string name 119 */ 120 final Shader opIndex( string name ) 121 { 122 return get( name ); 123 } 124 125 /** 126 * Returns a Shader based on its string name 127 */ 128 final Shader get( string name ) 129 { 130 Shader* shader = name in shaders; 131 return shader is null ? null : *shader; 132 } 133 } 134 135 /** 136 * Class storing the programID, VS ID, FS ID and ShaderUniform locations for a given Shader program 137 */ 138 final package class Shader 139 { 140 private: 141 uint _programID, _vertexShaderID, _fragmentShaderID; 142 string _shaderName; 143 auto versionRegex = ctRegex!r"\#version\s400"; 144 auto layoutRegex = ctRegex!r"layout\(location\s\=\s[0-9]+\)\s"; 145 146 public: 147 /// The program ID for the shader 148 mixin( Property!_programID ); 149 /// The ID for the vertex shader 150 mixin( Property!_vertexShaderID ); 151 /// The ID for the fragment shader 152 mixin( Property!_fragmentShaderID ); 153 /// The string name of the Shader 154 mixin( Property!_shaderName ); 155 /// Uint locations for each possible Shader Uniform 156 mixin( ShaderUniformFields ); 157 158 /** 159 * Creates a Shader Program from the name, and either the vertex and fragment shader strings, or their file names 160 */ 161 this(string name, string vertex, string fragment, bool preloaded = false ) 162 { 163 shaderName = name; 164 // Create shader 165 vertexShaderID = glCreateShader( GL_VERTEX_SHADER ); 166 fragmentShaderID = glCreateShader( GL_FRAGMENT_SHADER ); 167 programID = glCreateProgram(); 168 169 if(!preloaded) 170 { 171 auto vertexFile = Resource( vertex ); 172 auto fragmentFile = Resource( fragment ); 173 string vertexBody = vertexFile.readText(); 174 string fragmentBody = fragmentFile.readText(); 175 176 //If we're using OpenGL 3.3 then we need to 177 //change our GLSL version to match, and remove 178 //any layout(location = x) qualifiers (they 179 //aren't supported in GLSL 330) 180 if(config.graphics.usingGl33) 181 { 182 vertexBody = replaceAll(vertexBody, layoutRegex, ""); 183 vertexBody = replaceAll(vertexBody, versionRegex, "#version 330"); 184 185 fragmentBody = replaceAll(fragmentBody, layoutRegex, ""); 186 fragmentBody = replaceAll(fragmentBody, versionRegex, "#version 330"); 187 188 trace( vertexBody ); 189 } 190 191 compile( vertexBody, fragmentBody ); 192 } 193 else 194 { 195 if(config.graphics.usingGl33) 196 { 197 vertex = replaceAll(vertex, layoutRegex, ""); 198 vertex = replaceAll(vertex, versionRegex, "#version 330"); 199 200 fragment = replaceAll(fragment, layoutRegex, ""); 201 fragment = replaceAll(fragment, versionRegex, "#version 330"); 202 } 203 204 compile( vertex, fragment ); 205 } 206 207 //uniform is the *name* of the enum member not it's value 208 foreach( uniform; __traits( allMembers, ShaderUniform ) ) 209 { 210 mixin(uniform) = glGetUniformLocation( programID, mixin("ShaderUniform." ~ uniform).ptr ); 211 } 212 } 213 214 /** 215 * Compiles a Vertex and Fragment shader into a Shader Program 216 */ 217 void compile( string vertexBody, string fragmentBody ) 218 { 219 auto vertexCBody = vertexBody.ptr; 220 auto fragmentCBody = fragmentBody.ptr; 221 int vertexSize = cast(int)vertexBody.length; 222 int fragmentSize = cast(int)fragmentBody.length; 223 224 glShaderSource( vertexShaderID, 1, &vertexCBody, &vertexSize ); 225 glShaderSource( fragmentShaderID, 1, &fragmentCBody, &fragmentSize ); 226 227 GLint compileStatus = GL_TRUE; 228 glCompileShader( vertexShaderID ); 229 glGetShaderiv( vertexShaderID, GL_COMPILE_STATUS, &compileStatus ); 230 if( compileStatus != GL_TRUE ) 231 { 232 errorf( "%s Vertex Shader compile error", shaderName ); 233 char[1000] errorLog; 234 auto info = errorLog.ptr; 235 glGetShaderInfoLog( vertexShaderID, 1000, null, info ); 236 error( errorLog ); 237 assert(false); 238 } 239 240 glCompileShader( fragmentShaderID ); 241 glGetShaderiv( fragmentShaderID, GL_COMPILE_STATUS, &compileStatus ); 242 if( compileStatus != GL_TRUE ) 243 { 244 errorf( "%s Fragment Shader compile error", shaderName ); 245 char[1000] errorLog; 246 auto info = errorLog.ptr; 247 glGetShaderInfoLog( fragmentShaderID, 1000, null, info ); 248 error( errorLog ); 249 assert(false); 250 } 251 252 // Attach shaders to program 253 glAttachShader( programID, vertexShaderID ); 254 glAttachShader( programID, fragmentShaderID ); 255 glLinkProgram( programID ); 256 257 glGetProgramiv( programID, GL_LINK_STATUS, &compileStatus ); 258 if( compileStatus != GL_TRUE ) 259 { 260 errorf( "%s Shader program linking error", shaderName ); 261 char[1000] errorLog; 262 auto info = errorLog.ptr; 263 glGetProgramInfoLog( programID, 1000, null, info ); 264 error( errorLog ); 265 assert(false); 266 } 267 } 268 269 /** 270 * Pass through for glUniform1f 271 */ 272 final void bindUniform1f( uint uniform, const float value ) 273 { 274 glUniform1f( uniform, value ); 275 } 276 277 /** 278 * Pass through for glUniform2f 279 */ 280 final void bindUniform2f( uint uniform, const vec2f value ) 281 { 282 glUniform2f( uniform, value.x, value.y ); 283 } 284 285 /** 286 * Pass through for glUniform 3f 287 * Passes to the shader in XYZ order 288 */ 289 final void bindUniform3f( uint uniform, const vec3f value ) 290 { 291 glUniform3f( uniform, value.x, value.y, value.z ); 292 } 293 294 /** 295 * Pass through for glUniform2f 296 */ 297 final void bindUniform1ui( uint uniform, const uint value ) 298 { 299 glUniform1ui( uniform, value ); 300 } 301 302 /** 303 * pass through for glUniformMatrix4fv 304 */ 305 final void bindUniformMatrix4fv( uint uniform, mat4f matrix ) 306 { 307 glUniformMatrix4fv( uniform, 1, true, matrix.value_ptr ); 308 } 309 310 /** 311 * Bind an array of mat4s. 312 */ 313 final void bindUniformMatrix4fvArray( uint uniform, mat4f[] matrices ) 314 { 315 auto matptr = appender!(float[]); 316 foreach( matrix; matrices ) 317 { 318 matptr ~= matrix.value_ptr()[0..16]; 319 } 320 glUniformMatrix4fv( uniform, cast(int)matrices.length, true, matptr.data.ptr ); 321 } 322 323 /** 324 * Binds diffuse, normal, and specular textures to the shader 325 */ 326 final void bindMaterial( Material material ) 327 in 328 { 329 assert( material, "Cannot bind null material." ); 330 assert( material.diffuse && material.normal && material.specular, "Material must have diffuse, normal, and specular components." ); 331 } 332 body 333 { 334 //This is finding the uniform for the given texture, and setting that texture to the appropriate one for the object 335 glUniform1i( DiffuseTexture, 0 ); 336 glActiveTexture( GL_TEXTURE0 ); 337 glBindTexture( GL_TEXTURE_2D, material.diffuse.glID ); 338 339 glUniform1i( NormalTexture, 1 ); 340 glActiveTexture( GL_TEXTURE1 ); 341 glBindTexture( GL_TEXTURE_2D, material.normal.glID ); 342 343 glUniform1i( SpecularTexture, 2 ); 344 glActiveTexture( GL_TEXTURE2 ); 345 glBindTexture( GL_TEXTURE_2D, material.specular.glID ); 346 } 347 348 /** 349 * Binds a UI's texture 350 */ 351 final void bindUI( UserInterface ui ) 352 { 353 // This is part of a bigger problem. But in the interest of dope screenshots... 354 version( OSX ) 355 if( !ui.view ) 356 return; 357 358 glUniform1i( UITexture, 0 ); 359 glActiveTexture( GL_TEXTURE0 ); 360 glBindTexture( GL_TEXTURE_2D, ui.view.glID ); 361 } 362 363 /** 364 * Bind an ambient light 365 */ 366 final void bindAmbientLight( AmbientLight light ) 367 { 368 bindUniform3f( LightColor, light.color ); 369 } 370 371 /** 372 * Bind a directional light 373 */ 374 final void bindDirectionalLight( DirectionalLight light ) 375 { 376 bindUniform3f( LightDirection, light.direction); 377 bindUniform3f( LightColor, light.color ); 378 bindUniform1f( LightShadowless, cast(float)(!light.castShadows) ); 379 } 380 381 /** 382 * Bind a directional light after a modifying transform 383 */ 384 final void bindDirectionalLight( DirectionalLight light, mat4f transform ) 385 { 386 bindUniform3f( LightDirection, ( transform * vec4f( light.direction, 0.0f ) ).xyz ); 387 bindUniform3f( LightColor, light.color ); 388 bindUniform1f( LightShadowless, cast(float)(!light.castShadows) ); 389 } 390 391 /** 392 * Bind a point light 393 */ 394 final void bindPointLight( PointLight light ) 395 { 396 bindUniform3f( LightColor, light.color ); 397 bindUniform3f( LightPosition, light.owner.transform.worldPosition ); 398 bindUniform1f( LightRadius, light.radius ); 399 bindUniform1f( LightFalloffRate, light.falloffRate ); 400 } 401 402 /** 403 * Bind a point light after a modifying transform 404 */ 405 final void bindPointLight( PointLight light, mat4f transform ) 406 { 407 bindUniform3f( LightColor, light.color ); 408 bindUniform3f( LightPosition, ( transform * vec4f( light.owner.transform.worldPosition, 1.0f ) ).xyz); 409 bindUniform1f( LightRadius, light.radius ); 410 bindUniform1f( LightFalloffRate, light.falloffRate ); 411 } 412 413 414 /** 415 * Sets the eye position for lighting calculations 416 */ 417 final void setEyePosition( vec3f pos ) 418 { 419 glUniform3f( EyePosition, pos.x, pos.y, pos.z ); 420 } 421 422 /** 423 * Clean up the shader 424 */ 425 void shutdown() 426 { 427 glDeleteProgram( programID ); 428 } 429 }