1 /** 2 * Contains the base class for all light types, Light, as well as 3 * AmbientLight, DirectionalLight, PointLight, SpotLight. 4 */ 5 module dash.components.lights; 6 import dash.core, dash.components, dash.graphics; 7 import dash.utility; 8 9 import derelict.opengl3.gl3; 10 import std.math; 11 12 mixin( registerComponents!() ); 13 14 /** 15 * Base class for lights. 16 */ 17 abstract class Light : Component 18 { 19 public: 20 /// The color the light gives off. 21 @rename( "Color" ) @optional 22 vec3f color; 23 /// If it should cast shadows 24 @rename( "CastShadows" ) @optional 25 bool castShadows; 26 27 this( vec3f color ) 28 { 29 this.color = color; 30 castShadows = false; 31 } 32 33 override void update() { } 34 override void shutdown() { } 35 } 36 37 /** 38 * Ambient Light 39 */ 40 class AmbientLight : Light 41 { 42 this( vec3f color = vec3f( 1.0f ) ) 43 { 44 super( color ); 45 } 46 } 47 48 /** 49 * Directional Light 50 */ 51 class DirectionalLight : Light 52 { 53 private: 54 uint _shadowMapFrameBuffer; 55 uint _shadowMapTexture; 56 mat4f _projView; 57 int _shadowMapSize; 58 59 public: 60 /// The direction the light points in. 61 @rename( "Direction" ) @optional 62 vec3f direction; 63 /// The FrameBuffer for the shadowmap. 64 mixin( Property!( _shadowMapFrameBuffer ) ); 65 /// The shadow map's depth texture. 66 mixin( Property!( _shadowMapTexture ) ); 67 mixin( Property!( _projView ) ); 68 mixin( Property!( _shadowMapSize ) ); 69 70 this( vec3f color = vec3f( 1.0f ), vec3f direction = vec3f( 0.0f ), bool castShadows = false ) 71 { 72 this.direction = direction; 73 super( color ); 74 this.castShadows = castShadows; 75 } 76 77 /// Initializes the lights. 78 override void initialize() 79 { 80 if( castShadows ) 81 { 82 // generate framebuffer for shadow map 83 shadowMapFrameBuffer = 0; 84 glGenFramebuffers( 1, cast(uint*)&_shadowMapFrameBuffer ); 85 glBindFramebuffer( GL_FRAMEBUFFER, _shadowMapFrameBuffer ); 86 87 // generate depth texture of shadow map 88 shadowMapSize = 2048; 89 glGenTextures( 1, cast(uint*)&_shadowMapTexture ); 90 glBindTexture( GL_TEXTURE_2D, _shadowMapTexture ); 91 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, shadowMapSize, shadowMapSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null ); 92 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 93 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 94 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); 95 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 96 97 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _shadowMapTexture, 0 ); 98 99 // don't want any info besides depth 100 glDrawBuffer( GL_NONE ); 101 // don't want to read from gpu 102 glReadBuffer( GL_NONE ); 103 104 // check for success 105 if( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) 106 { 107 fatal( "Shadow map frame buffer failure." ); 108 assert(false); 109 } 110 } 111 } 112 113 /** 114 * calculates the light's projection and view matrices, and combines them 115 */ 116 void calculateProjView( box3f frustum ) 117 { 118 // determine the center of the frustum 119 vec3f center = vec3f( ( frustum.min + frustum.max ).x/2.0f, 120 ( frustum.min + frustum.max ).y/2.0f, 121 ( frustum.min + frustum.max ).z/2.0f ); 122 123 // determine the rotation for the viewing axis 124 // adapted from http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors 125 vec3f lDirNorm = direction.normalized; 126 vec3f baseAxis = vec3f( 0, 0, -1 ); 127 float cosTheta = dot( lDirNorm, baseAxis ); 128 float halfCosX2 = sqrt( 0.5f * (1.0f + cosTheta) ) * 2.0f; 129 vec3f w = cross( lDirNorm, baseAxis ); 130 quatf rotation = quatf( halfCosX2/2, w.x / halfCosX2, w.y / halfCosX2, w.z / halfCosX2 ); 131 132 // determine the x,y,z axes 133 vec3f eulers = rotation.toEulerAngles(); 134 float cosPitch = cos( eulers.x ); 135 float sinPitch = sin( eulers.x ); 136 float cosYaw = cos( eulers.y ); 137 float sinYaw = sin( eulers.y ); 138 vec3f xaxis = vec3f( cosYaw, 0.0f, -sinYaw ); 139 vec3f yaxis = vec3f( sinYaw * sinPitch, cosPitch, cosYaw * sinPitch ); 140 vec3f zaxis = vec3f( sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw ); 141 142 // build the view matrix 143 mat4f viewMatrix; 144 ///* 145 viewMatrix[ 0 ] = vec4f( xaxis, -xaxis.dot( center ) ).vector; 146 viewMatrix[ 1 ] = vec4f( yaxis, -yaxis.dot( center ) ).vector; 147 viewMatrix[ 2 ] = vec4f( zaxis, -zaxis.dot( center ) ).vector; 148 viewMatrix[ 3 ] = vec4f( 0, 0, 0, 1 ).vector; 149 /*/ 150 // using lookAt works for everying but a light direction of (0,+/-1,0) 151 light.view = Camera.lookAt( center - light.direction.normalized, center ); //*/ 152 153 // get frustum in view space 154 frustum.min = (viewMatrix * vec4f(frustum.min,1.0f)).xyz; 155 frustum.max = (viewMatrix * vec4f(frustum.max,1.0f)).xyz; 156 157 // get mins and maxes in view space 158 vec3f mins, maxes; 159 for( int i = 0; i < 3; i++ ) 160 { 161 if( frustum.min.vector[ i ] < frustum.max.vector[ i ] ) 162 { 163 mins.vector[ i ] = frustum.min.vector[ i ]; 164 maxes.vector[ i ] = frustum.max.vector[ i ]; 165 } 166 else 167 { 168 mins.vector[ i ] = frustum.max.vector[ i ]; 169 maxes.vector[ i ] = frustum.min.vector[ i ]; 170 } 171 } 172 173 float magicNumber = 1.5f; // literally the worst 174 projView = mat4f.orthographic( mins.x * magicNumber, maxes.x* magicNumber, mins.y* magicNumber , maxes.y* magicNumber, maxes.z* magicNumber, mins.z* magicNumber ) * viewMatrix; 175 } 176 } 177 178 /** 179 * Point Light 180 */ 181 class PointLight : Light 182 { 183 private: 184 mat4f _matrix; 185 186 public: 187 /// The area that lighting will be calculated for. 188 @rename( "Radius" ) 189 float radius; 190 /// The light's exponential attenuation modifier. 191 @rename( "FalloffRate" ) 192 float falloffRate; 193 194 this( vec3f color = vec3f( 1.0f ), float radius = 0.0f, float falloffRate = 0.0f ) 195 { 196 this.radius = radius; 197 this.falloffRate = falloffRate; 198 super( color ); 199 } 200 201 /** 202 * TODO 203 * 204 * Params: 205 * 206 * Returns: 207 */ 208 public mat4f getTransform() 209 { 210 _matrix = mat4f.identity; 211 // Scale 212 _matrix[ 0 ][ 0 ] = radius; 213 _matrix[ 1 ][ 1 ] = radius; 214 _matrix[ 2 ][ 2 ] = radius; 215 // Translate 216 vec3f position = owner.transform.worldPosition; 217 _matrix[ 0 ][ 3 ] = position.x; 218 _matrix[ 1 ][ 3 ] = position.y; 219 _matrix[ 2 ][ 3 ] = position.z; 220 return _matrix; 221 } 222 223 } 224 225 /** 226 * SpotLight Stub 227 */ 228 class SpotLight : Light 229 { 230 public: 231 this( vec3f color = vec3f() ) 232 { 233 super( color ); 234 } 235 }