diff --git a/source/components/animation.d b/source/components/animation.d index ae6a4243..a1ccf5ad 100644 --- a/source/components/animation.d +++ b/source/components/animation.d @@ -3,7 +3,7 @@ */ module components.animation; import core.properties; -import components.icomponent; +import components.component; import utility; import derelict.assimp3.assimp; @@ -12,7 +12,7 @@ import gl3n.linalg; /** * Animation object which handles all animation specific to the gameobject */ -class Animation : IComponent +class Animation : Component { private: /// Asset animation that the gameobject is animating based off of @@ -26,10 +26,6 @@ private: /// If the gameobject should be animating bool _animating; -public: - /// Bone transforms for the current pose (Passed to the shader) - mixin( Property!_currBoneTransforms ); - /** * Create animation object based on asset animation */ @@ -41,6 +37,10 @@ public: _animating = true; } +public: + /// Bone transforms for the current pose (Passed to the shader) + mixin( Property!_currBoneTransforms ); + /** * Updates the animation, updating time and getting a pose based on time */ @@ -152,6 +152,14 @@ public: addAnimationSet( animations[ ii ], 24 ); } + /** + * Returns the animation as an addible component. + */ + Animation getComponent() + { + return new Animation( this ); + } + /** * Recurse the node hierarchy, parsing it into a usable bone hierarchy * diff --git a/source/components/assets.d b/source/components/assets.d index 71d8d23f..35854d48 100644 --- a/source/components/assets.d +++ b/source/components/assets.d @@ -22,7 +22,6 @@ package: Mesh[string] meshes; Texture[string] textures; Material[string] materials; - AssetAnimation[string] animations; public: /// Basic quad, generally used for billboarding. @@ -71,7 +70,7 @@ public: // Load the unitSquare unitSquare = new Mesh( "", aiImportFileFromMemory( unitSquareMesh.toStringz, unitSquareMesh.length, - aiProcess_CalcTangentSpace | aiProcess_Triangulate | + aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType, "obj" ).mMeshes[0] ); @@ -79,10 +78,10 @@ public: { // Load mesh const aiScene* scene = aiImportFile( file.fullPath.toStringz, - aiProcess_CalcTangentSpace | aiProcess_Triangulate | + aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType ); assert( scene, "Failed to load scene file '" ~ file.fullPath ~ "' Error: " ~ aiGetErrorString().fromStringz ); - + // If animation data, add animation if( file.baseFileName in meshes ) logWarning( "Mesh ", file.baseFileName, " exsists more than once." ); @@ -90,10 +89,12 @@ public: // Add mesh if( scene.mNumMeshes > 0 ) { + auto newMesh = new Mesh( file.fullPath, scene.mMeshes[ 0 ] ); + if( scene.mNumAnimations > 0 ) - animations[ file.baseFileName ] = new AssetAnimation( scene.mAnimations, scene.mNumAnimations, scene.mMeshes[ 0 ], scene.mRootNode ); + newMesh.animationData = new AssetAnimation( scene.mAnimations, scene.mNumAnimations, scene.mMeshes[ 0 ], scene.mRootNode ); - meshes[ file.baseFileName ] = new Mesh( file.fullPath, scene.mMeshes[ 0 ] ); + meshes[ file.baseFileName ] = newMesh; } else { @@ -114,13 +115,13 @@ public: foreach( objFile; loadYamlFiles( Resources.Materials ) ) { - auto object = objFile[ 0 ]; + Node object = objFile[ 0 ]; auto name = object[ "Name" ].as!string; if( name in materials ) logWarning( "Material ", name, " exists more than once." ); - - auto newMat = Material.createFromYaml( object ); + + auto newMat = cast(Material)createYamlObject[ "Material" ]( object ); materials[ name ] = newMat; materialResources[ objFile[ 1 ] ] ~= newMat; } @@ -128,7 +129,6 @@ public: meshes.rehash(); textures.rehash(); materials.rehash(); - animations.rehash(); materialResources.rehash(); } @@ -156,10 +156,10 @@ public: mixin( refresh!q{meshes} ); mixin( refresh!q{textures} ); - + // Iterate over each file, and it's materials refreshYamlObjects!( - Material.createFromYaml, + node => cast(Material)createYamlObject[ "Material" ]( node ), node => node[ "Name" ].get!string in materials, ( node, mat ) => materials[ node[ "Name" ].get!string ] = mat, mat => materials.remove( mat.name ) ) @@ -185,11 +185,10 @@ public: mixin( shutdown!( q{meshes}, "Mesh" ) ); mixin( shutdown!( q{textures}, "Texture" ) ); mixin( shutdown!( q{materials}, "Material" ) ); - mixin( shutdown!( q{animations}, "Animation" ) ); } } -abstract class Asset : IComponent +abstract class Asset : Component { private: bool _isUsed; @@ -208,10 +207,6 @@ public: { _resource = resource; } - - override void update() { } - abstract void refresh(); - abstract override void shutdown(); } /// Obj for a 1x1 square billboard mesh diff --git a/source/components/behavior.d b/source/components/behavior.d deleted file mode 100644 index 38f63e9d..00000000 --- a/source/components/behavior.d +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Defines Behavior class, the base class for all scripts. - */ -module components.behavior; -import core, utility; - -import yaml; -import std.algorithm, std.array, std.traits; - -/** - * Defines methods for child classes to override. - */ -private abstract class ABehavior -{ - /// The object the behavior belongs to. - private GameObject _owner; - /// Function called by Behaviors to init the object. - /// Should not be touched by anything outside this module. - protected void initializeBehavior( Object param ) { } - /// The function called on initialization of the object. - void onInitialize() { } - /// Called on the update cycle. - void onUpdate() { } - /// Called on the draw cycle. - void onDraw() { } - /// Called on shutdown. - void onShutdown() { } - /// Called when refreshing an object. - void onRefresh() { } -} - -/** - * Defines methods for child classes to override, with a parameter for onInitialize. - * - * Params: - * InitType = The type for onInitialize to take. - */ -abstract class Behavior( InitType = void ) : ABehavior -{ - static if( !is( InitType == void ) ) - { - InitType initArgs; - protected final override void initializeBehavior( Object param ) - { - initArgs = cast(InitType)param; - } - } - - /// Returns the GameObject which owns this behavior. - mixin( Getter!_owner ); - - /** - * Registers subclasses with onInit function pointers. - */ - static this() - { - static if( !is( InitType == void ) ) - { - foreach( mod; ModuleInfo ) - { - foreach( klass; mod.localClasses ) - { - if( klass.base == typeid(Behavior!InitType) ) - { - getInitParams[ klass.name ] = &getObject!InitType; - } - } - } - } - } -} - -private Object function( Node )[string] getInitParams; - -/** - * Defines a collection of Behaviors to allow for multiple scripts to be added to an object. - */ -struct Behaviors -{ -private: - ABehavior[] behaviors; - GameObject _owner; - -public: - /** - * Constructor for Behaviors which assigns its owner. - * - * Params: - * owner = The owner of this behavior set. - */ - this( GameObject owner ) - { - _owner = owner; - } - - /** - * Adds a new behavior to the collection. - * - * Params: - * className = The behavior to add to the object. - * fields = Fields to convert give to behavior - */ - ABehavior createBehavior( string className, Node fields = Node( YAMLNull() ) ) - { - auto newBehavior = cast(ABehavior)Object.factory( className ); - newBehavior._owner = _owner; - - if( !newBehavior ) - { - logWarning( "Class ", className, " either not found or not child of Behavior." ); - return null; - } - - newBehavior._owner = _owner; - - if( !fields.isNull && className in getInitParams ) - { - newBehavior.initializeBehavior( getInitParams[ className ]( fields ) ); - } - - behaviors ~= newBehavior; - - return newBehavior; - } - - /** - * Adds a new behavior to the collection. - * - * Params: - * T = The behavior to add to the object. - * fields = Fields to convert give to behavior - */ - T createBehavior( T )( Node fields = Node( YAMLNull() ) ) if( is( T : ABehavior ) ) - { - auto newBehavior = new T; - - if( !newBehavior ) - { - logWarning( "Class ", T.stringof, " either not found or not child of Behavior." ); - return null; - } - - newBehavior._owner = _owner; - - if( !fields.isNull && T.stringof in getInitParams ) - { - newBehavior.initializeBehavior( getInitParams[ T.stringof ]( fields ) ); - } - - behaviors ~= newBehavior; - - return newBehavior; - } - - /** - * Gets the behavior of the given type. - * - * Returns: The bahavior - */ - BehaviorType get( BehaviorType = ABehavior )() - { - foreach( behav; behaviors ) - { - if( typeid(behav) == typeid(BehaviorType) ) - return cast(BehaviorType)behav; - } - - return null; - } - - mixin( callBehaviors ); -} - -enum callBehaviors = "".reduce!( ( a, func ) => a ~ - q{ - static if( __traits( compiles, ParameterTypeTuple!( __traits( getMember, ABehavior, "$func") ) ) && - ParameterTypeTuple!( __traits( getMember, ABehavior, "$func") ).length == 0 ) - { - void $func() - { - foreach( script; behaviors ) - { - script.$func(); - } - } - } - }.replace( "$func", func ) )( cast(string[])[__traits( derivedMembers, ABehavior )] ); diff --git a/source/components/camera.d b/source/components/camera.d index 3e68b48e..28e91878 100644 --- a/source/components/camera.d +++ b/source/components/camera.d @@ -7,15 +7,18 @@ import core, components, graphics, utility; import gl3n.linalg; import std.conv; +mixin( registerComponents!q{components.camera} ); + /** * Camera manages a view and projection matrix. */ -final class Camera : IComponent, IDirtyable +@yamlComponent( "Camera" ) +final class Camera : Component, IDirtyable { private: float _prevFov, _prevNear, _prevFar, _prevWidth, _prevHeight; - float _fov, _near, _far; + vec2 _projectionConstants; // For rebuilding linear Z in shaders mat4 _prevLocalMatrix; mat4 _viewMatrix; @@ -26,20 +29,17 @@ private: mat4 _inverseOrthogonalMatrix; public: - override void update() { } - override void shutdown() { } - /// TODO mixin( ThisDirtyGetter!( _viewMatrix, updateViewMatrix ) ); /// TODO mixin( ThisDirtyGetter!( _inverseViewMatrix, updateViewMatrix ) ); - /// TODO - mixin( Property!( _fov, AccessModifier.Public ) ); - /// TODO - mixin( Property!( _near, AccessModifier.Public ) ); - /// TODO - mixin( Property!( _far, AccessModifier.Public ) ); - + @field( "FOV" ) + float fov; + @field( "Near" ) + float near; + @field( "Far" ) + float far; + /** * TODO * @@ -159,7 +159,7 @@ public: _viewMatrix[ 1 ] = yaxis.vector ~ -( yaxis * owner.transform.position ); _viewMatrix[ 2 ] = zaxis.vector ~ -( zaxis * owner.transform.position ); _viewMatrix[ 3 ] = [ 0, 0, 0, 1 ]; - + _inverseViewMatrix = _viewMatrix.inverse(); } @@ -171,7 +171,7 @@ public: * cameraPos = The camera's position. * worldUp = The up direction in the world. * - * Returns: + * Returns: * A right handed view matrix for the given params. */ final static shared(mat4) lookAt( vec3 targetPos, vec3 cameraPos, vec3 worldUp = vec3(0,1,0) ) @@ -200,8 +200,6 @@ public: return result.transposed; } - - /** * TODO * @@ -225,9 +223,9 @@ private: */ final bool projectionDirty() { - return _fov != _prevFov || - _far != _prevFar || - _near != _prevNear || + return fov != _prevFov || + far != _prevFar || + near != _prevNear || cast(float)Graphics.width != _prevWidth || cast(float)Graphics.height != _prevHeight; } @@ -237,8 +235,8 @@ private: */ final void updatePerspective() { - _projectionConstants = vec2( ( -_far * _near ) / ( _far - _near ), _far / ( _far - _near ) ); - _perspectiveMatrix = mat4.perspective( cast(float)Graphics.width, cast(float)Graphics.height, _fov, _near, _far ); + _projectionConstants = vec2( ( -far * near ) / ( far - near ), far / ( far - near ) ); + _perspectiveMatrix = mat4.perspective( cast(float)Graphics.width, cast(float)Graphics.height, fov, near, far ); _inversePerspectiveMatrix = _perspectiveMatrix.inverse(); } @@ -249,7 +247,7 @@ private: { _orthogonalMatrix = mat4.identity; - _orthogonalMatrix[0][0] = 2.0f / Graphics.width; + _orthogonalMatrix[0][0] = 2.0f / Graphics.width; _orthogonalMatrix[1][1] = 2.0f / Graphics.height; _orthogonalMatrix[2][2] = -2.0f / (far - near); _orthogonalMatrix[3][3] = 1.0f; @@ -262,34 +260,10 @@ private: */ final void updateProjectionDirty() { - _prevFov = _fov; - _prevFar = _far; - _prevNear = _near; + _prevFov = fov; + _prevFar = far; + _prevNear = near; _prevWidth = cast(float)Graphics.width; _prevHeight = cast(float)Graphics.height; } } - -static this() -{ - import yaml; - IComponent.initializers[ "Camera" ] = ( Node yml, GameObject obj ) - { - obj.camera = new Camera; - obj.camera.owner = obj; - - //float fromYaml; - if( !yml.tryFind( "FOV", obj.camera._fov ) ) - logFatal( obj.name, " is missing FOV value for its camera. "); - if( !yml.tryFind( "Near", obj.camera._near ) ) - logFatal( obj.name, " is missing near plane value for its camera. "); - if( !yml.tryFind( "Far", obj.camera._far ) ) - logFatal( obj.name, " is missing Far plane value for its camera. "); - - obj.camera.updatePerspective(); - obj.camera.updateOrthogonal(); - obj.camera.updateProjectionDirty(); - - return obj.camera; - }; -} \ No newline at end of file diff --git a/source/components/component.d b/source/components/component.d new file mode 100644 index 00000000..5640366c --- /dev/null +++ b/source/components/component.d @@ -0,0 +1,348 @@ +/** + * Defines the IComponent interface, which is the base for all components. + */ +module components.component; +import core, components, graphics, utility; + +import yaml; +import std.array, std.string, std.traits; + +/// Tests if a type can be created from yaml. +enum isYamlObject(T) = __traits( compiles, { T obj; obj.yaml = Node( YAMLNull() ); } ); + +abstract class YamlObject +{ + Node yaml; + + /// Called when refreshing an object. + void refresh() { } + + this() + { + yaml = Node( YAMLNull() ); + } +} + +/** + * Interface for components to implement. + */ +abstract class Component : YamlObject +{ +public: + /// The node that defined the component. + Node yaml; + /// The GameObject that owns this component. + GameObject owner; + + /// The function called on initialization of the object. + void initialize() { } + /// Called on the update cycle. + void update() { } + /// Called on the draw cycle. + void draw() { } + /// Called on shutdown. + void shutdown() { } +} + +/** + * Meant to be added to components that can be set from YAML. + * Example: + * --- + * @yamlObject("Test") + * class Test : YamlObject + * { + * @field("X") + * int x; + * } + * --- + */ +auto yamlObject( string name = "" ) +{ + return YamlUDA( YamlType.Object, name, null ); +} + +/** + * Meant to be added to components that can be set from YAML. + * Example: + * --- + * @yamlComponent("Test") + * class Test : Component + * { + * @field("X") + * int x; + * } + * --- + */ +auto yamlComponent( string loader = "null" )( string name = "" ) +{ + return YamlUDA( YamlType.Component, name, mixin( loader ) ); +} + +/** + * Meant to be added to members for making them YAML accessible. + * + * Params: + * name = The name of the tag in YAML. + * loader = If cannot be loaded directly, specify function used to load it. + * + * Example: + * --- + * @yamlComponent("Test") + * class Test : Component + * { + * @field("X") + * int x; + * } + * --- + */ +auto field( string loader = "null" )( string name = "" ) +{ + return YamlUDA( YamlType.Field, name, mixin( loader ) ); +} + +/// Used to create objects from yaml. The key is the YAML name of the type. +YamlObject delegate( Node )[string] createYamlObject; +/// Used to create components from yaml. The key is the YAML name of the type. +Component delegate( Node )[string] createYamlComponent; +/// Refresh any object defined from yaml. The key is the typeid of the type. +void delegate( YamlObject, Node )[TypeInfo] refreshYamlObject; + +/** + * To be placed at the top of any module defining YamlComponents. + * + * Params: + * modName = The name of the module to register. + */ +enum registerComponents( string modName ) = q{ + static this() + { + import yaml; + mixin( "import mod = $modName;" ); + + // Foreach definition in the module (classes, structs, functions, etc.) + foreach( member; __traits( allMembers, mod ) ) + { + // If the we can get the attributes of the definition + static if( __traits( compiles, __traits( getAttributes, __traits( getMember, mod, member ) ) ) ) + { + // Iterate over each attribute and try to find a YamlEntry + foreach( attrib; __traits( getAttributes, __traits( getMember, mod, member ) ) ) + { + // If we find one, process it and go to next definition. + static if( is( typeof(attrib) == YamlUDA ) ) + { + if( attrib.type == YamlType.Component ) + { + /*static if( !is( typeof( mixin( member ) ) : Component ) ) + logError( "@yamlComponent() must be placed on a class which extends Component. ", member, " fails this check." );*/ + + // If the type has a loader, register it as the create function. + if( attrib.loader ) + { + typeLoaders[ typeid(mixin( member )) ] = attrib.loader; + createYamlComponent[ member ] = ( node ) { if( node.isScalar ) return cast(Component)attrib.loader( node.get!string ); else { logInfo( "Invalid node for ", member ); return null; } }; + } + } + else if( attrib.type == YamlType.Object ) + { + /*static if( !is( typeof( mixin( member ) ) : YamlObject ) && !isYamlObject!( mixin( member ) ) ) + logError( "@yamlObject() must be placed on a class which extends YamlObject or passes isYamlObject. ", member, " fails this check." );*/ + } + else + { + logError( "@field on a type is illegal." ); + } + + registerYamlObjects!( mixin( member ) )( attrib.name.length == 0 ? member : attrib.name, attrib.type ); + } + } + } + } + } +}.replace( "$modName", modName ); + +/// For internal use only. +LoaderFunction[TypeInfo] typeLoaders; + +/// DON'T MIND ME +void registerYamlObjects( Base )( string yamlName, YamlType type ) if( is( Base : YamlObject ) ) +{ + // If no name specified, use class name. + if( yamlName == "" ) + yamlName = Base.stringof.split( "." )[ $-1 ]; + + refreshYamlObject[ typeid(Base) ] = ( comp, n ) + { + auto b = cast(Base)comp; + + // If node contains reference to this type, grab that as root instead. + Node node; + if( !n.tryFind( yamlName, node ) ) + node = n; + + // Get all members of the class (including inherited ones). + foreach( memberName; __traits( allMembers, Base ) ) + { + // If the attributes are gettable. + static if( __traits( compiles, __traits( getAttributes, __traits( getMember, Base, memberName ) ) ) ) + { + // Iterate over each attribute on the member. + foreach( attrib; __traits( getAttributes, __traits( getMember, Base, memberName ) ) ) + { + // If it is marked as a field, process. + static if( is( typeof(attrib) == YamlUDA ) ) + { + if( attrib.type == YamlType.Field ) + { + string yamlFieldName; + // If a name is not specified, use the name of the member. + if( attrib.name == "" ) + yamlFieldName = memberName; + else + yamlFieldName = attrib.name; + + // If there's an loader on the field, use that. + if( attrib.loader ) + { + static if( is( typeof( mixin( "b." ~ memberName ) ) : YamlObject ) ) + { + string newStringVal; + if( node.tryFind( yamlFieldName, newStringVal ) ) + { + auto newVal = cast(typeof(mixin( "b." ~ memberName )))attrib.loader( newStringVal ); + // If value hasn't changed, or if it was changed through code, ignore. + string oldStringVal; + if( b.yaml.tryFind( yamlFieldName, oldStringVal ) ) + { + auto oldVal = cast(typeof(mixin( "b." ~ memberName )))attrib.loader( oldStringVal ); + if( oldStringVal == newStringVal || oldVal != mixin( "b." ~ memberName ) ) + continue; + } + + mixin( "b." ~ memberName ) = newVal; + } + else + { + logDebug( "Failed using attrib loader for ", yamlFieldName ); + } + } + } + // If the type of the field has a loader, use that. + else if( typeid( mixin( "b." ~ memberName ) ) in typeLoaders ) + { + static if( is( typeof( mixin( "b." ~ memberName ) ) : YamlObject ) ) + { + auto loader = typeid( mixin( "b." ~ memberName ) ) in typeLoaders; + + string newStringVal; + if( node.tryFind( yamlFieldName, newStringVal ) ) + { + auto newVal = cast(typeof(mixin( "b." ~ memberName )))( *loader )( newStringVal ); + // If value hasn't changed, ignore. + string oldStringVal; + if( b.yaml.tryFind( yamlFieldName, oldStringVal ) ) + { + auto oldVal = cast(typeof(mixin( "b." ~ memberName )))( *loader )( oldStringVal ); + if( oldStringVal == newStringVal || oldVal != mixin( "b." ~ memberName ) ) + continue; + } + + mixin( "b." ~ memberName ) = newVal; + } + else + { + logDebug( "Failed using typeloader for ", yamlFieldName ); + } + } + } + // Else just try to parse the yaml. + else + { + typeof( __traits( getMember, b, memberName ) ) val; + if( node.tryFind( yamlFieldName, val ) ) + { + // If value hasn't changed, ignore. + typeof( __traits( getMember, b, memberName ) ) oldVal; + if( b.yaml.tryFind( yamlFieldName, oldVal ) ) + { + if( oldVal == val || oldVal != mixin( "b." ~ memberName ) ) + continue; + } + + mixin( "b." ~ memberName ) = val; + } + else + { + logDebug( "Failed creating ", yamlFieldName, " of type ", typeof( mixin( "b." ~ memberName ) ).stringof ); + logDebug( "Typeloaders: ", typeLoaders ); + } + } + } + + break; + } + // If the user forgot (), remind them. + else static if( is( typeof(attrib == typeof(field) ) ) ) + { + static assert( false, "Don't forget () after field on " ~ memberName ); + } + } + } + } + + // Set the yaml property so the class has access to the yaml that created it. + b.yaml = node; + }; + + // Make sure the type is instantiable + static if( __traits( compiles, new Base ) ) + { + static if( is( Base : Component ) ) + { + if( type == YamlType.Component ) + { + if( auto loader = typeid( Base ) in typeLoaders ) + { + createYamlComponent[ yamlName ] = ( node ) + { + return cast(Component)( *loader )( node.get!string ); + }; + } + else + { + createYamlComponent[ yamlName ] = ( node ) + { + // Create an instance of the class to assign things to. + Component b = new Base; + + refreshYamlObject[ typeid(Base) ]( b, node ); + + return b; + }; + } + } + } + if( type == YamlType.Object ) + { + createYamlObject[ yamlName ] = ( node ) + { + // Create an instance of the class to assign things to. + YamlObject b = new Base; + + refreshYamlObject[ typeid(Base) ]( b, node ); + + return b; + }; + } + } +} + +enum YamlType { Object, Component, Field } +private alias LoaderFunction = YamlObject delegate( string ); + +struct YamlUDA +{ + YamlType type; + string name; + LoaderFunction loader; +} diff --git a/source/components/icomponent.d b/source/components/icomponent.d deleted file mode 100644 index 0bdae555..00000000 --- a/source/components/icomponent.d +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Defines the IComponent interface, which is the base for all components. - */ -module components.icomponent; -import core, graphics; - -import yaml; - -/** - * Interface for components to implement. - */ -interface IComponent -{ -private: - static GameObject[ IComponent ] owners; -public: - /** - * Functions to call when creating components. - */ - static IComponent function( Node, GameObject )[string] initializers; - /** - * Functions to call when refreshing a component. - */ - static void function( Node, GameObject )[string] refreshers; - - /** - * The GameObject that owns this component. - */ - final @property GameObject owner() - { - auto owner = this in owners; - if( owner ) - return *owner; - else - return null; - } - - /// ditto - final @property void owner( GameObject newOwner ) - { - owners[ this ] = newOwner; - } - - /** - * Function called on update. - */ - void update(); - - /** - * Function called on shutdown. - */ - void shutdown(); -} diff --git a/source/components/lights.d b/source/components/lights.d index 9252f41f..80ab6431 100644 --- a/source/components/lights.d +++ b/source/components/lights.d @@ -9,18 +9,20 @@ import utility; import gl3n.linalg, gl3n.aabb; import derelict.opengl3.gl3; +mixin( registerComponents!q{components.lights} ); + /** * Base class for lights. */ -class Light : IComponent +abstract class Light : Component { private: - vec3 _color; bool _castShadows; public: /// The color the light gives off. - mixin( Property!( _color, AccessModifier.Public ) ); + @field( "Color" ) + vec3 color; /// If it should cast shadows mixin( Property!( _castShadows ) ); @@ -30,16 +32,6 @@ public: _castShadows = false; } - static this() - { - IComponent.initializers[ "Light" ] = ( Node yml, GameObject obj ) - { - obj.light = yml.get!Light; - obj.light.owner = obj; - return obj.light; - }; - } - override void update() { } override void shutdown() { } } @@ -47,9 +39,10 @@ public: /** * Ambient Light */ -class AmbientLight : Light -{ - this( vec3 color ) +@yamlComponent() +class AmbientLight : Light +{ + this( vec3 color = vec3() ) { super( color ); } @@ -58,10 +51,10 @@ class AmbientLight : Light /** * Directional Light */ +@yamlComponent() class DirectionalLight : Light { private: - vec3 _direction; uint _shadowMapFrameBuffer; uint _shadowMapTexture; mat4 _projView; @@ -69,7 +62,8 @@ private: public: /// The direction the light points in. - mixin( Property!( _direction, AccessModifier.Public ) ); + @field( "Direction" ) + vec3 direction; /// The FrameBuffer for the shadowmap. mixin( Property!( _shadowMapFrameBuffer ) ); /// The shadow map's depth texture. @@ -77,9 +71,9 @@ public: mixin( Property!( _projView ) ); mixin( Property!( _shadowMapSize ) ); - this( vec3 color, vec3 direction, bool castShadows ) + this( vec3 color = vec3(), vec3 direction = vec3(), bool castShadows = false ) { - this.direction = direction; + this.direction = direction; super( color ); this.castShadows = castShadows; if( castShadows ) @@ -162,7 +156,7 @@ public: // get mins and maxes in view space vec3 mins, maxes; for( int i = 0; i < 3; i++ ) - { + { if( frustum.min.vector[ i ] < frustum.max.vector[ i ] ) { mins.vector[ i ] = frustum.min.vector[ i ]; @@ -176,8 +170,8 @@ public: } // build the projectionView matrix - mat4 bias = mat4( vec4( .5, 0, 0, .5), - vec4( 0, .5, 0, .5), + mat4 bias = mat4( vec4( .5, 0, 0, .5), + vec4( 0, .5, 0, .5), vec4( 0, 0, .5, .5), vec4( 0, 0, 0, 1) ); float magicNumber = 1.5f; // literally the worst @@ -188,20 +182,21 @@ public: /** * Point Light */ +@yamlComponent() class PointLight : Light { private: - float _radius; - float _falloffRate; mat4 _matrix; public: /// The area that lighting will be calculated for. - mixin( Property!( _radius, AccessModifier.Public ) ); + @field( "Radius" ) + float radius; /// The light's exponential attenuation modifier. - mixin( Property!( _falloffRate, AccessModifier.Public ) ); + @field( "FalloffRate" ) + float falloffRate; - this( vec3 color, float radius, float falloffRate ) + this( vec3 color = vec3(), float radius = 0.0f, float falloffRate = 0.0f ) { this.radius = radius; this.falloffRate = falloffRate; @@ -235,10 +230,11 @@ public: /** * SpotLight Stub */ +@yamlComponent() class SpotLight : Light { public: - this( vec3 color ) + this( vec3 color = vec3() ) { super( color ); } diff --git a/source/components/material.d b/source/components/material.d index aeb615c8..fba5182b 100644 --- a/source/components/material.d +++ b/source/components/material.d @@ -8,90 +8,56 @@ import yaml; import derelict.opengl3.gl3, derelict.freeimage.freeimage; import std.variant, std.conv, std.string; +mixin( registerComponents!q{components.material} ); + /** * A collection of textures that serve different purposes in the rendering pipeline. */ +@yamlComponent!( q{name => Assets.get!Material( name )} )() +@yamlObject() final class Material : Asset { -private: - Texture _diffuse, _normal, _specular; - immutable string _name; +package: + @field( "Name" ) + string _name; public: /// The diffuse (or color) map. - mixin( Property!(_diffuse, AccessModifier.Public) ); + @field( "Diffuse" ) + Texture diffuse; /// The normal map, which specifies which way a face is pointing at a given pixel. - mixin( Property!(_normal, AccessModifier.Public) ); + @field( "Normal" ) + Texture normal; /// The specular map, which specifies how shiny a given point is. - mixin( Property!(_specular, AccessModifier.Public) ); + @field( "Specular" ) + Texture specular; /// The name of the material. mixin( Getter!_name ); /** * Default constructor, makes sure everything is initialized to default. */ - this( string name ) + this( string name = "" ) { super( Resource( "" ) ); - _diffuse = _specular = defaultTex; - _normal = defaultNormal; + diffuse = specular = defaultTex; + normal = defaultNormal; _name = name; } - /** - * Create a Material from a Yaml node. - * - * Params: - * yamlObj = The YAML object to pull the data from. - * - * Returns: A new material with specified maps. - */ - static Material createFromYaml( Node yamlObj ) - { - auto obj = new Material( yamlObj[ "Name" ].get!string ); - - obj.refresh( yamlObj ); - - return obj; - } - - /** - * Refresh the asset. - */ - override void refresh() { } - - /** - * Refresh the asset. - * - * Params: - * yamlObj = The new makeup of the material. - */ - void refresh( Node yamlObj ) - { - string prop; - - if( yamlObj.tryFind( "Diffuse", prop ) ) - diffuse = Assets.get!Texture( prop ); - - if( yamlObj.tryFind( "Normal", prop ) ) - normal = Assets.get!Texture( prop ); - - if( yamlObj.tryFind( "Specular", prop ) ) - specular = Assets.get!Texture( prop ); - } - /** * Shuts down the material, making sure all references are released. */ override void shutdown() { - _diffuse = _specular = _normal = null; + diffuse = specular = normal = null; } } /** * TODO */ +@yamlComponent!( q{name => Assets.get!Texture( name )} )() class Texture : Asset { protected: @@ -210,29 +176,3 @@ private: return def; } - -static this() -{ - IComponent.initializers[ "Material" ] = ( Node yml, GameObject obj ) - { - if( yml.isScalar ) - { - obj.material = Assets.get!Material( yml.get!string ); - } - else if( yml.isMapping ) - { - logError( "Inline material definitions are not yet supported." ); - } - else - { - logError( "Unsupported format for Material in ", obj.name, "." ); - } - - return null; - }; - - IComponent.refreshers[ "Material" ] = ( Node yml, GameObject obj ) - { - IComponent.initializers[ "Material" ]( yml, obj ); - }; -} diff --git a/source/components/mesh.d b/source/components/mesh.d index 986ccf01..a558f5cf 100644 --- a/source/components/mesh.d +++ b/source/components/mesh.d @@ -9,6 +9,15 @@ import gl3n.linalg, gl3n.aabb; import std.stdio, std.stream, std.format, std.math, std.string; +mixin( registerComponents!q{components.mesh} ); + +/*Mesh getMesh( string name ) +{ + auto mesh = Assets.get!Mesh( yml.get!string ); + + return mesh; +}*/ + /** * Loads and manages meshes into OpenGL. * @@ -48,12 +57,14 @@ import std.stdio, std.stream, std.format, std.math, std.string; * Ogre XML * Q3D */ +@yamlComponent!( q{name => Assets.get!Mesh( name )} )() class Mesh : Asset { private: uint _glVertexArray, _numVertices, _numIndices, _glIndexBuffer, _glVertexBuffer; bool _animated; AABB _boundingBox; + AssetAnimation _animationData; public: /// TODO @@ -68,6 +79,8 @@ public: mixin( Property!_glVertexBuffer ); /// TODO mixin( Property!_animated ); + /// Stores all data about animations on the mesh. + mixin( Property!( _animationData, AccessModifier.Package ) ); /// The bounding box of the mesh. mixin( RefGetter!_boundingBox ); @@ -283,8 +296,6 @@ public: glBindVertexArray( 0 ); } - override void update() { } - /** * Refresh the asset. */ @@ -298,15 +309,22 @@ public: aiProcess_JoinIdenticalVertices | aiProcess_SortByPType ); assert( scene, "Failed to load scene file '" ~ resource.fullPath ~ "' Error: " ~ aiGetErrorString().fromStringz ); - Mesh tempMesh; - // Add mesh if( scene.mNumMeshes > 0 ) { - if( scene.mNumAnimations > 0 ) - Assets.animations[ resource.baseFileName ] = new AssetAnimation( scene.mAnimations, scene.mNumAnimations, scene.mMeshes[ 0 ], scene.mRootNode ); + Mesh tempMesh = new Mesh( resource.fullPath, scene.mMeshes[ 0 ] ); - tempMesh = new Mesh( resource.fullPath, scene.mMeshes[ 0 ] ); + if( scene.mNumAnimations > 0 ) + tempMesh.animationData = new AssetAnimation( scene.mAnimations, scene.mNumAnimations, scene.mMeshes[ 0 ], scene.mRootNode ); + + // Copy attributes + _glVertexArray = tempMesh._glVertexArray; + _numVertices = tempMesh._numVertices; + _numIndices = tempMesh._numIndices; + _glIndexBuffer = tempMesh._glIndexBuffer; + _glVertexBuffer = tempMesh._glVertexBuffer; + _animated = tempMesh._animated; + _boundingBox = tempMesh._boundingBox; } else { @@ -316,15 +334,6 @@ public: // Release mesh aiReleaseImport( scene ); - - // Copy attributes - _glVertexArray = tempMesh._glVertexArray; - _numVertices = tempMesh._numVertices; - _numIndices = tempMesh._numIndices; - _glIndexBuffer = tempMesh._glIndexBuffer; - _glVertexBuffer = tempMesh._glVertexBuffer; - _animated = tempMesh._animated; - _boundingBox = tempMesh._boundingBox; } /** @@ -337,7 +346,6 @@ public: } } - /** * Helper function that calculates a modifier for the reconstructed bitangent based on regenerating them * May be needed elsewhere @@ -357,22 +365,3 @@ private float calcTangentHandedness( aiVector3D nor, aiVector3D tan, aiVector3D return (dot(cross(n,t),b) > 0.0f) ? -1.0f : 1.0f; } - -static this() -{ - import yaml; - IComponent.initializers[ "Mesh" ] = ( Node yml, GameObject obj ) - { - obj.mesh = Assets.get!Mesh( yml.get!string ); - - // If the mesh has animation also add animation component - if( obj.mesh.animated ) - { - auto anim = new Animation( Assets.get!AssetAnimation( yml.get!string ) ); - obj.addComponent( anim ); - obj.animation = anim; - } - - return obj.mesh; - }; -} diff --git a/source/components/package.d b/source/components/package.d index cee51b79..86c849e8 100644 --- a/source/components/package.d +++ b/source/components/package.d @@ -1,11 +1,10 @@ module components; public: +import components.component; import components.animation; -import components.icomponent; import components.assets; import components.material; import components.mesh; import components.camera; import components.lights; import components.userinterface; -import components.behavior; diff --git a/source/components/userinterface.d b/source/components/userinterface.d index 346934eb..2b034b84 100644 --- a/source/components/userinterface.d +++ b/source/components/userinterface.d @@ -163,7 +163,7 @@ public: /** * Creates an Awesomium web view texture */ -class AwesomiumView : Texture, IComponent +class AwesomiumView : Texture { private: version( Windows ) diff --git a/source/core/dgame.d b/source/core/dgame.d index 23071535..08040ead 100644 --- a/source/core/dgame.d +++ b/source/core/dgame.d @@ -149,6 +149,8 @@ public: // End drawing Graphics.endDraw(); + + //break; } stop(); @@ -200,6 +202,8 @@ private: bench!( { Prefabs.initialize(); } )( "Prefabs init" ); bench!( { UserInterface.initializeAwesomium(); } )( "UI init" ); bench!( { DGame.instance.onInitialize(); } )( "Game init" ); + + debug scheduleIntervaledTask( 1.seconds, { currentState = EngineState.Refresh; return false; } ); } /** diff --git a/source/core/gameobject.d b/source/core/gameobject.d index 41edeaf6..69402f2d 100644 --- a/source/core/gameobject.d +++ b/source/core/gameobject.d @@ -57,11 +57,11 @@ private: Camera _camera; GameObject _parent; GameObject[] _children; - IComponent[TypeInfo] componentList; + Component[TypeInfo] componentList; string _name; ObjectStateFlags* _stateFlags; bool canChangeName; - Behaviors _behaviors; + Node _yaml; static uint nextId = 1; package: @@ -69,27 +69,27 @@ package: public: /// The current transform of the object. - mixin( RefGetter!( _transform, AccessModifier.Public ) ); + mixin( RefGetter!_transform ); /// The Material belonging to the object. - mixin( Property!( _material, AccessModifier.Public ) ); + mixin( Property!_material ); /// The Mesh belonging to the object. - mixin( Property!( _mesh, AccessModifier.Public ) ); + mixin( Property!_mesh ); /// The animation on the object. - mixin( Property!( _animation, AccessModifier.Public ) ); + mixin( Property!_animation ); /// The light attached to this object. - mixin( Property!( _light, AccessModifier.Public ) ); + mixin( Property!_light ); /// The camera attached to this object. - mixin( Property!( _camera, AccessModifier.Public ) ); + mixin( Property!_camera ); /// The object that this object belongs to. - mixin( Property!( _parent, AccessModifier.Public ) ); + mixin( Property!_parent ); /// All of the objects which list this as parent - mixin( Property!( _children, AccessModifier.Public ) ); + mixin( Property!_children ); + /// The yaml node that created the object. + mixin( RefGetter!_yaml ); /// The current update settings mixin( Property!( _stateFlags, AccessModifier.Public ) ); /// The name of the object. mixin( Getter!_name ); - /// The scripts this object owns. - mixin( RefGetter!_behaviors ); /// ditto mixin( ConditionalSetter!( _name, q{canChangeName}, AccessModifier.Public ) ); /// The ID of the object. @@ -137,27 +137,6 @@ public: obj.transform.rotation = quat.identity.rotatex( transVec.x.radians ).rotatey( transVec.y.radians ).rotatez( transVec.z.radians ); } - if( yamlObj.tryFind( "Behaviors", innerNode ) ) - { - if( !innerNode.isSequence ) - { - logWarning( "Behaviors tag of ", obj.name, " must be a sequence." ); - } - else - { - foreach( Node behavior; innerNode ) - { - string className; - Node fields; - if( !behavior.tryFind( "Class", className ) ) - logFatal( "Behavior element in ", obj.name, " must have a Class value." ); - if( !behavior.tryFind( "Fields", fields ) ) - fields = Node( YAMLNull() ); - obj.behaviors.createBehavior( className, fields ); - } - } - } - if( yamlObj.tryFind( "Children", innerNode ) ) { if( innerNode.isSequence ) @@ -181,36 +160,24 @@ public: // Init components foreach( string key, Node componentNode; yamlObj ) { - if( key == "Name" || key == "InstanceOf" || key == "Transform" || key == "Children" || key == "Behaviors" ) + if( key == "Name" || key == "InstanceOf" || key == "Transform" || key == "Children" ) continue; - if( auto init = key in IComponent.initializers ) - obj.addComponent( (*init)( componentNode, obj ) ); + if( auto init = key in createYamlComponent ) + { + auto newComp = cast(Component)(*init)( componentNode ); + obj.addComponent( newComp ); + newComp.owner = obj; + } else logWarning( "Unknown key: ", key ); } - obj.behaviors.onInitialize(); + foreach( comp; obj.componentList ) + comp.initialize(); return obj; } - /** - * Create a GameObject from a Yaml node. - * - * Params: - * fields = The YAML node to pull info from. - * - * Returns: - * A tuple of the object created at index 0, and the behavior at index 1. - */ - static auto createWithBehavior( BehaviorT )( Node fields = Node( YAMLNull() ) ) - { - auto newObj = new GameObject; - - newObj.behaviors.createBehavior!BehaviorT( fields ); - - return tuple( newObj, newObj.behaviors.get!BehaviorT ); - } /** * Creates basic GameObject with transform and connection to transform's emitter. @@ -218,7 +185,6 @@ public: this() { _transform = Transform( this ); - _behaviors = Behaviors( this ); // Create default material material = new Material( "default" ); @@ -236,9 +202,6 @@ public: */ final void update() { - if( stateFlags.updateBehaviors ) - behaviors.onUpdate(); - if( stateFlags.updateComponents ) foreach( ci, component; componentList ) component.update(); @@ -253,7 +216,8 @@ public: */ final void draw() { - behaviors.onDraw(); + foreach( component; componentList ) + component.draw(); foreach( obj; children ) obj.draw(); @@ -264,7 +228,8 @@ public: */ final void shutdown() { - behaviors.onShutdown(); + foreach( component; componentList ) + component.shutdown(); foreach( obj; children ) obj.shutdown(); @@ -278,14 +243,21 @@ public: */ final void refresh( Node node ) { - foreach( string name, Node component; node ) + // Update components + foreach( type; componentList.keys ) { - if( auto refresher = name in IComponent.refreshers ) + if( auto refresher = type in refreshYamlObject ) + { + ( *refresher )( componentList[ type ], node ); + componentList[ type ].refresh(); + } + else { - ( *refresher )( component, this ); + componentList.remove( type ); } } + // Update children Node yamlChildren; if( node.tryFind( "Children", yamlChildren ) && yamlChildren.isSequence ) { @@ -328,26 +300,54 @@ public: removeChild( child ); } } - - behaviors.onRefresh(); } /** * Adds a component to the object. */ - final void addComponent( T )( T newComponent ) if( is( T : IComponent ) ) + final void addComponent( Component newComponent ) { if( newComponent ) - componentList[ typeid(T) ] = newComponent; + { + componentList[ typeid(newComponent) ] = newComponent; + + enum setProperty( string prop ) = q{ + if( typeid(newComponent) == typeid(typeof($prop)) ) + { + $prop = cast(typeof($prop))newComponent; + return; + } + }.replace( "$prop", prop ); + + mixin( setProperty!q{_material} ); + mixin( setProperty!q{_camera} ); + mixin( setProperty!q{_animation} ); + if( typeid(newComponent) == typeid(Mesh) ) + { + _mesh = cast(Mesh)newComponent; + + if( _mesh.animated ) + addComponent( _mesh.animationData.getComponent() ); + + return; + } + else if( typeid(newComponent) == typeid(Light) || typeid(newComponent).base == typeid(Light) ) + { + _light = cast(Light)newComponent; + return; + } + } } /** * Gets a component of the given type. */ - deprecated( "Make properties for any component being accessed." ) - final T getComponent( T )() if( is( T : IComponent ) ) + final T getComponent( T )() if( is( T : Component ) ) { - return componentList[ typeid(T) ]; + if( auto comp = typeid(T) in componentList ) + return cast(T)*comp; + else + return null; } /** diff --git a/source/core/prefabs.d b/source/core/prefabs.d index 63f74318..1285aa6c 100644 --- a/source/core/prefabs.d +++ b/source/core/prefabs.d @@ -8,6 +8,8 @@ import yaml; import gl3n.linalg; import std.variant; +mixin( registerComponents!q{core.prefabs} ); + /** * Prefabs manages prefabs and allows access to them. */ @@ -34,7 +36,9 @@ public: auto object = objFile[0]; auto name = object[ "Name" ].as!string; - auto newFab = new Prefab( object ); + //auto newFab = new Prefab( object ); + auto newFab = cast(Prefab)createYamlObject[ "Prefab" ]( object ); + newFab.name = name; prefabs[ name ] = newFab; prefabResources[ objFile[1] ] ~= newFab; } @@ -46,7 +50,7 @@ public: void refresh() { refreshYamlObjects!( - node => new Prefab( node ), + node => cast(Prefab)createYamlObject[ "Prefab" ]( node ), node => node[ "Name" ].get!string in prefabs, ( node, fab ) => prefabs[ node[ "Name" ].get!string ] = fab, fab => prefabs.remove( fab.name ) ) @@ -60,34 +64,12 @@ private: /** * A prefab that allows for quick object creation. */ -final class Prefab +@yamlObject() +final class Prefab : YamlObject { public: /// The name of the prefab. - mixin( Getter!_name ); - - /** - * Create a prefab from a YAML node. - * - * Params: - * yml = The YAML node to get info from. - */ - this( Node yml ) - { - refresh( yml ); - this._name = yml[ "Name" ].get!string; - } - - /** - * Refreshes the makeup of the prefab. - * - * Params: - * yml = The new yaml for the prefab. - */ - void refresh( Node yml ) - { - this.yaml = yml; - } + mixin( Property!_name ); /** * Creates a GameObject instance from the prefab. @@ -101,6 +83,5 @@ public: } private: - immutable string _name; - Node yaml; + string _name; } diff --git a/source/core/reflection.d b/source/core/reflection.d index 28a49291..ef46c47f 100644 --- a/source/core/reflection.d +++ b/source/core/reflection.d @@ -1,22 +1,6 @@ module core.reflection; import core; -/** - * Meant to be added to members for making them YAML accessible. - * Example: - * --- - * class Test : GameObject - * { - * @Tweakable - * int x; - * } - * --- - */ -struct Tweakable -{ - -} - /** * Initializes reflection things. */ diff --git a/source/graphics/adapters/adapter.d b/source/graphics/adapters/adapter.d index f85a62ca..078b48c6 100644 --- a/source/graphics/adapters/adapter.d +++ b/source/graphics/adapters/adapter.d @@ -274,7 +274,7 @@ public: void shadowPass() { foreach( light; directionalLights ) - { + { if( light.castShadows ) { glBindFramebuffer( GL_FRAMEBUFFER, light.shadowMapFrameBuffer ); diff --git a/source/utility/config.d b/source/utility/config.d index f57e0255..92d6143e 100644 --- a/source/utility/config.d +++ b/source/utility/config.d @@ -2,14 +2,15 @@ * Defines the static class Config, which handles all configuration options. */ module utility.config; +import components.component; import utility.resources, utility.output; public import yaml; import std.variant, std.algorithm, std.traits, std.range, - std.array, std.conv, std.file, std.typecons; + std.array, std.conv, std.file, std.typecons, std.path; -private string fileToYaml( string filePath ) { return filePath.replace( "\\", "/" ).replace( "../", "" ).replace( "/", "." ).replace( ".yml", "" ); } +private string fileToYaml( string filePath ) { return filePath.relativePath( getcwd() ).replace( "\\", "/" ).replace( "../", "" ).replace( "/", "." ).replace( ".yml", "" ); } /** * Place this mixin anywhere in your game code to allow the Content.yml file @@ -85,31 +86,6 @@ static this() result.w = vals[ 3 ].to!float; return result; } ); - constructor.addConstructorMapping( "!Light-Directional", ( ref Node node ) - { - vec3 color; - vec3 dir; - bool shadows = false; - node.tryFind( "Color", color ); - node.tryFind( "Direction", dir ); - node.tryFind( "CastShadows", shadows ); - return cast(Light)new DirectionalLight( color, dir, shadows ); - } ); - constructor.addConstructorMapping( "!Light-Ambient", ( ref Node node ) - { - vec3 color; - node.tryFind( "Color", color ); - return cast(Light)new AmbientLight( color ); - } ); - constructor.addConstructorMapping( "!Light-Point", ( ref Node node ) - { - vec3 color; - float radius, falloffRate; - node.tryFind( "Color", color ); - node.tryFind( "Radius", radius ); - node.tryFind( "FalloffRate", falloffRate ); - return cast(Light)new PointLight( color, radius, falloffRate ); - } ); constructor.addConstructorScalar( "!Verbosity", &constructConv!Verbosity ); constructor.addConstructorScalar( "!Shader", ( ref Node node ) => Shaders.get( node.get!string ) ); //constructor.addConstructorScalar( "!Texture", ( ref Node node ) => Assets.get!Texture( node.get!string ) ); @@ -119,7 +95,7 @@ static this() /** * Process all yaml files in a directory. - * + * * Params: * folder = The folder to iterate over. */ @@ -158,11 +134,11 @@ Tuple!( Node, Resource )[] loadYamlFiles( string folder ) if( fileContent.isSequence ) { foreach( Node childChild; fileContent ) - nodes ~= tuple( childChild, Resource( "" ) ); + nodes ~= tuple( childChild, configFile ); } - else + else if( fileContent.isMapping ) { - nodes ~= tuple( fileContent, Resource( "" ) ); + nodes ~= tuple( fileContent, configFile ); } } } @@ -172,7 +148,7 @@ Tuple!( Node, Resource )[] loadYamlFiles( string folder ) /** * Process all documents files in a directory. - * + * * Params: * folder = The folder to iterate over. */ @@ -183,7 +159,7 @@ Node[] loadYamlDocuments( string path ) /** * Processes all yaml files in a directory, and converts each document into an object of type T. - * + * * Params: * folder = The folder to look in. */ @@ -194,7 +170,7 @@ T[] loadYamlObjects( T )( string folder ) /** * Load a yaml file with the engine-specific mappings. - * + * * Params: * filePath = The path to file to load. */ @@ -212,7 +188,7 @@ Node loadYamlFile( string filePath ) { logFatal( "Error parsing file ", Resource( filePath ).baseFileName, ": ", e.msg ); return Node(); - } + } } else { @@ -222,7 +198,7 @@ Node loadYamlFile( string filePath ) /** * Load a yaml file with the engine-specific mappings. - * + * * Params: * filePath = The path to file to load. */ @@ -243,7 +219,7 @@ Node[] loadAllDocumentsInYamlFile( string filePath ) { logFatal( "Error parsing file ", Resource( filePath ).baseFileName, ": ", e.msg ); return []; - } + } } else { @@ -257,12 +233,16 @@ Node[] loadAllDocumentsInYamlFile( string filePath ) * Params: * createFunc = The function used to create a new object of type ObjectType. * existsFunc = The function that checks if a node is already stored. Returns a pointer to the object. - * addToResourcesFunc =Adds the + * addToResourcesFunc =Adds the * ObjectType = The type of object stored in YAML. * objectResources = The map of files to the objects they store. */ void refreshYamlObjects( alias createFunc, alias existsFunc, alias addToResourcesFunc, alias removeFunc, ObjectType )( ref ObjectType[][Resource] objectResources ) { + // Disable refreshing YAML from a single file. + if( !contentNode.isNull ) + return; + // Iterate over each file, and it's objects foreach( file, ref objs; objectResources.dup ) { @@ -299,7 +279,15 @@ void refreshYamlObjects( alias createFunc, alias existsFunc, alias addToResource else auto mat = oldMat; - mat.refresh( node ); + if( mat.yaml != node ) + { + static if( __traits( compiles, mat.refresh( node ) ) ) + mat.refresh( node ); + else + refreshYamlObject[ typeid(mat) ]( mat, node ); + mat.yaml = node; + } + objsFound[ mat ] = true; objectResources[ file ] ~= mat; } @@ -342,7 +330,7 @@ unittest { import std.stdio; import std.exception; - + writeln( "Dash Config find unittest" ); auto n1 = Node( [ "test1": 10 ] ); @@ -350,13 +338,13 @@ unittest assert( n1.find!int( "test1" ) == 10, "Config.find error." ); assertThrown!YAMLException(n1.find!int( "dontexist" )); - + // nested test auto n2 = Node( ["test2": n1] ); auto n3 = Node( ["test3": n2] ); - + assert( n3.find!int( "test3.test2.test1" ) == 10, "Config.find nested test failed"); - + auto n4 = Loader.fromString( "test3:\n" " test2:\n" @@ -391,6 +379,10 @@ final bool tryFind( T )( Node node, string path, ref T result ) nothrow @safe foreach( Node element; res ) result ~= element.get!U; } + else static if( __traits( compiles, res.getObject!T ) ) + { + result = res.getObject!T; + } else { result = res.get!T; @@ -549,7 +541,7 @@ final T getObject( T )( Node node ) } } } - + return toReturn; } unittest @@ -599,6 +591,7 @@ public static: if( exists( Resources.CompactContentFile ) ) { logDebug( "Using Content.yml file." ); + configFile = Resource( Resources.CompactContentFile ); contentNode = Resources.CompactContentFile.loadYamlFile(); } else @@ -608,27 +601,30 @@ public static: } } - config = configFile.fullPath.loadYamlFile(); + config = Resources.ConfigFile.loadYamlFile(); } void refresh() { - version( EmbedContent ) - { - initialize(); - } + // No need to refresh, there can be no changes. + version( EmbedContent ) { } else { - if( exists( Resources.CompactContentFile ~ ".yml" ) ) + if( exists( Resources.CompactContentFile ) ) { logDebug( "Using Content.yml file." ); - contentNode = Resources.CompactContentFile.loadYamlFile(); + if( configFile.needsRefresh ) + { + contentNode = Node( YAMLNull() ); + contentNode = Resources.CompactContentFile.loadYamlFile(); + config = Resources.ConfigFile.loadYamlFile(); + } } else { logDebug( "Using normal content directory." ); if( configFile.needsRefresh ) - config = configFile.fullPath.loadYamlFile(); + config = Resources.ConfigFile.loadYamlFile(); } } } diff --git a/source/utility/tasks.d b/source/utility/tasks.d index 4ed64437..a4e57287 100644 --- a/source/utility/tasks.d +++ b/source/utility/tasks.d @@ -343,7 +343,7 @@ void scheduleIntervaledTask( Duration interval, uint numExecutions, bool delegat void executeTasks() { size_t[] toRemove; // Indicies of tasks which are done - foreach( i, task; parallel( scheduledTasks ) ) + foreach( i, task; scheduledTasks/*.parallel*/ ) { if( task() ) synchronized toRemove ~= i;