diff --git a/package.json b/package.json index f98bf15f..5cdc678b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "derelict-gl3": "~master", "derelict-fi": "~master", "derelict-assimp3": "~master", - "dyaml": "~master" + "dyaml": "~master", + "gl3n" : "~master" }, "targetName": "dash", "targetType": "executable", diff --git a/setup-windows.bat b/setup-windows.bat index 53a849e1..f8570936 100644 --- a/setup-windows.bat +++ b/setup-windows.bat @@ -3,3 +3,4 @@ dub fetch --local derelict-gl3 --version=~master dub fetch --local derelict-fi --version=~master dub fetch --local derelict-assimp3 --version=~master dub fetch --local dyaml --version=~master +dub fetch --local gl3n --version=~master \ No newline at end of file diff --git a/source/components/assets.d b/source/components/assets.d index 1eab296d..5e3fa198 100644 --- a/source/components/assets.d +++ b/source/components/assets.d @@ -14,7 +14,7 @@ public static: /** * Get the asset with the given type and name. */ - final T get( T )( string name ) if( is( T : Component ) ) + final T get( T )( string name ) if( is( T == Mesh ) || is( T == Texture ) || is( T == Material ) ) { static if( is( T == Mesh ) ) { diff --git a/source/components/camera.d b/source/components/camera.d index 4b787184..4557d34f 100644 --- a/source/components/camera.d +++ b/source/components/camera.d @@ -4,77 +4,59 @@ module components.camera; import core.properties, core.gameobject; import components.component; -import graphics.shaders.shader; -import math.matrix, math.vector; +import graphics.shaders; + +import gl3n.linalg; import std.signals, std.conv; final class Camera : Component { -public: - /** - * The view matrix of the camera. - */ - mixin DirtyProperty!( "Matrix!4", "viewMatrix", "updateViewMatrix" ); - +public: mixin Signal!( string, string ); - this( GameObject owner ) + this( ) { - super( owner ); - - owner.transform.connect( &this.setMatrixDirty ); + super( null ); + _viewMatrixIsDirty = true; } override void update() { } - override void draw( Shader shader ) { } override void shutdown() { } - static Matrix!4 lookAtLH( Vector!3 cameraPosition, Vector!3 cameraTarget, Vector!3 cameraUpVector ) + final @property ref mat4 viewMatrix() { - auto zaxis = ( cameraTarget - cameraPosition ).normalize(); - auto xaxis = ( cameraUpVector % zaxis ).normalize(); - auto yaxis = zaxis % xaxis; + if( _viewMatrixIsDirty ) + updateViewMatrix(); - auto newMatrix = new Matrix!4(); - - newMatrix.matrix[0][0] = xaxis.x; - newMatrix.matrix[1][0] = xaxis.y; - newMatrix.matrix[2][0] = xaxis.z; - newMatrix.matrix[0][1] = yaxis.x; - newMatrix.matrix[1][1] = yaxis.y; - newMatrix.matrix[2][1] = yaxis.z; - newMatrix.matrix[0][2] = zaxis.x; - newMatrix.matrix[1][2] = zaxis.y; - newMatrix.matrix[2][2] = zaxis.z; - newMatrix.matrix[3][0] = -( xaxis * cameraPosition ); - newMatrix.matrix[3][1] = -( yaxis * cameraPosition ); - newMatrix.matrix[3][2] = -( zaxis * cameraPosition ); - newMatrix.matrix[3][3] = 1.0f; - - return newMatrix; + return _viewMatrix; } private: + mat4 _viewMatrix; + bool _viewMatrixIsDirty; final void setMatrixDirty( string prop, string newVal ) { _viewMatrixIsDirty = true; } final void updateViewMatrix() { - auto up = owner.transform.rotation.matrix * Vector!3.up; - auto lookAt = ( owner.transform.rotation.matrix * Vector!3.forward ) + owner.transform.position; - - Vector!3[3] axes; - axes[ 2 ] = ( lookAt - owner.transform.position ).normalize(); - axes[ 0 ] = up.cross( axes[ 2 ] ).normalize(); - axes[ 1 ] = axes[ 2 ].cross( axes[ 0 ] ).normalize(); - - for( uint ii = 0; ii < 3; ++ii ) - { - for( uint jj = 0; jj < 3; ++jj ) - viewMatrix.matrix[ jj ][ ii ] = axes[ ii ][ jj ]; - viewMatrix.matrix[ 3 ][ ii ] = -axes[ ii ].dot( owner.transform.position ); - } + //Assuming pitch & yaw are in radians + float cosPitch = cos( owner.transform.rotation.pitch ); + float sinPitch = sin( owner.transform.rotation.pitch ); + float cosYaw = cos( owner.transform.rotation.yaw ); + float sinYaw = sin( owner.transform.rotation.yaw ); + + vec3 xaxis = vec3( cosYaw, 0.0f, -sinYaw ); + vec3 yaxis = vec3( sinYaw * sinPitch, cosPitch, cosYaw * sinPitch ); + vec3 zaxis = vec3( sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw ); + + _viewMatrix.clear( 0.0f ); + _viewMatrix[ 0 ] = xaxis.vector ~ -( xaxis * owner.transform.position ); + _viewMatrix[ 1 ] = yaxis.vector ~ -( yaxis * owner.transform.position ); + _viewMatrix[ 2 ] = zaxis.vector ~ -( zaxis * owner.transform.position ); + _viewMatrix[ 3 ] = [ 0, 0, 0, 1 ]; + + _viewMatrixIsDirty = false; } } diff --git a/source/components/component.d b/source/components/component.d index 62f3b460..447c5ae8 100644 --- a/source/components/component.d +++ b/source/components/component.d @@ -3,7 +3,7 @@ */ module components.component; import core.properties, core.gameobject; -import graphics.shaders.shader; +import graphics.shaders; /** * Interface for components to implement. @@ -19,10 +19,6 @@ abstract class Component * Function called on update. */ abstract void update(); - /** - * Function calledn on draw. - */ - abstract void draw( Shader shader ); /** * Function called on shutdown. */ @@ -31,5 +27,5 @@ abstract class Component /** * The GameObject that owns this component. */ - mixin Property!( "GameObject", "owner", "protected" ); + mixin Property!( "GameObject", "owner", "public" ); } diff --git a/source/components/lights.d b/source/components/lights.d new file mode 100644 index 00000000..eabc7e98 --- /dev/null +++ b/source/components/lights.d @@ -0,0 +1,50 @@ +module components.lights; +import core.properties; +import components.component; +import graphics.shaders; + +import gl3n.linalg; + +class Light : Component +{ +public: + mixin Property!( "vec3", "color", "public" ); + + this( vec3 color ) + { + super( null ); + + this.color = color; + } + + override void update() + { + + } + + override void shutdown() + { + + } + +} + +class AmbientLight : Light +{ + this( vec3 color ) + { + super( color ); + } +} + +class DirectionalLight : Light +{ +public: + mixin Property!( "vec3", "direction" ); + + this( vec3 color, vec3 direction ) + { + this.direction = direction; + super( color ); + } +} diff --git a/source/components/lights/directional.d b/source/components/lights/directional.d deleted file mode 100644 index a5a2bd2c..00000000 --- a/source/components/lights/directional.d +++ /dev/null @@ -1,16 +0,0 @@ -module components.lights.directional; -import core.properties; -import components.lights.light; -import math.vector; - -class DirectionalLight : Light -{ -public: - mixin Property!( "Vector!3", "direction" ); - - this( Vector!3 color, Vector!3 direction ) - { - this.direction = direction; - super( color ); - } -} diff --git a/source/components/lights/light.d b/source/components/lights/light.d deleted file mode 100644 index 485e60c7..00000000 --- a/source/components/lights/light.d +++ /dev/null @@ -1,34 +0,0 @@ -module components.lights.light; -import core.properties; -import components.component; -import graphics.shaders.shader; -import math.vector; - -class Light : Component -{ -public: - mixin Property!( "Vector!3", "color", "public" ); - - this( Vector!3 color ) - { - super( null ); - - this.color = color; - } - - override void update() - { - - } - - override void draw( Shader shader ) - { - - } - - override void shutdown() - { - - } - -} \ No newline at end of file diff --git a/source/components/material.d b/source/components/material.d index 4265a68c..ff20d5c0 100644 --- a/source/components/material.d +++ b/source/components/material.d @@ -1,11 +1,11 @@ module components.material; import core.properties; import components; -import graphics.shaders.shader, graphics.shaders.glshader; +import graphics.graphics, graphics.shaders; import utility.config; import yaml; -import derelict.opengl3.gl3; +import derelict.opengl3.gl3, derelict.freeimage.freeimage; import std.variant, std.conv; final class Material : Component @@ -41,19 +41,45 @@ public: } override void update() { } - override void draw( Shader shader ) { } override void shutdown() { } +} + +final class Texture +{ +public: + mixin Property!( "uint", "width" ); + mixin Property!( "uint", "height" ); + mixin Property!( "uint", "glID" ); + + this( string filePath ) + { + filePath ~= "\0"; + auto imageData = FreeImage_ConvertTo32Bits( FreeImage_Load( FreeImage_GetFileType( filePath.ptr, 0 ), filePath.ptr, 0 ) ); + + width = FreeImage_GetWidth( imageData ); + height = FreeImage_GetHeight( imageData ); + + glGenTextures( 1, &_glID ); + glBindTexture( GL_TEXTURE_2D, glID ); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + GL_BGRA, //FreeImage loads in BGR format because fuck you + GL_UNSIGNED_BYTE, + cast(GLvoid*)FreeImage_GetBits( imageData ) ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + + FreeImage_Unload( imageData ); + glBindTexture( GL_TEXTURE_2D, 0 ); + } - final void bind( Shader shader ) + void shutdown() { - GLint textureLocation = glGetUniformLocation( (cast(GLShader)shader).programID, "diffuseTexture\0" ); - glUniform1i( textureLocation, 0 ); - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, diffuse.glID ); - - textureLocation = glGetUniformLocation( (cast(GLShader)shader).programID, "normalTexture\0" ); - glUniform1i( textureLocation, 1 ); - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, normal.glID ); + glBindTexture( GL_TEXTURE_2D, 0 ); + glDeleteBuffers( 1, &_glID ); } } diff --git a/source/components/mesh.d b/source/components/mesh.d index ff378816..9614dd2b 100644 --- a/source/components/mesh.d +++ b/source/components/mesh.d @@ -5,11 +5,10 @@ module components.mesh; import core.properties; import components.component; -import graphics.graphics, graphics.shaders.shader; +import graphics.graphics, graphics.shaders; import utility.output; -import math.vector; -import derelict.assimp3.assimp; +import derelict.assimp3.assimp; import derelict.opengl3.gl3; import std.stdio, std.stream, std.format, std.math; @@ -35,8 +34,8 @@ public: // Load the scene via assimp const aiScene* scene = aiImportFile(( filePath ~ "\0" ).ptr, aiProcess_CalcTangentSpace | aiProcess_Triangulate | - aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | - aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder ); + aiProcess_JoinIdenticalVertices | aiProcess_SortByPType );//| + //aiProcess_FlipWindingOrder ); float[] outputData; uint[] indices; if(!scene) @@ -137,76 +136,11 @@ public: glBindVertexArray( 0 ); } - /// Calculates two magic numbers (tangent and binormal) which are necessary for normal mapping - Vector!3[2] calculateTangentBinormal( Vector!3[3] vertices, Vector!2[3] uvs ) - { - Vector!3[2] tangentBinormal; - - Vector!3 vector1, vector2, tangent, binormal; - Vector!2 uvector, vvector; - float den, length; - - //Calculate vectors for this face - vector1 = new Vector!3( vertices[1].x - vertices[0].x, - vertices[1].y - vertices[0].y, - vertices[1].z - vertices[0].z ); - - vector2 = new Vector!3( vertices[2].x - vertices[0].x, - vertices[2].y - vertices[0].y, - vertices[2].z - vertices[0].z ); - - //Calculate the UV space vectors - uvector = new Vector!2(); - vvector = new Vector!2(); - - uvector.x = uvs[1].x - uvs[0].x; - vvector.x = uvs[1].y - uvs[0].y; - - uvector.y = uvs[2].x - uvs[0].x; - vvector.y = uvs[2].y - uvs[0].y; - - //Calculate the denominator of the tangent/binormal equation - den = 1.0f/(uvector.x * vvector.y - uvector.y * vvector.x); - - //Calculate the cross products and multiply by the coefficient to get the tangent and binomial - tangent = new Vector!3(); - tangent.x = (vvector.y * vector1.x - vvector.x * vector2.x) * den; - tangent.y = (vvector.y * vector1.y - vvector.x * vector2.y) * den; - tangent.z = (vvector.y * vector1.z - vvector.x * vector2.z) * den; - - binormal = new Vector!3(); - binormal.x = (uvector.x * vector2.x - uvector.y * vector1.x) * den; - binormal.y = (uvector.x * vector2.y - uvector.y * vector1.y) * den; - binormal.z = (uvector.x * vector2.z - uvector.y * vector1.z) * den; - - //Normalize each vector - length = sqrt((tangent.x * tangent.x) + (tangent.y * tangent.y) + (tangent.z * tangent.z)); - tangent.x = tangent.x / length; - tangent.y = tangent.y / length; - tangent.z = tangent.z / length; - - length = sqrt((binormal.x * binormal.x) + (binormal.y * binormal.y) + (binormal.z * binormal.z)); - binormal.x = binormal.x / length; - binormal.y = binormal.y / length; - binormal.z = binormal.z / length; - - //Store them in the vertices - tangentBinormal[0] = tangent; - tangentBinormal[1] = binormal; - - return tangentBinormal; - } - override void update() { } - override void draw( Shader shader ) - { - //shader.drawMesh( this ); - } - override void shutdown() { glDeleteBuffers( 1, &_glVertexBuffer ); diff --git a/source/components/package.d b/source/components/package.d index 1d40cb76..e17ccd88 100644 --- a/source/components/package.d +++ b/source/components/package.d @@ -4,5 +4,5 @@ import components.component; import components.assets; import components.material; import components.mesh; -import components.texture; import components.camera; +import components.lights; diff --git a/source/components/texture.d b/source/components/texture.d deleted file mode 100644 index ff9888ff..00000000 --- a/source/components/texture.d +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Defines the Texture class, which contains info for a texture loaded into the world. - */ -module components.texture; -import core.properties; -import components.component; -import graphics.graphics, graphics.shaders.shader; - -import derelict.opengl3.gl3; -import derelict.freeimage.freeimage; - -final class Texture : Component -{ -public: - mixin Property!( "uint", "width" ); - mixin Property!( "uint", "height" ); - mixin Property!( "uint", "glID" ); - - this( string filePath ) - { - super( null ); - - filePath ~= "\0"; - auto imageData = FreeImage_ConvertTo32Bits( FreeImage_Load( FreeImage_GetFileType( filePath.ptr, 0 ), filePath.ptr, 0 ) ); - - width = FreeImage_GetWidth( imageData ); - height = FreeImage_GetHeight( imageData ); - - glGenTextures( 1, &_glID ); - glBindTexture( GL_TEXTURE_2D, glID ); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RGBA, - width, - height, - 0, - GL_BGRA, //FreeImage loads in BGR format because fuck you - GL_UNSIGNED_BYTE, - cast(GLvoid*)FreeImage_GetBits( imageData ) ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - - FreeImage_Unload( imageData ); - glBindTexture( GL_TEXTURE_2D, 0 ); - } - - override void update() - { - - } - - override void draw( Shader shader ) - { - //shader.bindTexture( this ); - } - - override void shutdown() - { - glBindTexture( GL_TEXTURE_2D, 0 ); - glDeleteBuffers( 1, &_glID ); - } - -} diff --git a/source/core/dgame.d b/source/core/dgame.d index 34cc4e8b..01218ef9 100644 --- a/source/core/dgame.d +++ b/source/core/dgame.d @@ -38,8 +38,11 @@ public: start(); // Loop until there is a quit message from the window or the user. - while( currentState != GameState.Quit && currentState != GameState.Reset ) + while( currentState != GameState.Quit ) { + if( currentState == GameState.Reset ) + reload(); + ////////////////////////////////////////////////////////////////////////// // Update ////////////////////////////////////////////////////////////////////////// @@ -74,9 +77,6 @@ public: Graphics.endDraw(); } - if( currentState == GameState.Reset ) - saveState(); - stop(); } @@ -112,10 +112,11 @@ private: */ final void start() { - currentState = GameState.Menu; + currentState = GameState.Game; //camera = null; Config.initialize(); + Input.initialize(); Output.initialize(); Graphics.initialize(); Assets.initialize(); @@ -137,6 +138,16 @@ private: Graphics.shutdown(); } + /** + * Reloads content and yaml. + */ + final void reload() + { + stop(); + + start(); + } + /** * Called when engine is resetting. */ diff --git a/source/core/gameobject.d b/source/core/gameobject.d index fc3df746..f097da5e 100644 --- a/source/core/gameobject.d +++ b/source/core/gameobject.d @@ -4,11 +4,11 @@ module core.gameobject; import core.prefabs, core.properties; import components; -import graphics.graphics, graphics.shaders.shader; +import graphics.graphics, graphics.shaders; import utility.config; -import math.transform, math.vector, math.quaternion; import yaml; +import gl3n.linalg; import std.signals, std.conv, std.variant; @@ -27,6 +27,14 @@ public: * The Mesh belonging to the object */ mixin Property!( "Mesh", "mesh", "public" ); + /** + * The light attached to this object + */ + mixin Property!( "Light", "light", "public" ); + /** + * The camera attached to this object + */ + mixin Property!( "Camera", "camera", "public" ); /** * The object that this object belongs to */ @@ -50,9 +58,19 @@ public: // Try to get from script if( Config.tryGet!string( "Script.ClassName", prop, yamlObj ) ) { - obj = cast(GameObject)Object.factory( prop.get!string ); + const ClassInfo scriptClass = ClassInfo.find( prop.get!string ); + + if( Config.tryGet!string( "InstanceOf", prop, yamlObj ) ) + { + obj = Prefabs[ prop.get!string ].createInstance( scriptClass ); + } + else + { + obj = cast(GameObject)scriptClass.create(); + } } - else if( Config.tryGet!string( "InstanceOf", prop, yamlObj ) ) + + if( Config.tryGet!string( "InstanceOf", prop, yamlObj ) ) { obj = Prefabs[ prop.get!string ].createInstance(); } @@ -63,7 +81,9 @@ public: if( Config.tryGet!string( "Camera", prop, yamlObj ) ) { - //TODO: Setup camera + auto cam = new Camera; + obj.addComponent( cam ); + cam.owner = obj; } if( Config.tryGet!string( "Material", prop, yamlObj ) ) @@ -78,15 +98,21 @@ public: if( Config.tryGet( "Transform", innerNode, yamlObj ) ) { - Vector!3 transVec; + vec3 transVec; if( Config.tryGet( "Scale", transVec, innerNode ) ) obj.transform.scale = transVec; if( Config.tryGet( "Position", transVec, innerNode ) ) obj.transform.position = transVec; if( Config.tryGet( "Rotation", transVec, innerNode ) ) - obj.transform.rotation = Quaternion.fromEulerAngles( transVec ); + obj.transform.rotation = quat.euler_rotation( transVec.y, transVec.z, transVec.x ); + } + + if( Config.tryGet!Light( "Light", prop, yamlObj ) ) + { + obj.addComponent( prop.get!Light ); } + obj.transform.updateMatrix(); return obj; } @@ -122,7 +148,14 @@ public: { onDraw(); - Graphics.drawObject( this ); + if( mesh !is null ) + { + Graphics.drawObject( this ); + } + if( light !is null ) + { + Graphics.addLight( light ); + } } /** @@ -151,6 +184,11 @@ public: material = cast(Material)newComponent; else if( typeid( newComponent ) == typeid( Mesh ) ) mesh = cast(Mesh)newComponent; + else if( typeid( newComponent ) == typeid( DirectionalLight ) || + typeid( newComponent ) == typeid( AmbientLight ) ) + light = cast(Light)newComponent; + else if( typeid( newComponent ) == typeid( Camera ) ) + camera = cast(Camera)newComponent; } /** @@ -179,3 +217,88 @@ public: private: Component[ClassInfo] componentList; } + +class Transform +{ +public: + this( GameObject obj = null ) + { + owner = obj; + position = vec3(0,0,0); + scale = vec3(1,1,1); + rotation = quat(0,0,0,0); + updateMatrix(); + } + + ~this() + { + //destroy( position ); + //destroy( rotation ); + //destroy( scale ); + } + + mixin Property!( "GameObject", "owner" ); + vec3 position; + quat rotation; + vec3 scale; + //mixin EmmittingProperty!( "vec3", "position", "public" ); + //mixin EmmittingProperty!( "quat", "rotation", "public" ); + //mixin EmmittingProperty!( "vec3", "scale", "public" ); + + /** + * This returns the object's position relative to the world origin, not the parent + */ + final @property vec3 worldPosition() + { + if( owner.parent is null ) + return position; + else + return owner.parent.transform.worldPosition + position; + } + + /** + * This returns the object's rotation relative to the world origin, not the parent + */ + final @property quat worldRotation() + { + if( owner.parent is null ) + return rotation; + else + return owner.parent.transform.worldRotation * rotation; + } + + final @property mat4 matrix() + { + if( _matrixIsDirty ) + updateMatrix(); + + if( owner.parent is null ) + return _matrix; + else + return owner.parent.transform.matrix * _matrix; + } + + mixin Signal!( string, string ); + + final void updateMatrix() + { + _matrix = mat4.identity; + // Scale + _matrix.scale([scale.x, scale.y, scale.z]); + //Rotate + _matrix.rotation( rotation.to_matrix!( 3, 3 ) ); + // Translate + _matrix.translation([position.x, position.y, position.z]); + + _matrixIsDirty = false; + } + +private: + mat4 _matrix; + bool _matrixIsDirty; + + final void setMatrixDirty( string prop, string newVal ) + { + _matrixIsDirty = true; + } +} \ No newline at end of file diff --git a/source/core/gameobjectcollection.d b/source/core/gameobjectcollection.d index 4e669442..28d2ae61 100644 --- a/source/core/gameobjectcollection.d +++ b/source/core/gameobjectcollection.d @@ -3,7 +3,7 @@ */ module core.gameobjectcollection; import core.gameobject; -import graphics.shaders.shader; +import graphics.shaders; import utility.filepath, utility.config; import yaml; @@ -86,6 +86,10 @@ public: apply( go => go.draw() ); } -private: + final GameObject opIndex( string key ) + { + return objects[ key ]; + } + GameObject[string] objects; } diff --git a/source/core/prefabs.d b/source/core/prefabs.d index 1602ddb7..b2429031 100644 --- a/source/core/prefabs.d +++ b/source/core/prefabs.d @@ -1,10 +1,10 @@ module core.prefabs; import core.gameobject; import components; -import math.transform, math.vector, math.quaternion; import utility.filepath, utility.config; import yaml; +import gl3n.linalg; import std.variant; final abstract class Prefabs @@ -16,6 +16,9 @@ public static: void initialize() { + foreach( key; prefabs.keys ) + prefabs.remove( key ); + void addObject( Node object ) { auto name = object[ "Name" ].as!string; @@ -69,13 +72,18 @@ public: if( Config.tryGet( "Transform", innerNode, yml ) ) { - Vector!3 transVec; + vec3 transVec; if( Config.tryGet( "Scale", transVec, innerNode ) ) transform.scale = transVec; if( Config.tryGet( "Position", transVec, innerNode ) ) transform.position = transVec; if( Config.tryGet( "Rotation", transVec, innerNode ) ) - transform.rotation = Quaternion.fromEulerAngles( transVec ); + transform.rotation = quat.euler_rotation( transVec.y, transVec.z, transVec.x ); + } + + if( Config.tryGet!Light( "Light", prop, innerNode ) ) + { + componentReferences ~= prop.get!Light; } } @@ -85,27 +93,33 @@ public: scriptClass = null; } - final GameObject createInstance() + final GameObject createInstance( const ClassInfo overrideScript = null ) { GameObject result; - if( scriptClass ) - result = cast(GameObject)scriptClass.create(); + auto script = overrideScript is null ? scriptClass : overrideScript; + + if( script ) + result = cast(GameObject)script.create(); else result = new GameObject; - result.transform.scale.values[ 0..3 ] = transform.scale.values[ 0..3 ]; - result.transform.position.values[ 0..3 ] = transform.position.values[ 0..3 ]; + result.transform.scale.vector[ 0..3 ] = transform.scale.vector[ 0..3 ]; + result.transform.position.vector[ 0..3 ] = transform.position.vector[ 0..3 ]; result.transform.rotation.x = transform.rotation.x; result.transform.rotation.y = transform.rotation.y; result.transform.rotation.z = transform.rotation.z; - result.transform.rotation.w = transform.rotation.w; + //result.transform.rotation.w = transform.rotation.w; foreach( cpn; componentReferences ) result.addComponent( cpn ); foreach( cpncls; componentCreations ) - result.addComponent( cast(Component)cpncls.create() ); + { + auto inst = cast(Component)cpncls.create(); + result.addComponent( inst ); + inst.owner = result; + } result.transform.updateMatrix(); diff --git a/source/graphics/adapters/adapter.d b/source/graphics/adapters/adapter.d index db58f0de..1f1e6fad 100644 --- a/source/graphics/adapters/adapter.d +++ b/source/graphics/adapters/adapter.d @@ -1,10 +1,10 @@ module graphics.adapters.adapter; import core.gameobject, core.properties; -import components.assets, components.mesh, components.camera, components.lights.light, components.lights.directional; -import graphics.shaders.shader, graphics.shaders.shaders, graphics.shaders.glshader; -import math.vector, math.matrix; +import components; +import graphics.shaders; import utility.config, utility.output; +import gl3n.linalg; import derelict.opengl3.gl3; version( Windows ) @@ -44,6 +44,9 @@ public: mixin Property!( "bool", "fullscreen", "protected" ); mixin Property!( "bool", "backfaceCulling", "protected" ); mixin Property!( "bool", "vsync", "protected" ); + mixin Property!( "float", "fov", "protected" ); + mixin Property!( "float", "near", "protected" ); + mixin Property!( "float", "far", "protected" ); mixin Property!( "uint", "deferredFrameBuffer", "protected" ); mixin Property!( "uint", "diffuseRenderTexture", "protected" ); //Alpha channel stores Specular color mixin Property!( "uint", "normalRenderTexture", "protected" ); //Alpha channel stores Specular power @@ -111,6 +114,7 @@ public: GLenum[ 2 ] DrawBuffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 ]; glDrawBuffers( 2, DrawBuffers.ptr ); glViewport(0, 0, width, height); + updateProjection(); if( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) { @@ -135,7 +139,7 @@ public: glEnable( GL_DEPTH_TEST ); glDisable( GL_BLEND ); - glUseProgram( (cast(GLShader)Shaders[GeometryShader]).programID ); + glUseProgram( Shaders[GeometryShader].programID ); } /** @@ -147,17 +151,15 @@ public: final void drawObject( GameObject object ) { // set the shader - GLShader shader = cast(GLShader)Shaders[GeometryShader]; + auto shader = Shaders[GeometryShader]; glBindVertexArray( object.mesh.glVertexArray ); - shader.setUniformMatrix( ShaderUniform.World , object.transform.matrix ); - shader.setUniformMatrix( ShaderUniform.WorldView, object.transform.matrix * - Camera.lookAtLH( new Vector!3( 0, 0, 0), object.transform.position, new Vector!3( 0, 1, 0 ) ) ); - shader.setUniformMatrix( ShaderUniform.WorldViewProjection , object.transform.matrix * - Matrix!4.buildPerspective( std.math.PI_2, cast(float)width / cast(float)height, 1, 1000 ) ); + shader.bindUniformMatrix4fv( ShaderUniform.World , object.transform.matrix ); + shader.bindUniformMatrix4fv( ShaderUniform.WorldViewProjection , projection * + ( ( activeCamera !is null ) ? activeCamera.viewMatrix : mat4.identity ) * + object.transform.matrix ); - //This is finding the uniform for the given texture, and setting that texture to the appropriate one for the object - object.material.bind( shader ); + shader.bindMaterial( object.material ); glDrawElements( GL_TRIANGLES, object.mesh.numVertices, GL_UNSIGNED_INT, null ); @@ -181,7 +183,7 @@ public: glBindFramebuffer( GL_FRAMEBUFFER, 0 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - GLShader shader = cast(GLShader)Shaders[LightingShader]; + auto shader = Shaders[LightingShader]; glUseProgram( shader.programID ); // bind geometry pass textures @@ -200,21 +202,61 @@ public: glActiveTexture( GL_TEXTURE2 ); glBindTexture( GL_TEXTURE_2D, depthRenderTexture ); + // bind the directional and ambient lights + if( directionalLight is null ) + { + directionalLight = new DirectionalLight( vec3(), vec3() ); + } + if( ambientLight is null ) + { + ambientLight = new AmbientLight( vec3() ); + } + shader.bindDirectionalLight( directionalLight ); + shader.bindAmbientLight( ambientLight ); + // bind the window mesh for directional lights glBindVertexArray( Assets.get!Mesh( WindowMesh ).glVertexArray ); - - // bind the directional and ambient lights - Light tempDirLight = new DirectionalLight( new Vector!3( 1.0f, 1.0f, 1.0f ), new Vector!3( 0.0f, -1.0f, 0.5f ) ); - shader.bindLight( tempDirLight ); - Light tempAmbLight = new Light( new Vector!3( .2f, .2f, .2f ) ); - shader.bindLight( tempAmbLight ); - glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, null ); glBindVertexArray(0); glUseProgram(0); swapBuffers(); + + lights = []; + ambientLight = null; + directionalLight = null; + } + + final void addLight( Light light ) + { + if( typeid( light ) == typeid( AmbientLight ) ) + { + if( ambientLight is null ) + { + ambientLight = cast(AmbientLight)light; + } + else + log( OutputType.Info, "Attemtping to add multiple ambient lights to the scene. Ignoring additional ambient lights." ); + } + else if( typeid( light ) == typeid( DirectionalLight ) ) + { + if( directionalLight is null ) + { + directionalLight = cast(DirectionalLight)light; + } + else + log( OutputType.Info, "Attemtping to add multiple directional lights to the scene. Ignoring additional directional lights." ); + } + else + { + lights ~= light; + } + } + + final void setCamera( Camera camera ) + { + activeCamera = camera; } protected: @@ -234,5 +276,21 @@ protected: backfaceCulling = Config.get!bool( "Graphics.BackfaceCulling" ); vsync = Config.get!bool( "Graphics.VSync" ); + fov = Config.get!float( "Display.FieldOfView" ); + near = Config.get!float( "Display.NearPlane" ); + far = Config.get!float( "Display.FarPlane" ); + } + + final void updateProjection() + { + projection = mat4.perspective( cast(float)width, cast(float)height, fov, near, far ); } + +private: + Camera activeCamera; + mat4 projection; + //To be cleared after a draw call: + AmbientLight ambientLight; + DirectionalLight directionalLight; + Light[] lights; } diff --git a/source/graphics/adapters/win32.d b/source/graphics/adapters/win32.d index 0192d158..eaab0fa8 100644 --- a/source/graphics/adapters/win32.d +++ b/source/graphics/adapters/win32.d @@ -162,7 +162,7 @@ public: glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // Set front face - glFrontFace( GL_CW ); + //glFrontFace( GL_CW ); reload(); diff --git a/source/graphics/graphics.d b/source/graphics/graphics.d index b146c891..e9487344 100644 --- a/source/graphics/graphics.d +++ b/source/graphics/graphics.d @@ -1,5 +1,5 @@ module graphics.graphics; -import graphics.adapters, graphics.shaders.shaders; +import graphics.adapters, graphics.shaders; final abstract class Graphics { diff --git a/source/graphics/shaders.d b/source/graphics/shaders.d new file mode 100644 index 00000000..f1fba9d9 --- /dev/null +++ b/source/graphics/shaders.d @@ -0,0 +1,353 @@ +module graphics.shaders; +import core.properties; +import components; +import graphics.graphics; +import utility.filepath, utility.output; + +import derelict.opengl3.gl3; +import gl3n.linalg; + +import std.string, std.traits; + +final abstract class Shaders +{ +public static: + final void initialize() + { + shaders[ "geometry" ] = new Shader( "geometry", geometryVS, geometryFS, true ); + shaders[ "lighting" ] = new Shader( "lighting", lightingVS, lightingFS, true ); + foreach( file; FilePath.scanDirectory( FilePath.Resources.Shaders, "*.fs.glsl" ) ) + { + // Strip .fs from file name + string name = file.baseFileName[ 0..$-3 ]; + if(name != "geometry" && name != "lighting") + { + shaders[ name ] = new Shader( name, file.directory ~ "\\" ~ name ~ ".vs.glsl", file.fullPath ); + } + else + { + log( OutputType.Warning, "Shader not loaded: Shader found which would overwrite " ~ name ~ " shader" ); + } + } + + shaders.rehash(); + } + + final void shutdown() + { + foreach_reverse( index; 0 .. shaders.length ) + { + auto name = shaders.keys[ index ]; + shaders[ name ].shutdown(); + shaders.remove( name ); + } + /*foreach( name, shader; shaders ) + { + shader.shutdown(); + shaders.remove( name ); + }*/ + } + + final Shader opIndex( string name ) + { + return get( name ); + } + + final Shader get( string name ) + { + auto shader = name in shaders; + return shader is null ? null : *shader; + } + +private: + Shader[string] shaders; +} + +public enum ShaderUniform +{ + World = "world", + WorldView = "worldView", + WorldViewProjection = "worldViewProj", + DiffuseTexture = "diffuseTexture", + NormalTexture = "normalTexture", + DepthTexture = "depthTexture", + DirectionalLightDirection = "dirLight.direction", + DirectionalLightColor = "dirLight.color", + AmbientLight = "ambientLight" +} + +final package class Shader +{ +public: + mixin Property!( "uint", "programID", "protected" ); + mixin Property!( "uint", "vertexShaderID", "protected" ); + mixin Property!( "uint", "fragmentShaderID", "protected" ); + mixin Property!( "string", "shaderName", "protected" ); + protected int[string] uniformLocations; + + this(string name, string vertex, string fragment, bool preloaded = false ) + { + shaderName = name; + // Create shader + vertexShaderID = glCreateShader( GL_VERTEX_SHADER ); + fragmentShaderID = glCreateShader( GL_FRAGMENT_SHADER ); + programID = glCreateProgram(); + + if(!preloaded) + { + auto vertexFile = new FilePath( vertex ); + auto fragmentFile = new FilePath( fragment ); + string vertexBody = vertexFile.getContents(); + string fragmentBody = fragmentFile.getContents(); + compile( vertexBody, fragmentBody ); + } + else + { + compile( vertex, fragment ); + } + } + + void compile( string vertexBody, string fragmentBody ) + { + auto vertexCBody = vertexBody.ptr; + auto fragmentCBody = fragmentBody.ptr; + int vertexSize = cast(int)vertexBody.length; + int fragmentSize = cast(int)fragmentBody.length; + + glShaderSource( vertexShaderID, 1, &vertexCBody, &vertexSize ); + glShaderSource( fragmentShaderID, 1, &fragmentCBody, &fragmentSize ); + + GLint compileStatus = GL_TRUE; + glCompileShader( vertexShaderID ); + glGetShaderiv( vertexShaderID, GL_COMPILE_STATUS, &compileStatus ); + if( compileStatus != GL_TRUE ) + { + log( OutputType.Error, shaderName ~ " Vertex Shader compile error" ); + char[1000] errorLog; + auto info = errorLog.ptr; + glGetShaderInfoLog( vertexShaderID, 1000, null, info ); + log( OutputType.Error, errorLog ); + assert(false); + } + + glCompileShader( fragmentShaderID ); + glGetShaderiv( fragmentShaderID, GL_COMPILE_STATUS, &compileStatus ); + if( compileStatus != GL_TRUE ) + { + log( OutputType.Error, shaderName ~ " Fragment Shader compile error" ); + char[1000] errorLog; + auto info = errorLog.ptr; + glGetShaderInfoLog( fragmentShaderID, 1000, null, info ); + log( OutputType.Error, errorLog ); + assert(false); + } + + // Attach shaders to program + glAttachShader( programID, vertexShaderID ); + glAttachShader( programID, fragmentShaderID ); + glLinkProgram( programID ); + + bindUniforms(); + + glGetProgramiv( programID, GL_LINK_STATUS, &compileStatus ); + if( compileStatus != GL_TRUE ) + { + log( OutputType.Error, shaderName ~ " Shader program linking error" ); + char[1000] errorLog; + auto info = errorLog.ptr; + glGetProgramInfoLog( programID, 1000, null, info ); + log( OutputType.Error, errorLog ); + assert(false); + } + } + + void bindUniforms() + { + //uniform is the *name* of the enum member not it's value + foreach( uniform; [ EnumMembers!ShaderUniform ] ) + { + //thus we use the mixin to get the value at compile time + int uniformLocation = glGetUniformLocation( programID, uniform.ptr ); + + uniformLocations[ uniform ] = uniformLocation; + } + } + + int getUniformLocation( ShaderUniform uniform ) + { + return uniformLocations[ uniform ]; + } + + final void bindUniform1f( ShaderUniform uniform, const float value ) + { + auto currentUniform = getUniformLocation( uniform ); + + glUniform1f( currentUniform, value ); + } + + final void bindUniformMatrix4fv( ShaderUniform uniform, mat4 matrix ) + { + auto currentUniform = getUniformLocation( uniform ); + + glUniformMatrix4fv( currentUniform, 1, true, matrix.value_ptr ); + } + + final void bindMaterial( Material material ) + { + //This is finding the uniform for the given texture, and setting that texture to the appropriate one for the object + GLint textureLocation = getUniformLocation( ShaderUniform.DiffuseTexture ); + glUniform1i( textureLocation, 0 ); + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, material.diffuse.glID ); + + textureLocation = getUniformLocation( ShaderUniform.NormalTexture ); + glUniform1i( textureLocation, 1 ); + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, material.normal.glID ); + } + + final void bindAmbientLight( AmbientLight light ) + { + glUniform3f( getUniformLocation( ShaderUniform.AmbientLight ), light.color.x, light.color.y, light.color.z ); + } + + final void bindDirectionalLight( DirectionalLight light ) + { + // buffer light here + glUniform3f( getUniformLocation( ShaderUniform.DirectionalLightDirection ), (cast(DirectionalLight)light).direction.x, (cast(DirectionalLight)light).direction.y, (cast(DirectionalLight)light).direction.z ); + glUniform3f( getUniformLocation( ShaderUniform.DirectionalLightColor ), light.color.x, light.color.y, light.color.z ); + } + + void shutdown() + { + + } +} + +immutable string geometryVS = q{ +#version 400 + +layout(location = 0) in vec3 vPosition_m; +layout(location = 1) in vec2 vUV; +layout(location = 2) in vec3 vNormal_m; +layout(location = 3) in vec3 vTangent_m; + +out vec4 fPosition_s; +out vec3 fNormal_w; +out vec2 fUV; +out vec3 fTangent_w; +out vec3 fBitangent_w; + +uniform mat4 world; +uniform mat4 worldView; +uniform mat4 worldViewProj; + +void main( void ) +{ + // gl_Position is like SV_Position + fPosition_s = worldViewProj * vec4( vPosition_m, 1.0f ); + gl_Position = fPosition_s; + fUV = vUV; + + fNormal_w = ( world * vec4( vNormal_m, 0.0f ) ).xyz; + fTangent_w = ( world * vec4( vTangent_m, 0.0f ) ).xyz; +} +}; + +immutable string geometryFS = q{ +#version 400 + +in vec4 fPosition_s; +in vec3 fNormal_w; +in vec2 fUV; +in vec3 fTangent_w; + +layout( location = 0 ) out vec4 color; +layout( location = 1 ) out vec4 normal_w; + +uniform sampler2D diffuseTexture; +uniform sampler2D normalTexture; + +vec2 encode( vec3 normal ) +{ + float t = sqrt( 2 / 1 - normal.z ); + return normal.xy * t; +} + +vec3 calculateMappedNormal() +{ + vec3 normal = normalize( fNormal_w ); + vec3 tangent = normalize( fTangent_w ); + //Use Gramm-Schmidt process to orthogonalize the two + tangent = normalize( tangent - dot( tangent, normal ) * normal ); + vec3 bitangent = cross( tangent, normal ); + vec3 normalMap = ((texture( normalTexture, fUV ).xyz) * 2) - 1; + mat3 TBN = mat3( tangent, bitangent, normal ); + return normalize( TBN * normalMap ); +} + +void main( void ) +{ + color = texture( diffuseTexture, fUV ); + normal_w = vec4( calculateMappedNormal(), 1.0f ); +} +}; + +immutable string lightingVS = q{ +#version 400 + +layout(location = 0) in vec3 vPosition_s; +layout(location = 1) in vec2 vUV; + +out vec4 fPosition_s; +out vec2 fUV; + +void main( void ) +{ + fPosition_s = vec4( vPosition_s, 1.0f ); + gl_Position = fPosition_s; + fUV = vUV; +} +}; + +immutable string lightingFS = q{ +#version 400 + +struct DirectionalLight +{ + vec3 color; + vec3 direction; +}; + +in vec4 fPosition; +in vec2 fUV; + +uniform sampler2D diffuseTexture; +uniform sampler2D normalTexture; +uniform sampler2D depthTexture; +uniform DirectionalLight dirLight; +uniform vec3 ambientLight; + +// https://stackoverflow.com/questions/9222217/how-does-the-fragment-shader-know-what-variable-to-use-for-the-color-of-a-pixel +out vec4 color; + +vec3 decode( vec2 enc ) +{ + float t = ( ( enc.x * enc.x ) + ( enc.y * enc.y ) ) / 4; + float ti = sqrt( 1 - t ); + return vec3( ti * enc.x, ti * enc.y, -1 + t * 2 ); +} + +void main( void ) +{ + vec4 textureColor = texture( diffuseTexture, fUV ); + vec3 normal = texture( normalTexture, fUV ).xyz; + + // temp vars until we get lights in + //vec3 lightDirection = vec3( 0.0f, -1.0f, 0.5f ); + //vec4 diffuseColor = vec4( 1.0f, 1.0f, 1.0f, 1.0f ); + + float diffuseIntensity = clamp( dot( normal, -dirLight.direction ), 0, 1 ); + color = ( vec4( ambientLight, 1.0f ) + ( diffuseIntensity * vec4( dirLight.color, 1.0f ) ) ) * textureColor; +} +}; diff --git a/source/graphics/shaders/dxshader.d b/source/graphics/shaders/dxshader.d deleted file mode 100644 index ce9cf4ab..00000000 --- a/source/graphics/shaders/dxshader.d +++ /dev/null @@ -1,18 +0,0 @@ -module graphics.shaders.dxshader; -import components.mesh, components.texture; -import graphics.shaders.shader; - -package class DXShader : Shader -{ -public: - this( string vertexPath, string pixelPath ) - { - - } - - override void shutdown() - { - - } - -} diff --git a/source/graphics/shaders/glshader.d b/source/graphics/shaders/glshader.d deleted file mode 100644 index ae13729d..00000000 --- a/source/graphics/shaders/glshader.d +++ /dev/null @@ -1,147 +0,0 @@ -module graphics.shaders.glshader; -import core.properties; -import components.mesh, components.texture, components.lights.light, components.lights.directional; -import graphics.shaders.shader; -import utility.filepath, utility.output; -import math.matrix, math.vector; -import derelict.opengl3.gl3; - -import std.traits; - -public enum ShaderUniform -{ - World = "world", - WorldView = "worldView", - WorldViewProjection = "worldViewProj", - DiffuseTexture = "diffuseTexture", - NormalTexture = "normalTexture", - DepthTexture = "depthTexture", - DirectionalLightDirection = "dirLight.direction", - DirectionalLightColor = "dirLight.color", - AmbientLight = "ambientLight" -} - -final package class GLShader : Shader -{ -public: - mixin Property!( "uint", "programID", "protected" ); - mixin Property!( "uint", "vertexShaderID", "protected" ); - mixin Property!( "uint", "fragmentShaderID", "protected" ); - mixin Property!( "string", "shaderName", "protected" ); - protected int[string] uniformLocations; - - this(string name, string vertexPath, string fragmentPath ) - { - shaderName = name; - // Create shader - vertexShaderID = glCreateShader( GL_VERTEX_SHADER ); - fragmentShaderID = glCreateShader( GL_FRAGMENT_SHADER ); - programID = glCreateProgram(); - - auto vertexFile = new FilePath( vertexPath ); - auto fragmentFile = new FilePath( fragmentPath ); - string vertexBody = vertexFile.getContents(); - string fragmentBody = fragmentFile.getContents(); - auto vertexCBody = vertexBody.ptr; - auto fragmentCBody = fragmentBody.ptr; - int vertexSize = cast(int)vertexBody.length; - int fragmentSize = cast(int)fragmentBody.length; - - glShaderSource( vertexShaderID, 1, &vertexCBody, &vertexSize ); - glShaderSource( fragmentShaderID, 1, &fragmentCBody, &fragmentSize ); - - GLint compileStatus = GL_TRUE; - glCompileShader( vertexShaderID ); - glGetShaderiv( vertexShaderID, GL_COMPILE_STATUS, &compileStatus ); - if( compileStatus != GL_TRUE ) - { - log( OutputType.Error, shaderName ~ " Vertex Shader compile error" ); - char[1000] errorLog; - auto info = errorLog.ptr; - glGetShaderInfoLog( vertexShaderID, 1000, null, info ); - log( OutputType.Error, errorLog ); - assert(false); - } - - glCompileShader( fragmentShaderID ); - glGetShaderiv( fragmentShaderID, GL_COMPILE_STATUS, &compileStatus ); - if( compileStatus != GL_TRUE ) - { - log( OutputType.Error, shaderName ~ " Fragment Shader compile error" ); - char[1000] errorLog; - auto info = errorLog.ptr; - glGetShaderInfoLog( fragmentShaderID, 1000, null, info ); - log( OutputType.Error, errorLog ); - assert(false); - } - - // Attach shaders to program - glAttachShader( programID, vertexShaderID ); - glAttachShader( programID, fragmentShaderID ); - glLinkProgram( programID ); - - bindUniforms(); - - glGetProgramiv( programID, GL_LINK_STATUS, &compileStatus ); - if( compileStatus != GL_TRUE ) - { - log( OutputType.Error, shaderName ~ " Shader program linking error", vertexPath ); - char[1000] errorLog; - auto info = errorLog.ptr; - glGetProgramInfoLog( programID, 1000, null, info ); - log( OutputType.Error, errorLog ); - assert(false); - } - } - - void bindUniforms() - { - //uniform is the *name* of the enum member not it's value - foreach( uniform; [ EnumMembers!ShaderUniform ] ) - { - //thus we use the mixin to get the value at compile time - int uniformLocation = glGetUniformLocation( programID, uniform.ptr ); - - uniformLocations[ uniform ] = uniformLocation; - } - } - - int getUniformLocation( ShaderUniform uniform ) - { - return uniformLocations[ uniform ]; - } - - final void setUniform( ShaderUniform uniform, const float value ) - { - auto currentUniform = getUniformLocation( uniform ); - - glUniform1f( currentUniform, value ); - } - - final void setUniformMatrix( ShaderUniform uniform, const Matrix!4 matrix ) - { - auto currentUniform = getUniformLocation( uniform ); - - glUniformMatrix4fv( currentUniform, 1, false, matrix.matrix.ptr.ptr ); - } - - void bindLight( Light light ) - { - if( typeid(light) == typeid(DirectionalLight) ) - { - // buffer light here - glUniform3f( getUniformLocation( ShaderUniform.DirectionalLightDirection ), (cast(DirectionalLight)light).direction.x, (cast(DirectionalLight)light).direction.y, (cast(DirectionalLight)light).direction.z ); - glUniform3f( getUniformLocation( ShaderUniform.DirectionalLightColor ), light.color.x, light.color.y, light.color.z ); - } - else //Base light class means ambient light - { - glUniform3f( getUniformLocation( ShaderUniform.AmbientLight ), light.color.x, light.color.y, light.color.z ); - } - - } - - override void shutdown() - { - - } -} diff --git a/source/graphics/shaders/shader.d b/source/graphics/shaders/shader.d deleted file mode 100644 index d7be9845..00000000 --- a/source/graphics/shaders/shader.d +++ /dev/null @@ -1,11 +0,0 @@ -module graphics.shaders.shader; -import components.texture, components.mesh; -import math.matrix; - -abstract class Shader -{ -public: - void shutdown(); - - Matrix!4 projectionMatrix, viewMatrix, modelMatrix; -} diff --git a/source/graphics/shaders/shaders.d b/source/graphics/shaders/shaders.d deleted file mode 100644 index 52444866..00000000 --- a/source/graphics/shaders/shaders.d +++ /dev/null @@ -1,50 +0,0 @@ -module graphics.shaders.shaders; -import graphics.graphics, graphics.shaders.shader, graphics.shaders.glshader, graphics.shaders.dxshader; -import utility.filepath; - -import std.string; - -final abstract class Shaders -{ -public static: - final void initialize() - { - foreach( file; FilePath.scanDirectory( FilePath.Resources.Shaders, "*.fs.glsl" ) ) - { - // Strip .fs from file name - string name = file.baseFileName[ 0..$-3 ]; - shaders[ name ] = new GLShader( name, file.directory ~ "\\" ~ name ~ ".vs.glsl", file.fullPath ); - } - - shaders.rehash(); - } - - final void shutdown() - { - foreach_reverse( index; 0 .. shaders.length ) - { - auto name = shaders.keys[ index ]; - shaders[ name ].shutdown(); - shaders.remove( name ); - } - /*foreach( name, shader; shaders ) - { - shader.shutdown(); - shaders.remove( name ); - }*/ - } - - final Shader opIndex( string name ) - { - return get( name ); - } - - final Shader get( string name ) - { - auto shader = name in shaders; - return shader is null ? null : *shader; - } - -private: - Shader[string] shaders; -} diff --git a/source/math/matrix.d b/source/math/matrix.d deleted file mode 100644 index 650a76b4..00000000 --- a/source/math/matrix.d +++ /dev/null @@ -1,163 +0,0 @@ -module math.matrix; -import math.vector; -import std.math, std.numeric, std.traits; - -alias Matrix!3 Matrix3; -alias Matrix!4 Matrix4; - -final class Matrix( uint S = 4 ) if( S > 1 && S < 5 ) -{ -public: -static -{ - const Matrix!S identity = new Matrix!S; - Matrix!4 buildPerspective( const float fov, const float screenAspect, const float near, const float depth ) - { - auto toReturn = new Matrix!4; - auto yScale = 1 / tan( fov / 2 ); - auto xScale = yScale / screenAspect; - - toReturn.matrix[ 0 ][ 0 ] = xScale; - toReturn.matrix[ 1 ][ 1 ] = yScale; - toReturn.matrix[ 2 ][ 2 ] = depth / ( depth - near ); - toReturn.matrix[ 2 ][ 3 ] = 1.0f; - toReturn.matrix[ 3 ][ 2 ] = ( -near * depth ) / ( depth - near ); - toReturn.matrix[ 3 ][ 3 ] = 0.0f; - - return toReturn; - } - - Matrix!4 buildOrthogonal( const float width, const float height, const float near, const float far ) - { - auto toReturn = new Matrix!4; - - toReturn.matrix[ 0 ][ 0 ] = 2.0f / width; - toReturn.matrix[ 1 ][ 1 ] = 2.0f / height; - toReturn.matrix[ 2 ][ 2 ] = -2.0f / ( far - near ); - toReturn.matrix[ 3 ][ 3 ] = 1.0f; - - return toReturn; - } -} - - alias matrix this; - - this() pure @safe - { - for( uint ii = 0; ii < S; ++ii ) - { - for( uint jj = 0; jj < S; ++jj ) - matrix[ ii ][ jj ] = 0.0f; - - matrix[ ii ][ ii ] = 1.0f; - } - } - - /** - Calls other operators, and assigns results back to itself - */ - final Matrix!S opOpAssign( string op )( const Matrix!S other ) - { - matrix = opBinary!( op[ 0 ] )( other ).matrix; - return this; - } - - alias opBinary!"*" multiply; - - /** - * Responsible for all math functions related to Matrix. - */ - final auto opBinary( string op, T = Matrix!S )( T other ) - { - static if ( is( Unqual!T == Matrix!S ) ) - { - static if ( op == "*" ) - { - // From here: http://rosettacode.org/wiki/Matrix_multiplication#Stronger_Statically_Typed_Version - auto result = new Matrix!S; - float[ S ] aux; - - foreach ( j; 0..S ) - { - foreach ( i, bi; other.matrix ) - aux[ i ] = bi[ j ]; - foreach ( i, ai; matrix ) - result.matrix[ i ][ j ] = dotProduct( ai, aux ); - } - - return result; - } - else static if ( op == "+" ) - { - auto result = new Matrix!S; - - foreach ( yy; 0..S ) - foreach ( xx; 0..S ) - result.matrix[ xx ][ yy ] = matrix[ xx ][ yy ] + other.matrix[ xx ][ yy ]; - } - else static assert( 0, "Operator " ~ op ~ " not implemented." ); - } - else static if ( is( Unqual!T == float ) ) - { - static if ( op == "*" ) - { - auto result = Matrix!S; - - foreach ( yy, row; matrix ) - foreach ( xx, element; row ) - result.matrix[ xx ][ yy ] = element * other; - - return result; - } - } - // Is vector? - else static if ( __traits(compiles, T.length) && T.length <= S ) - { - // Deduce Vector length - foreach( VS; staticIota!( 0, S ) ) - { - static if ( __traits(compiles, T.length) && VS == T.length && is( Unqual!T == Vector!VS ) ) - { - static if ( op == "*" ) - { - auto result = new Vector!VS; - - for( uint ii = 0; ii < VS; ++ii ) - for( uint jj = 0; jj < VS; ++jj ) - result.values[ ii ] += matrix[ jj ][ ii ] * other.values[ jj ]; - - return result; - } - else static assert( 0, "Operator " ~ op ~ " not implemented." ); - } - } - - // Incase of fall though, break at run time. - // This shouldn't ever get hit. - assert( false ); - } - else static assert( 0, "Operator " ~ op ~ " not implemented." ); - } - - final Matrix!S opUnary( string op : "-" )() pure @safe - { - return inverse(); - } - final Matrix!S inverse() pure @safe - { - return new Matrix!S; - } - - final Matrix!S transpose() pure @safe - { - auto result = new Matrix!S; - - for( uint yy = 0; yy < S; ++yy ) - for( uint xx = 0; xx < S; ++xx ) - result.matrix[ xx ][ yy ] = matrix[ yy ][ xx ]; - - return result; - } - - float[ S ][ S ] matrix; -} diff --git a/source/math/quaternion.d b/source/math/quaternion.d deleted file mode 100644 index 66d7c33e..00000000 --- a/source/math/quaternion.d +++ /dev/null @@ -1,108 +0,0 @@ -module math.quaternion; -import core.properties; -import math.matrix, math.vector; - -import std.signals, std.conv; -import std.math; - -final class Quaternion -{ -public: - this() - { - _w = 1.0f; - _x = 0.0f; - _y = 0.0f; - _z = 0.0f; - - matrix = new Matrix!4(); - } - - this( const float x, const float y, const float z, const float angle ) - { - immutable float fHalfAngle = angle / 2.0f; - immutable float fSin = sin( fHalfAngle ); - - _w = cos( fHalfAngle ); - _x = fSin * x; - _y = fSin * y; - _z = fSin * z; - - matrix = new Matrix!4(); - } - - static Quaternion fromEulerAngles( Vector!3 angles ) - { - return fromEulerAngles( angles.x, angles.y, angles.z ); - } - - static Quaternion fromEulerAngles( const float x, const float y, const float z ) - { - auto res = new Quaternion; - - float cosHalfX = cos( x / 2 ); - float cosHalfY = cos( y / 2 ); - float cosHalfZ = cos( z / 2 ); - float sinHalfX = sin( x / 2 ); - float sinHalfY = sin( y / 2 ); - float sinHalfZ = sin( z / 2 ); - - // From here: http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Conversion - res._x = ( cosHalfZ * cosHalfY * cosHalfX ) + ( sinHalfZ * sinHalfY * sinHalfX ); - res._y = ( sinHalfZ * cosHalfY * cosHalfX ) - ( cosHalfZ * sinHalfY * sinHalfX ); - res._z = ( cosHalfZ * sinHalfY * cosHalfX ) + ( sinHalfZ * cosHalfY * sinHalfX ); - res._w = ( cosHalfZ * cosHalfY * sinHalfX ) - ( sinHalfZ * sinHalfX * cosHalfX ); - - return res; - } - - mixin Signal!( string, string ); - - mixin EmmittingPropertySetDirty!( "float", "x", "matrix", "public" ); - mixin EmmittingPropertySetDirty!( "float", "y", "matrix", "public" ); - mixin EmmittingPropertySetDirty!( "float", "z", "matrix", "public" ); - mixin EmmittingPropertySetDirty!( "float", "w", "matrix", "public" ); - - mixin DirtyProperty!( "Matrix!4", "matrix", "updateMatrix" ); - - final Quaternion opBinary( string op )( Quaternion other ) - { - static if ( op == "*" ) - { - return new Quaternion( - x * other.w + y * other.z - z * other.y + w * other.x, - -x * other.z + y * other.w + z * other.x + w * other.y, - x * other.y - y * other.x + z * other.w + w * other.z, - -x * other.x - y * other.y - z * other.z + w * other.w ); - } - else static assert ( 0, "Operator " ~ op ~ " not implemented." ); - } - - final ref Quaternion opOpAssign( string op )( Quaternion other ) - { - static if ( op == "*" ) - { - x = x * other.w + y * other.z - z * other.y + w * other.x; - y = -x * other.z + y * other.w + z * other.x + w * other.y; - z = x * other.y - y * other.x + z * other.w + w * other.z; - w = -x * other.x - y * other.y - z * other.z + w * other.w; - - return this; - } - else static assert ( 0, "Operator " ~ op ~ " not implemented for assign." ); - } - -private: - final void updateMatrix() - { - _matrix.matrix[ 0 ][ 0 ] = 1.0f - 2.0f * y * y - 2.0f * z * z; - _matrix.matrix[ 0 ][ 1 ] = 2.0f * x * y - 2.0f * z * w; - _matrix.matrix[ 0 ][ 2 ] = 2.0f * x * z + 2.0f * y * w; - _matrix.matrix[ 1 ][ 0 ] = 2.0f * x * y + 2.0f * z * w; - _matrix.matrix[ 1 ][ 1 ] = 1.0f - 2.0f * x * x - 2.0f * z * z; - _matrix.matrix[ 1 ][ 2 ] = 2.0f * y * z - 2.0f * x * w; - _matrix.matrix[ 2 ][ 0 ] = 2.0f * x * z - 2.0f * y * w; - _matrix.matrix[ 2 ][ 1 ] = 2.0f * y * z + 2.0f * x * w; - _matrix.matrix[ 2 ][ 2 ] = 1.0f - 2.0f * x * x - 2.0f * y * y; - } -} diff --git a/source/math/transform.d b/source/math/transform.d deleted file mode 100644 index a7462e75..00000000 --- a/source/math/transform.d +++ /dev/null @@ -1,106 +0,0 @@ -module math.transform; -import core.properties, core.gameobject; -import math.vector, math.matrix, math.quaternion; - -import std.signals, std.conv; - -class Transform -{ -public: - this( GameObject obj = null ) - { - owner = obj; - - position = new Vector!3; - rotation = new Quaternion; - scale = new Vector!3( 1.0, 1.0, 1.0); - - position.connect( &emit ); - rotation.connect( &emit ); - scale.connect( &emit ); - - updateMatrix(); - - position.connect( &setMatrixDirty ); - rotation.connect( &setMatrixDirty ); - scale.connect( &setMatrixDirty ); - connect( &setMatrixDirty ); - } - - ~this() - { - destroy( position ); - destroy( rotation ); - destroy( scale ); - } - - mixin Property!( "GameObject", "owner" ); - mixin EmmittingProperty!( "Vector!3", "position", "public" ); - mixin EmmittingProperty!( "Quaternion", "rotation", "public" ); - mixin EmmittingProperty!( "Vector!3", "scale", "public" ); - - /** - * This returns the object's position relative to the world origin, not the parent - */ - final @property Vector!3 worldPosition() - { - if( owner.parent is null ) - return position; - else - return owner.parent.transform.worldPosition + position; - } - - /** - * This returns the object's rotation relative to the world origin, not the parent - */ - final @property Quaternion worldRotation() - { - if( owner.parent is null ) - return rotation; - else - return owner.parent.transform.worldRotation * rotation; - } - - final @property Matrix!4 matrix() - { - if( _matrixIsDirty ) - updateMatrix(); - - if( owner.parent is null ) - return _matrix; - else - return owner.parent.transform.matrix * _matrix; - } - - mixin Signal!( string, string ); - - final void updateMatrix() - { - auto newMatrix = new Matrix!4; - - // Scale - newMatrix.matrix[ 0 ][ 0 ] = scale.x; - newMatrix.matrix[ 1 ][ 1 ] = scale.y; - newMatrix.matrix[ 2 ][ 2 ] = scale.z; - newMatrix.matrix[ 3 ][ 3 ] = 1.0f; - - // Rotate - _matrix = newMatrix * rotation.matrix; - - // Translate - _matrix.matrix[ 3 ][ 0 ] = position.x; - _matrix.matrix[ 3 ][ 1 ] = position.y; - _matrix.matrix[ 3 ][ 2 ] = position.z; - - _matrixIsDirty = false; - } - -private: - Matrix!4 _matrix; - bool _matrixIsDirty; - - final void setMatrixDirty( string prop, string newVal ) - { - _matrixIsDirty = true; - } -} diff --git a/source/math/vector.d b/source/math/vector.d deleted file mode 100644 index 44680bb4..00000000 --- a/source/math/vector.d +++ /dev/null @@ -1,257 +0,0 @@ -module math.vector; -import core.properties; - -import std.math; -import std.signals, std.conv, std.typetuple, std.traits; - -alias Vector!2 Vector2; -alias Vector!3 Vector3; -alias Vector!4 Vector4; - -class Vector( uint S = 3 ) -{ -public: - enum length = S; - - this( float def = 0.0f ) @safe - { - for( uint ii = 0; ii < S; ++ii ) - values[ ii ] = def; - } - - this( float[] arg ... ) @safe - { - for( uint ii = 0; ii < S; ++ii ) - values[ ii ] = arg[ ii ]; - } - - /** - * Returns a swizzled vector - */ - final auto opDispatch( string prop )() if( prop.length > 1 ) - { - auto result = new Vector!( prop.length ); - - foreach( index; staticIota!( 0, prop.length ) ) - { - mixin( "result[ index ] = " ~ prop[ index ] ~ ";" ); - } - - return result; - } - - /** - * Calls other operators, and assigns results back to itself - */ - final Vector!S opOpAssign( string op )( const Vector!S other ) @safe - { - values = opBinary!( op[ 0 ] )( other ).values; - return this; - } - - final override int opCmp( Object o ) - { - if( typeid(o) != typeid(this) ) - return 1; - - auto other = cast(Vector!S)o; - - for( uint ii = 0; ii < S; ++ii ) - { - if( values[ ii ] < other[ ii ] ) - return -1; - else if( values[ ii ] > other[ ii ] ) - return 1; - } - - return 0; - } - - /// Alias for operators - static if( S == 3 ) - alias opBinary!"%" cross; - alias opBinary!"*" dot; - alias opBinary!"+" add; - alias opBinary!"-" subtract; - - /** - * Responsible for all math functions related to Vector. - */ - final auto opBinary( string op, T = Vector!S )( T other ) - { - static if ( is( Unqual!T == Vector!S ) ) - { - static if ( op == "*" ) - { - float result = 0; - - foreach ( ii; 0..S ) - result += values[ ii ] * other[ ii ]; - - return result; - } - else static if ( op == "%" && S == 3 ) - { - auto result = new Vector!S; - - result.x = ( y * other.z ) - ( z * other.y ); - result.y = ( z * other.x ) - ( x * other.z ); - result.z = ( x * other.y ) - ( y * other.x ); - - return result; - } - else static if ( op == "+" ) - { - auto result = new Vector!S; - - for( uint ii = 0; ii < S; ++ii ) - result[ ii ] = values[ ii ] + other[ ii ]; - - return result; - } - else static if ( op == "-" ) - { - auto result = new Vector!S; - - for( uint ii = 0; ii < S; ++ii ) - result[ ii ] = values[ ii ] - other[ ii ]; - - return result; - } - else static assert( 0, "Operator " ~ op ~ " not implemented." ); - } - else static if( is( Unqual!T == float ) ) - { - static if ( op == "*" ) - { - auto result = new Vector!S; - - for( uint ii = 0; ii < S; ++ii ) - result.values[ ii ] += values[ ii ] * other; - - return result; - } - else static assert( 0, "Operator " ~ op ~ " not implemented." ); - } - else static assert( 0, "Operator " ~ op ~ " not implemented for type " ~ T.stringof ~ "." ); - } - - final Vector!S opUnary( string op )() pure @safe - { - static if ( op == "-" ) - { - auto result = new Vector!S; - - for( uint ii = 0; ii < S; ++ii ) - result[ ii ] = -values[ ii ]; - - return result; - } - else static assert ( 0, "Operator " ~ op ~ " not implemented." ); - } - - final float opIndex( uint index ) - { - return values[ index ]; - } - - final void opIndexAssign( float newVal, uint index ) - { - values[ index ] = newVal; - } - - final float magnitude() pure @safe - { - float result = 0.0f; - - for( uint ii = 0; ii < S; ++ii ) - result += values[ ii ] * values[ ii ]; - - result = sqrt( result ); - - return result; - } - - final Vector!S normalize() pure @safe - { - auto result = new Vector!S; - auto mag = magnitude(); - - for( uint ii = 0; ii < S; ++ii ) - result[ ii ] = values[ ii ] / mag; - - return result; - } - - // Set named accessors - static if( S >= 2 ) - { - mixin EmmittingBackedProperty!( "float", "values[0]", "x", "public" ); - mixin EmmittingBackedProperty!( "float", "values[1]", "y", "public" ); - } - static if( S >= 3 ) - { - mixin EmmittingBackedProperty!( "float", "values[2]", "z", "public" ); - } - static if( S >= 4 ) - { - mixin EmmittingBackedProperty!( "float", "values[3]", "w", "public" ); - } - - // Set predefined constants - static if( S == 2 ) - { - static const Vector!2 up = new Vector!2( 0.0f, 1.0f ); - static const Vector!2 right = new Vector!2( 1.0f, 0.0f ); - } - static if( S == 3 ) - { - static const Vector!3 up = new Vector!3( 0.0f, 1.0f, 0.0f ); - static const Vector!3 forward = new Vector!3( 0.0f, 0.0f, 1.0f ); - static const Vector!3 right = new Vector!3( 1.0f, 0.0f, 0.0f ); - } - - /// Stores the values in the vector - float[ S ] values; - - mixin Signal!( string, string ); -} - -// Creates a range from start inclusive to end exclusive. -template staticIota(size_t start, size_t end) -{ - static if(start == end) - alias TypeTuple!() staticIota; - else static if(start < end) - alias TypeTuple!(start, staticIota!(start + 1, end)) staticIota; - else - static assert(0, "start cannot be greater then end!"); -} - -unittest -{ - import std.stdio; - writeln( "Dash Vector opDispatch unittest" ); - - auto vec1 = new Vector!3( 1.0f, 2.0f, 3.0f ); - - auto vec2 = vec1.zyx; - - assert( vec2.x == vec1.z ); - assert( vec2.y == vec1.y ); - assert( vec2.z == vec1.x ); -} -unittest -{ - import std.stdio; - writeln( "Dash Vector magnitude unittest" ); - - auto vec2 = new Vector!2( 1.0f, 0.0f ); - assert( vec2.magnitude == 1.0f ); - - auto vec3 = new Vector!3( 0.0f, 2.0f, 0.0f ); - assert( vec3.magnitude == 2.0f ); - - auto vec4 = new Vector!4( 0.0f, 0.0f, 3.0f, 0.0f ); - assert( vec4.magnitude == 3.0f ); -} diff --git a/source/utility/config.d b/source/utility/config.d index a16997d5..d1506ca0 100644 --- a/source/utility/config.d +++ b/source/utility/config.d @@ -6,11 +6,12 @@ import utility.filepath; // Imports for conversions import core.dgame : GameState; -import components.assets; -import graphics.shaders.shaders; +import components.assets, components.lights; +import graphics.shaders; import utility.output : Verbosity; -import math.vector, math.quaternion; +import utility.input : Keyboard; +import gl3n.linalg; import yaml; import std.array, std.conv, std.string, std.path, std.typecons, std.variant; @@ -33,12 +34,15 @@ public static: constructor.addConstructorMapping( "!Quaternion-Map", &constructQuaternion ); constructor.addConstructorScalar( "!GameState", &constructConv!GameState ); constructor.addConstructorScalar( "!Verbosity", &constructConv!Verbosity ); + constructor.addConstructorScalar( "!Keyboard", &constructConv!Keyboard ); constructor.addConstructorScalar( "!Shader", ( ref Node node ) => Shaders.get( node.get!string ) ); + constructor.addConstructorMapping( "!Light-Directional", &constructDirectionalLight ); + constructor.addConstructorMapping( "!Light-Ambient", &constructAmbientLight ); //constructor.addConstructorScalar( "!Texture", ( ref Node node ) => Assets.get!Texture( node.get!string ) ); //constructor.addConstructorScalar( "!Mesh", ( ref Node node ) => Assets.get!Mesh( node.get!string ) ); //constructor.addConstructorScalar( "!Material", ( ref Node node ) => Assets.get!Material( node.get!string ) ); - config = loadYaml( FilePath.Resources.Config ); + config = loadYaml( FilePath.Resources.ConfigFile ); } /** @@ -127,7 +131,7 @@ public static: right = right[ split + 1..$ ]; } - if( !current.containsKey( left ) ) + if( !current.isMapping || !current.containsKey( left ) ) return false; current = current[ left ]; @@ -166,14 +170,14 @@ private: Constructor constructor; } -Vector!2 constructVector2( ref Node node ) +vec2 constructVector2( ref Node node ) { - auto result = new Vector!2; + vec2 result; if( node.isMapping ) { - result.values[ 0 ] = node[ "x" ].as!float; - result.values[ 1 ] = node[ "y" ].as!float; + result.x = node[ "x" ].as!float; + result.y = node[ "y" ].as!float; } else if( node.isScalar ) { @@ -191,15 +195,15 @@ Vector!2 constructVector2( ref Node node ) return result; } -Vector!3 constructVector3( ref Node node ) +vec3 constructVector3( ref Node node ) { - auto result = new Vector!3; + vec3 result; if( node.isMapping ) { - result.values[ 0 ] = node[ "x" ].as!float; - result.values[ 1 ] = node[ "y" ].as!float; - result.values[ 2 ] = node[ "z" ].as!float; + result.x = node[ "x" ].as!float; + result.y = node[ "y" ].as!float; + result.z = node[ "z" ].as!float; } else if( node.isScalar ) { @@ -218,9 +222,9 @@ Vector!3 constructVector3( ref Node node ) return result; } -Quaternion constructQuaternion( ref Node node ) +quat constructQuaternion( ref Node node ) { - Quaternion result = new Quaternion; + quat result; if( node.isMapping ) { @@ -247,6 +251,25 @@ Quaternion constructQuaternion( ref Node node ) return result; } +Light constructDirectionalLight( ref Node node ) +{ + vec3 color; + vec3 dir; + + Config.tryGet( "Color", color, node ); + Config.tryGet( "Direction", dir, node ); + + return new DirectionalLight( color, dir ); +} + +Light constructAmbientLight( ref Node node ) +{ + vec3 color; + Config.tryGet( "Color", color, node ); + + return new AmbientLight( color ); +} + T constructConv( T )( ref Node node ) if( is( T == enum ) ) { if( node.isScalar ) diff --git a/source/utility/filepath.d b/source/utility/filepath.d index a2e34c41..65fc41b9 100644 --- a/source/utility/filepath.d +++ b/source/utility/filepath.d @@ -2,6 +2,8 @@ * Defines the FilePath class, which stores default resource paths, and handles path manipulation. */ module utility.filepath; +import utility.output; + static import std.file, std.path; import std.stdio; @@ -29,7 +31,9 @@ public: Objects = ResourceHome ~ "/Objects", Shaders = ResourceHome ~ "/Shaders", UI = ResourceHome ~ "/UI", - Config = ResourceHome ~ "/Config.yml", + ConfigDir = ResourceHome ~ "/Config", + ConfigFile = ConfigDir ~ "/Config.yml", + InputBindings = ConfigDir ~ "/Input.yml", } /** @@ -40,6 +44,12 @@ public: // Get absolute path to folder string safePath = std.path.buildNormalizedPath( std.path.absolutePath( path ) ); + if( !std.file.exists( safePath ) ) + { + log( OutputType.Info, path, " does not exist." ); + return []; + } + // Start array auto files = new FilePath[ 1 ]; uint filesFound = 0; @@ -157,7 +167,7 @@ unittest import std.stdio; writeln( "Dash FilePath properties unittest" ); - auto fp = new FilePath( FilePath.Resources.Config ); + auto fp = new FilePath( FilePath.Resources.ConfigFile ); assert( fp.fileName == "Config.yml", "FilePath.fileName error." ); assert( fp.baseFileName == "Config", "FilePath.baseFileName error." ); diff --git a/source/utility/input.d b/source/utility/input.d index 07090550..804ae8e5 100644 --- a/source/utility/input.d +++ b/source/utility/input.d @@ -2,8 +2,10 @@ * Defines the static Input class, which is responsible for handling all keyboard/mouse/controller interactions. */ module utility.input; +import utility.config, utility.filepath, utility.output; -import std.typecons; +import yaml; +import std.typecons, std.conv; final abstract class Input { @@ -14,6 +16,68 @@ public static: alias void delegate( uint, bool ) KeyEvent; alias void delegate( uint ) KeyStateEvent; + /** + * Processes Config/Input.yml and pulls input string bindings. + */ + final void initialize() + { + auto bindings = Config.loadYaml( FilePath.Resources.InputBindings ); + + foreach( key; keyBindings.keys ) + keyBindings.remove( key ); + + foreach( string name, Node bind; bindings ) + { + log( OutputType.Info, "Binding ", name ); + + if( bind.isScalar ) + { + keyBindings[ name ] = bind.get!Keyboard; + } + else if( bind.isSequence ) + { + foreach( Node child; bind ) + { + try + { + keyBindings[ name ] = child.get!Keyboard; + } + catch + { + log( OutputType.Error, "Failed to parse keybinding for input ", name ); + } + } + } + else if( bind.isMapping ) + { + foreach( string type, Node value; bind ) + { + final switch( type ) + { + case "Keyboard": + try + { + keyBindings[ name ] = value.get!Keyboard; + } + catch + { + try + { + keyBindings[ name ] = value.get!string.to!Keyboard; + } + catch + { + log( OutputType.Error, "Failed to parse keybinding for input ", name ); + } + } + + break; + } + } + } + } + } + /** * Updates the key states, and calls all key events. */ @@ -92,7 +156,35 @@ public static: staging[ keyCode ] = newState; } + /** + * Gets the state of a string-bound input. + * + * Params: + * input = The input to check for. + * checkPrevious = Whether or not to make sure the key was up last frame. + */ + final T getState( T = bool )( string input, bool checkPrevious = false ) if( is( T == bool ) /*|| is( T == float )*/ ) + { + static if( is( T == bool ) ) + { + if( input in keyBindings ) + { + return isKeyDown( keyBindings[ input ], checkPrevious ); + } + else + { + throw new Exception( "Input " ~ input ~ " not bound." ); + } + } + /*else static if( is( T == float ) ) + { + + }*/ + } + private: + Keyboard[ string ] keyBindings; + KeyEvent[][ uint ] keyEvents; KeyState current; @@ -157,7 +249,7 @@ private: * * From: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx */ -enum Keys: uint +enum Keyboard: uint { MouseLeft = 0x01, /// Left mouse button MouseRight = 0x02, /// Right mouse button