<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.WorldSpace.ChunkSpace;
using OpenTK;

namespace isometricgame.GameEngine.WorldSpace.Generators
{
    public class GenericGenerator : Generator
    {
        public GenericGenerator(int seed) 
            : base(seed)
        {
        }

        public override Chunk CreateChunk(Vector2 pos)
        {
            Chunk c = new Chunk(pos);
            float[,] noiseMap = Perlin.InterprolateNoise(pos);

            RenderStructure ground = new RenderStructure(Chunk.CHUNK_TILE_WIDTH, Chunk.CHUNK_TILE_HEIGHT);

            for (int x = 0; x < Chunk.CHUNK_TILE_WIDTH; x++)
            {
                for (int y = 0; y < Chunk.CHUNK_TILE_WIDTH; y++)
                {
                    int z = (int)noiseMap[x, y];
                    int id = (z > 3) ? 1 : 0;
                    ground.structuralUnits[x][y] = new RenderUnit(id, 0, new Vector3(x, y, z));
                }
            }

            c.AddStructure(ground);

            return c;
        }

        public override void FinalizeChunk(ChunkDirectory chunkDirectory, ref Chunk c)
        {
            chunkDirectory.PerformTileOrientation(c.TileSpaceLocation, ref c.ChunkStructures[0]);
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.WorldSpace;
using isometricgame.GameEngine.WorldSpace.ChunkSpace;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine
{
    public class Camera
    {
        private float zNear = 0.01f, zFar = 10f;
        private float zoom = 0.2f;
        
        private Vector3 position = new Vector3(0,0,0);
        private float velocity = 3;
        private Scene scene;

        private GameObject focusObject;

        public float Velocity { get => velocity; set => velocity = value; }
        public Vector3 Position { get => position; set => position = value; }
        public GameObject FocusObject { get => focusObject; set => focusObject = value; }

        public Vector3 TargetPosition => (focusObject != null) ? focusObject.Position : position;

        public float Zoom { get => zoom; set => zoom = value; }

        public float Iso_X => Chunk.CartesianToIsometric_X(position.X, position.Y);
        public float Iso_Y => Chunk.CartesianToIsometric_Y(position.X, position.Y, position.Z);

        public Camera(Scene scene)
        {
            this.scene = scene;
        }
        
        public void Pan_Linear(float deltaT)
        {
            Vector3 pos = (focusObject != null) ? focusObject.Position : Vector3.Zero;
            Vector3 distanceVector = pos - position;

            position += (distanceVector * velocity) * deltaT;
        }

        public void Pan_Pow2(float deltaT)
        {

        }

        public void Pan_Pow3(float deltaT)
        {

        }

        public Matrix4 GetView()
        {
            //return Matrix4.CreateTranslation(new Vector3(-Iso_X, -Iso_Y, -1000f)) * Matrix4.CreatePerspectiveFieldOfView(fov, aspect, zNear, zFar);
            //return Matrix4.LookAt(new Vector3(0, 0, 1), new Vector3(0, 0, 0), new Vector3(0, 1, 0))
            //    * Matrix4.CreateScale(fov, fov, 1) *
            //    Matrix4.CreateTranslation(Iso_X, Iso_Y, 1f);
            return Matrix4.CreateTranslation(-Iso_X, -Iso_Y, -1f) 
                * Matrix4.CreateScale(zoom) 
                * Matrix4.CreateOrthographic(scene.Game.Width, scene.Game.Height, zNear, zFar);
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using isometricgame.GameEngine.Events.Arguments;
using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.Systems.Rendering;
using isometricgame.GameEngine.WorldSpace;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace isometricgame.GameEngine.Systems.Rendering
{
    public class RenderService : GameSystem
    {
        private SpriteLibrary SpriteLibrary;

        private Matrix4 projection;
        private Matrix4 cachedWorldMatrix;

        private Shader shader;

        private string shaderSource_Vert, shaderSource_Frag;

        public RenderService(Game game, int windowWidth, int windowHeight) 
            : base(game)
        {
            AdjustProjection(windowWidth, windowHeight);
            cachedWorldMatrix = Matrix4.CreateTranslation(new Vector3(0,0,0));

            shaderSource_Vert = Path.Combine(game.GAME_DIRECTORY_SHADERS, "shader.vert");
            shaderSource_Frag = Path.Combine(game.GAME_DIRECTORY_SHADERS, "shader.frag");

            shader = new Shader(shaderSource_Vert, shaderSource_Frag);
        }

        public override void Load()
        {
            SpriteLibrary = Game.GetSystem<SpriteLibrary>();
        }

        public override void Unload()
        {
            shader.Dispose();
        }

        public void AdjustProjection(int width, int height)
        {
            projection = Matrix4.CreateOrthographicOffCenter(0, width, height, 0, 0, 1);
        }

        internal void BeginRender()
        {
            GL.ClearColor(Color.FromArgb(5, 5, 25));
            GL.Clear(ClearBufferMask.ColorBufferBit);

            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref projection);

            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
            GL.Enable(EnableCap.Texture2D);

            GL.MatrixMode(MatrixMode.Modelview);
        }

        public void RenderScene(Scene scene, FrameArgument e)
        {
            //Matrix4 view = World.Camera.GetView();

            cachedWorldMatrix = scene.SceneMatrix;
            scene.RenderFrame(this, e);
        }

        internal void EndRender()
        {
            GL.Flush();
        }

        public void UseSprite(int spriteId, int vaoIndex = 0)
        {
            SpriteLibrary.sprites[spriteId].VAO_Index = vaoIndex;
            SpriteLibrary.sprites[spriteId].Use();
        }

        public void DrawSprite(ref RenderUnit renderUnit, float x, float y, float z = 0)
        {
            UseSprite(renderUnit.Id, renderUnit.VAO_Index);
            DrawSprite(x + SpriteLibrary.sprites[renderUnit.Id].OffsetX, y + SpriteLibrary.sprites[renderUnit.Id].OffsetY, z);
        }

        public void DrawSprite(int spriteId, float x, float y, int vaoIndex = 0, float z = 0)
        {
            UseSprite(spriteId, vaoIndex);
            DrawSprite(x, y, z);
        }

        private void DrawSprite(float x, float y, float z = 0)
        {
            shader.Use();

            int transform = GL.GetUniformLocation(shader.Handle, "transform");

            Matrix4 translation = Matrix4.CreateTranslation(new Vector3(x, y, z)) * cachedWorldMatrix;

            GL.UniformMatrix4(transform, true, ref translation);
            
            GL.DrawArrays(PrimitiveType.Quads, 0, VertexArray.VERTEXARRAY_INDEX_COUNT);
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Exceptions.Services
{
    /// <summary>
    /// Thrown when a GameService of a certain type is already registered.
    /// </summary>
    public class ExistingServiceException : Exception
    {
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Exceptions.Attributes
{
    /// <summary>
    /// This exception is thrown whenever an Attribute is added to an object that already posessed an attribute of that type.
    /// This is thrown because Attributes should be determined during object construction. Only the needed attributes should be added.
    /// Additionally, attributes which should be disabled should be disabled via Attribute.Toggle();
    /// </summary>
    public class ExistingAttributeException : Exception
    {

    }
}
<file_sep>﻿using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Systems
{
    public class MathHelper
    {
        public static bool IsBounded_XYZ(Vector3 subjectVector, Vector3 lowerBound, Vector3 upperBound)
        {
            return (
                
                subjectVector.X > lowerBound.X &&
                subjectVector.Y > lowerBound.Y &&
                subjectVector.Z > lowerBound.Z &&

                subjectVector.X < upperBound.X &&
                subjectVector.Y < upperBound.Y &&
                subjectVector.Z < upperBound.Z
                
                );
        }

        public static bool IsBounded_XY(Vector3 subjectVector, Vector3 lowerBound, Vector3 upperBound)
        {
            return (

                subjectVector.X > lowerBound.X &&
                subjectVector.Y > lowerBound.Y &&

                subjectVector.X < upperBound.X &&
                subjectVector.Y < upperBound.Y

                );
        }

        public static int MapEven(int n)
        {
            return 2 * n;
        }

        public static int MapOdd(int n)
        {
            return MapEven(n) + 1;
        }

        public static int MapPositive(int n)
        {
            if (n == 0)
                return 0;
            return (2 * Math.Abs(n)) + ((n + Math.Abs(n)) / (2 * n));
        }

        public static int MapCoordsToUniqueInteger(int x, int y)
        {
            int index = 4 * MapPositive(x) + MapPositive(y);
            int quadSel = index % 4;

            if (quadSel == 0)
                return (4 * index) + 1;
            else if (quadSel == 1)
                return 2 * ((2 * index) + 1);
            else if (quadSel == 2)
                return (4 * index) + 3;
            else
                return 4 * index;
        }

        //This is simply genius. Props to wiki
        public static float MapCoordsToUniqueFloat(int x, int y)
        {
            return 2920 * (float)Math.Sin(x * 21942 + y * 171324 + 8912) * (float)Math.Cos(x * 23157 * y * 217832 + 9758);
        }

        public static int StrideToIndex(int n, int stride)
        {
            return ((stride - 1) * (n + 1)) + n;
        }

        /// <summary>
        /// Used a lot for finding the angle. With 0rad being on  the x axis
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y_or_z"></param>
        /// <returns></returns>
        public static float GetAngle(float x, float y_or_z, float offset=0f)
        {
            float angle = (float)(Math.Atan2(x,y_or_z) / Math.PI * 180f) + offset;
            if (angle < 0)
                angle += 360f;
            return angle;
        }

        public static float GetAngle(Vector2 position, float offset = 0f)
        {
            return GetAngle(position.X, position.Y, offset);
        }

        public static bool FormsTriangle(float side1, float side2, float side3)
        {
            return
                (
                side1 + side2 > side3 &&
                side1 + side3 > side2 &&
                side2 + side3 > side1
                );
        }

        public static float Euler_To_Radian(float thetaEuler)
        {
            return (float)(thetaEuler * Math.PI / 180f);
        }

        public static float Radian_To_Euler(float thetaRadian)
        {
            return (float)(thetaRadian / Math.PI * 180f);
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.Systems;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.WorldSpace.ChunkSpace
{
    public enum ChunkStructureLayerType
    {
        Ground = 0,
        Liquid = 1,
        Flora = 2
    }

    public struct Chunk
    {
        public static readonly int CHUNK_TILE_WIDTH = 16;
        public static readonly int CHUNK_TILE_HEIGHT = 16;
        public static int CHUNK_PIXEL_WIDTH => CHUNK_TILE_WIDTH * 35;
        public static int CHUNK_PIXEL_HEIGHT => CHUNK_TILE_HEIGHT * 18;

        public static int CHUNK_MAX_STRUCTURE_COUNT = 3;

        public static IntegerPosition WorldSpace_To_ChunkSpace(Vector2 position)
        {
            return new Vector2(position.X / Chunk.CHUNK_TILE_WIDTH, position.Y / Chunk.CHUNK_TILE_HEIGHT);
        }
        public static float CartesianToIsometric_X(float x, float y)
        {
            return Tile.TILE_WIDTH * 0.5f * (x + y);
        }
        public static float IsometricToCartesian_X(float iso_x, float cart_y)
        {
            return ((2 * iso_x) / Tile.TILE_WIDTH) - cart_y;
        }

        public static float CartesianToIsometric_Y(float x, float y, float z)
        {
            return (Tile.TILE_HEIGHT-7) * 0.5f * (y - x) + (-z * 6);
        }
        public static float IsometricToCartesian_Y(float iso_y, float cart_x, float z)
        {
            return ((2 * iso_y - (12 * z)) / (Tile.TILE_HEIGHT - 7)) + cart_x;
        }

        public static int Localize(int x_or_y) => (CHUNK_PIXEL_WIDTH + (x_or_y % CHUNK_TILE_WIDTH)) % CHUNK_TILE_WIDTH;
        public static IntegerPosition Localize(IntegerPosition pos) => new IntegerPosition(Localize(pos.X), Localize(pos.Y));

        //private Tile[,] tiles = new Tile[CHUNK_TILE_WIDTH, CHUNK_TILE_HEIGHT];
        private IntegerPosition chunkIndexPosition;
        private bool isFinalized;
        private bool isValid;

        private RenderStructure[] chunkStructures;
        private int structureCount;

        private float minimumZ, maximumZ;

        //public Tile[,] Tiles { get => tiles; set => tiles = value; }
        /// <summary>
        /// Base Location is used for positioning on the chunk level. 
        /// </summary>
        public IntegerPosition ChunkIndexPosition { get => chunkIndexPosition; set => chunkIndexPosition = value; }

        /// <summary>
        /// TileSpace Location is used for positioning on the tile level.
        /// </summary>
        public IntegerPosition TileSpaceLocation => new IntegerPosition(
            ChunkIndexPosition.X * Chunk.CHUNK_TILE_WIDTH,
            ChunkIndexPosition.Y * Chunk.CHUNK_TILE_WIDTH
            );

        /// <summary>
        /// This is the edge of the chunk in terms of Tile Space.
        /// </summary>
        public IntegerPosition TileSpaceEdgeLocation => TileSpaceLocation + new IntegerPosition(16, 16);

        public bool IsFinalized { get => isFinalized; }
        public float MinimumZ { get => minimumZ; set => minimumZ = value; }
        public float MaximumZ { get => maximumZ; set => maximumZ = value; }
        public RenderStructure[] ChunkStructures { get => chunkStructures; private set => chunkStructures = value; }
        public bool IsValid { get => isValid; set => isValid = value; }

        public Chunk(Vector2 baseLocation)
        {
            this.chunkIndexPosition = baseLocation;
            isFinalized = false;
            minimumZ = 0;
            maximumZ = 0;
            isValid = true;
            chunkStructures = new RenderStructure[CHUNK_MAX_STRUCTURE_COUNT];
            structureCount = 0;
        }

        public void AddStructure(RenderStructure structure)
        {
            chunkStructures[structureCount] = structure;
            structureCount++;
        }

        public void AssertZValues()
        {
            for (int i = 0; i < chunkStructures.Length; i++)
            {
                if (chunkStructures[i].minimumZ < minimumZ)
                    minimumZ = chunkStructures[i].minimumZ;
                if (chunkStructures[i].maximumZ > maximumZ)
                    maximumZ = chunkStructures[i].maximumZ;
            }
        }

        public bool WithinPosition(Vector2 basePos)
        {
            return TileSpaceLocation.X <= basePos.X &&
                TileSpaceEdgeLocation.X > basePos.X &&
                TileSpaceLocation.Y <= basePos.Y &&
                TileSpaceEdgeLocation.Y > basePos.Y;
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using isometricgame.GameEngine.Systems.Rendering;

namespace isometricgame.GameEngine.Systems.Rendering
{
    public class TextDisplayer : GameSystem
    {
        public static readonly string CHARS = ",gjpqyABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhiklmnorstuvwxz1234567890.?!/-+@#$%^&*()_=[]\\{}|:;\"'<>`~";

        private SpriteLibrary SpriteLibrary;

        private Dictionary<string, int> fonts = new Dictionary<string, int>();

        public TextDisplayer(Game game) 
            : base(game)
        {

        }

        public override void Load()
        {
            SpriteLibrary = Game.GetSystem<SpriteLibrary>();
        }

        public void LoadFont(string fontName, int fontSpriteId)
        {
            fonts.Add(fontName, fontSpriteId);
        }

        public void DrawText(RenderService renderService, string text, string fontName, float x, float y)
        {
            float fontWidth = SpriteLibrary.sprites[fonts[fontName]].SubWidth;
            float fontHeight = SpriteLibrary.sprites[fonts[fontName]].SubHeight;
            float descentGap = fontHeight / 2 + 1;
            float commaDescent = -3;
            float descentCharacter = -5;

            int font = fonts[fontName];

            float yWrite = y, xWrite = x, yOffset = 0;
            for (int i = 0; i < text.Length; i++)
            {
                if (text[i] == ' ')
                {
                    xWrite += fontWidth;
                    continue;
                }
                if (text[i] == '\n')
                {
                    yWrite-= fontHeight + descentGap;
                    xWrite = x;
                    continue;
                }

                int index = CHARS.IndexOf(text[i]);

                yOffset = (index == 0) ? commaDescent : (index < 6) ? descentCharacter : 0;

                renderService.DrawSprite(font, xWrite, yWrite + yOffset, index);
                xWrite += fontWidth;
            }
        }
    }
}
<file_sep>﻿using OpenTK;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;

namespace isometricgame.GameEngine.Rendering
{
    public struct Texture2D
    {
        private int id;
        private Vector2 size;

        public Texture2D(Bitmap bitmap, bool pixelated = true)
        {
            BitmapData bmpd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            size = new Vector2(bitmap.Width, bitmap.Height);

            id = GL.GenTexture();

            GL.BindTexture(TextureTarget.Texture2D, id);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmpd.Scan0);

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, pixelated ? (int)TextureMinFilter.Nearest : (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, pixelated ? (int)TextureMagFilter.Nearest : (int)TextureMagFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.ClampToEdge, pixelated ? (int)TextureMagFilter.Nearest : (int)TextureMagFilter.Linear);

            bitmap.UnlockBits(bmpd);
        }

        public int ID => id;
        public Vector2 Size => size;
        public int Width => (int)size.X;
        public int Height => (int)size.Y;

        public int Area => Width * Height;
    }
}
<file_sep>﻿using isometricgame.GameEngine;
using isometricgame.GameEngine.WorldSpace;
using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Systems;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using isometricgame.GameEngine.Exceptions.Services;
using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.Events.Arguments;
using isometricgame.GameEngine.Systems.Rendering;
using isometricgame.GameEngine.Systems.Serialization;
using OpenTK.Graphics;
using isometricgame.GameEngine.Systems.Input;

namespace isometricgame.GameEngine
{
    public class Game : GameWindow
    {
        //PATHS
        public readonly string GAME_DIRECTORY_BASE;
        public readonly string GAME_DIRECTORY_WORLDS;
        public readonly string GAME_DIRECTORY_ASSETS;
        public readonly string GAME_DIRECTORY_SHADERS;
        
        #region Systems
        //SERVICES
        private AssetProvider contentPipe;
        private SpriteLibrary spriteLibrary;
        private RenderService renderService;
        private TextDisplayer textDisplayer;
        private InputSystem inputSystem;
        private AnimationSchematicLibrary animationSchematicLibrary;

        private List<GameSystem> systems = new List<GameSystem>();
        /// <summary>
        /// Responsible for loading and unloading textures.
        /// </summary>
        public AssetProvider AssetProvider { get => contentPipe; private set => contentPipe = value; }
        /// <summary>
        /// Responsible for recording and recieving loaded sprites.
        /// </summary>
        public SpriteLibrary SpriteLibrary { get => spriteLibrary; private set => spriteLibrary = value; }

        public RenderService RenderService { get => renderService; private set => renderService = value; }

        public TextDisplayer TextDisplayer { get => textDisplayer; private set => textDisplayer = value; }

        public InputSystem InputSystem { get => inputSystem; private set => inputSystem = value; }

        public AnimationSchematicLibrary AnimationSchematicLibrary { get => animationSchematicLibrary; private set => animationSchematicLibrary = value; }
        #endregion

        #region Time
        private double renderTime, updateTime;

        public double RenderTime => renderTime;
        public double UpdateTime => updateTime;
        #endregion

        private Scene scene;

        public Game(int width, int height, string title, string GAME_DIR = "", string GAME_DIR_ASSETS = "", string GAME_DIR_WORLDS = "")
            : base(width, height, GraphicsMode.Default, title)
        {
            GAME_DIRECTORY_BASE = (GAME_DIR == String.Empty) ? AppDomain.CurrentDomain.BaseDirectory : GAME_DIR;
            GAME_DIRECTORY_ASSETS = (GAME_DIR_ASSETS == String.Empty) ? Path.Combine(GAME_DIRECTORY_BASE, "Assets\\") : GAME_DIR_ASSETS;
            GAME_DIRECTORY_SHADERS = Path.Combine(GAME_DIRECTORY_ASSETS, "Shaders\\");
            GAME_DIRECTORY_WORLDS = (GAME_DIR_WORLDS == String.Empty) ? Path.Combine(GAME_DIRECTORY_BASE, "Worlds\\") : GAME_DIR_WORLDS;
            
            RegisterSystems();

            //END SERVICES

            LoadContent();
        }

        protected override void OnResize(EventArgs e)
        {
            GL.Viewport(ClientRectangle);
            RenderService.AdjustProjection(Width, Height);
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {

        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            updateTime += e.Time;

            scene.UpdateFrame(new FrameArgument(UpdateTime, e.Time));
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            renderTime += e.Time;

            RenderService.BeginRender();

            RenderService.RenderScene(scene, new FrameArgument(RenderTime, e.Time));

            RenderService.EndRender();
            
            SwapBuffers();
        }

        protected override void OnLoad(EventArgs e)
        {

        }

        protected override void OnUnload(EventArgs e)
        {
            foreach (GameSystem gamesys in systems)
                gamesys.Unload();
        }

        public T GetSystem<T>() where T : GameSystem
        {
            foreach (GameSystem system in systems)
                if (system is T)
                    return system as T;
            throw new ServiceNotFoundException();
        }

        protected void RegisterSystem<T>(T gameService) where T : GameSystem
        {
            if (systems.Exists((s) => s is T))
                throw new ExistingServiceException();
            systems.Add(gameService);
        }

        protected virtual void RegisterSystems()
        {
            AssetProvider = new AssetProvider(this);
            SpriteLibrary = new SpriteLibrary(this);
            RenderService = new RenderService(this, Width, Height);
            TextDisplayer = new TextDisplayer(this);
            InputSystem = new InputSystem(this);
            AnimationSchematicLibrary = new AnimationSchematicLibrary(this);

            RegisterSystem(AssetProvider);
            RegisterSystem(SpriteLibrary);
            RegisterSystem(RenderService);
            RegisterSystem(TextDisplayer);
            RegisterSystem(InputSystem);
            RegisterSystem(AnimationSchematicLibrary);

            foreach (GameSystem system in systems)
                system.Load();
        }

        protected virtual void LoadContent()
        {

        }

        public void SetScene(Scene scene)
        {
            this.scene = scene;
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.WorldSpace.Generators;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.WorldSpace.ChunkSpace
{
    /// <summary>
    /// Responsible for generating new chunks, and serializing and deserializing them.
    /// </summary>
    public class ChunkDirectory
    {
        //Chunks[0] is the minimal position. It is often times removed or inserted.
        private List<Chunk> activeChunks = new List<Chunk>();
        private List<Chunk> skirtChunks = new List<Chunk>();
        private Generator chunkGenerator;
        private int renderDistance, newRenderDistance;

        private Chunk[,] _chunks;
        private IntegerPosition center = new IntegerPosition(0, 0);
        private bool firstRender = true;

        #region locationals

        public float MinimalX_ByBaseLocation => Chunks[0,0].ChunkIndexPosition.X;
        public float MinimalY_ByBaseLocation => Chunks[0,0].ChunkIndexPosition.Y;
        public float MaximalX_ByBaseLocation => Chunks[DoubleDist-1, DoubleDist-1].ChunkIndexPosition.X;
        public float MaximalY_ByBaseLocation => Chunks[DoubleDist-1, DoubleDist-1].ChunkIndexPosition.Y;

        public float MinimalX_ByTileLocation => MinimalX_ByBaseLocation * Chunk.CHUNK_TILE_WIDTH;
        public float MinimalY_ByTileLocation => MinimalY_ByBaseLocation * Chunk.CHUNK_TILE_HEIGHT;
        public float MaximalX_ByTileLocation => (MaximalX_ByBaseLocation+1) * Chunk.CHUNK_TILE_WIDTH-1;
        public float MaximalY_ByTileLocation => (MaximalY_ByBaseLocation+1) * Chunk.CHUNK_TILE_HEIGHT-1;

        public float VisibleWidth => (MaximalX_ByTileLocation - MinimalX_ByTileLocation) * Tile.TILE_WIDTH;
        public float VisibleHeight => (MaximalY_ByTileLocation - MinimalY_ByTileLocation) * Tile.TILE_HEIGHT;

        //public float MinimalX_ByGameLocation => MinimalX_ByBaseLocation * Chunk.CHUNK_PIXEL_WIDTH;
        //public float MinimalY_ByGameLocation => MinimalY_ByBaseLocation * Chunk.CHUNK_PIXEL_HEIGHT;
        //public float MaximalX_ByGameLocation => MaximalX_ByBaseLocation * Chunk.CHUNK_PIXEL_WIDTH;
        //public float MaximalY_ByGameLocation => MaximalY_ByBaseLocation * Chunk.CHUNK_PIXEL_HEIGHT;
        #endregion

        private int DoubleDist => renderDistance * 2 + 1;

        public Generator ChunkGenerator { get => chunkGenerator; set => chunkGenerator = value; }

        public int RenderDistance { get => renderDistance; set => newRenderDistance = value; }
        public Chunk[,] Chunks { get => _chunks; private set => _chunks = value; }

        public ChunkDirectory(int renderDistance, Generator chunkGenerator)
        {
            this.chunkGenerator = chunkGenerator;
            this.renderDistance = renderDistance;
            this.newRenderDistance = renderDistance;

            Chunks = new Chunk[DoubleDist, DoubleDist];
        }

        /// <summary>
        /// Sorts the chunks from 0 -> n by the sum of X*renderDist and Y.
        /// </summary>
        /// <returns></returns>
        public List<Chunk> GetSortedChunks()
        {
            return activeChunks.OrderBy((c) => (c.ChunkIndexPosition.X * renderDistance) + c.ChunkIndexPosition.Y).ToList();
        }

        public void SetCenter(Vector2 position)
        {
            center = Chunk.WorldSpace_To_ChunkSpace(position);
        }

        public IntegerPosition DeliminateChunkIndex(IntegerPosition position)
        {
            IntegerPosition posToChunk = Chunk.WorldSpace_To_ChunkSpace(position);
            IntegerPosition chunkPosition = posToChunk - center + new IntegerPosition(renderDistance, renderDistance);

            return chunkPosition;
        }

        /// <summary>
        /// Deliminates a tile using gamespace coordinates.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public IntegerPosition Localize(IntegerPosition position, IntegerPosition cpos)
        {
            /*
            IntegerPosition posToChunk = Chunk.WorldSpace_To_ChunkSpace(position);
            IntegerPosition chunkPosition = posToChunk - center + new IntegerPosition(renderDistance, renderDistance);

            Chunk c = _chunks[chunkPosition.X, chunkPosition.Y];
            */        

            return position - Chunks[cpos.X, cpos.Y].TileSpaceLocation;
        }

        private Vector3 DeliminateRenderUnit_Position(IntegerPosition position, int structureIndex = 0)
        {
            //contemplate using unsafe {}.
            IntegerPosition chunkPosition = DeliminateChunkIndex(position);
            IntegerPosition pos = Localize(position, chunkPosition);
            return Chunks[chunkPosition.X, chunkPosition.Y].ChunkStructures[structureIndex].structuralUnits[pos.X][pos.Y].Position;
        }

        public byte GetTileOrientation(IntegerPosition localPos, IntegerPosition chunkTilePos, ref RenderStructure structure)
        {
            IntegerPosition lookupPos;
            byte orientation = 0;
            byte oriVal = 0;
            Vector3 target = structure.structuralUnits[localPos.X][localPos.Y].Position;

            for (int ly = -1; ly < 2; ly++)
            {
                for (int lx = -1; lx < 2; lx++)
                {
                    if (lx == 0 && ly == 0)
                        continue;
                    oriVal = Tile.ORIENTATIONS[lx + 1, ly + 1];
                    lookupPos = new IntegerPosition(lx, ly) + localPos;
                    Vector3 t;

                    if (lookupPos.X < 0 || lookupPos.Y < 0 || lookupPos.X >= Chunk.CHUNK_TILE_WIDTH || lookupPos.Y >= Chunk.CHUNK_TILE_HEIGHT)
                    {
                        t = DeliminateRenderUnit_Position(lookupPos + chunkTilePos);
                    }
                    else
                    {
                        t = structure.structuralUnits[lookupPos.X][lookupPos.Y].Position;
                    }

                    if (t.Z < target.Z)
                    {
                        orientation = (byte)(orientation | oriVal);
                    }
                }
            }

            return orientation;
        }

        //we will need a means to check live to add/drop chunks
        public void ChunkCleanup(Vector2 centerPosition)
        {
            IntegerPosition newCenter = Chunk.WorldSpace_To_ChunkSpace(centerPosition);

            if (renderDistance != newRenderDistance)
                renderDistance = newRenderDistance;
            else if (newCenter == center && !firstRender)
                return;
            else if (firstRender)
                firstRender = false;

            IntegerPosition offsetFromOriginal = center - newCenter;
            
            Chunk[,] newChunkSet = new Chunk[DoubleDist, DoubleDist];
            
            IntegerPosition render_offset = new IntegerPosition(renderDistance, renderDistance);

            IntegerPosition minPos = newCenter - render_offset;
            IntegerPosition maxPos = newCenter + render_offset;
            Chunk c;

            //move chunks
            for (int x = 0 ; x < DoubleDist; x++)
            {
                for (int y = 0; y < DoubleDist; y++)
                {
                    c = (x < Chunks.GetLength(0) && y < Chunks.GetLength(1)) ? Chunks[x, y] : default(Chunk);

                    if (c.IsValid)
                    {
                        if (
                            c.ChunkIndexPosition.X >= minPos.X &&
                            c.ChunkIndexPosition.Y >= minPos.Y &&
                            c.ChunkIndexPosition.X < maxPos.X &&
                            c.ChunkIndexPosition.Y < maxPos.Y
                            )
                        {
                            IntegerPosition index = c.ChunkIndexPosition + render_offset - newCenter;
                            newChunkSet[index.X, index.Y] = c;
                        }
                        else
                        {

                        }
                    }

                    if (newChunkSet[x, y].IsValid)
                        continue;

                    IntegerPosition newChunkPos = new IntegerPosition(x, y) - render_offset + newCenter;
                    Chunk new_c = ChunkGenerator.CreateChunk(newChunkPos);
                    newChunkSet[x, y] = new_c;
                }
            }

            Chunks = newChunkSet;
            center = newCenter;

            //verify z values and orientations
            for (int x = 1; x < DoubleDist-1; x++) //do not verify skirt chunks, only: [1, n-1]
            {
                for (int y = 1; y < DoubleDist-1; y++)
                {
                    if (!Chunks[x, y].IsFinalized)
                    {
                        ChunkGenerator.FinalizeChunk(this, ref Chunks[x, y]);
                    }
                }
            }
        }

        public void PerformTileOrientation(IntegerPosition chunkTileSpace, ref RenderStructure structure)
        {
            IntegerPosition localPos = new IntegerPosition(0,0);

            for(localPos.Y = 0; localPos.Y < Chunk.CHUNK_TILE_HEIGHT; localPos.Y++)
            {
                for (localPos.X = 0; localPos.X < Chunk.CHUNK_TILE_WIDTH; localPos.X++)
                {
                    byte orientation = GetTileOrientation(localPos, chunkTileSpace, ref structure);

                    if (orientation >= 15)
                    {
                        orientation = 0;
                        structure.structuralUnits[localPos.X][localPos.Y].Position -= new Vector3(0,0,1);
                    }
                    structure.structuralUnits[localPos.X][localPos.Y].VAO_Index = orientation;
                }
            }
        }
    }
}
<file_sep>﻿using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.WorldSpace.ChunkSpace
{
    public struct IntegerPosition
    {
        int x, y;

        public int X { get => x; set => x = value; }
        public int Y { get => y; set => y = value; }

        public IntegerPosition(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public bool Equals(IntegerPosition position) => position.x == x && position.y == y;

        public override string ToString()
        {
            return String.Format("{0}, {1}", x, y);
        }

        public static bool operator ==(IntegerPosition pos1, IntegerPosition pos2)
        {
            return pos1.Equals(pos2);
        }

        public static bool operator !=(IntegerPosition pos1, IntegerPosition pos2)
        {
            return !pos1.Equals(pos2);
        }

        public static IntegerPosition operator -(IntegerPosition pos1, IntegerPosition pos2)
        {
            return new IntegerPosition(pos1.x - pos2.x, pos1.y - pos2.y);
        }

        public static IntegerPosition operator +(IntegerPosition pos1, IntegerPosition pos2)
        {
            return new IntegerPosition(pos1.x + pos2.x, pos1.y + pos2.y);
        }

        public static IntegerPosition operator *(IntegerPosition pos1, int scalar)
        {
            return new IntegerPosition(pos1.x * scalar, pos1.y * scalar);
        }

        public static implicit operator IntegerPosition(Vector2 pos) => new IntegerPosition((int)Math.Floor(pos.X), (int)Math.Floor(pos.Y));
        public static implicit operator Vector2(IntegerPosition pos) => new Vector2(pos.X, pos.Y);
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Scenes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Components.Rendering
{
    public class SpriteComponent : GameComponent
    {
        public SpriteComponent() 
            : base()
        {
        }

        /// <summary>
        /// might need to make this thread safe.
        /// </summary>
        /// <param name="s"></param>
        public virtual void SetSprite(RenderUnit ru)
        {
            if (ParentObject != null)
                ParentObject.renderUnit = ru;
        }

        public virtual void SetSprite(int spriteId, int vao_Index = 0)
        {
            if (ParentObject != null)
            {
                ParentObject.renderUnit.Id = spriteId;
                ParentObject.renderUnit.VAO_Index = vao_Index;
                ParentObject.renderUnit.IsInitialized = true;
            }
        }
    }
}
<file_sep>﻿using OpenTK;
using OpenTK.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Systems.Input
{
    public class InputSystem : GameSystem
    {
        private Dictionary<InputType, List<InputHandler>> inputDirectory = new Dictionary<InputType, List<InputHandler>>()
        {
            { InputType.Keyboard_Press, new List<InputHandler>() },
            { InputType.Keyboard_UpDown, new List<InputHandler>() },
            { InputType.Mouse_Button, new List<InputHandler>() },
            { InputType.Mouse_Move, new List<InputHandler>() },
            { InputType.Mouse_Wheel, new List<InputHandler>() }
        };

        private Dictionary<int, List<InputHandler>> handlerGroups = new Dictionary<int, List<InputHandler>>();
        
        public InputSystem(Game game) 
            : base(game)
        {
            game.KeyDown += GameWindow_KeyDown;
            game.KeyPress += GameWindow_KeyPress;
            game.KeyUp += GameWindow_KeyUp;

            game.MouseDown += GameWindow_MouseDown;
            game.MouseUp += GameWindow_MouseUp;
            game.MouseWheel += GameWindow_MouseWheel;
            game.MouseMove += GameWindow_MouseMove;
        }

        public InputHandler RegisterHandler(InputType inputType, bool enabled = true, int handlerID = -1)
        {
            int newID =  (handlerID > 0) ? handlerID : handlerGroups.Keys.Count;
            InputHandler handler = new InputHandler(newID, inputType, enabled);

            if (handlerGroups.ContainsKey(newID))
                handlerGroups[newID].Add(handler);
            else
            {
                handlerGroups.Add(newID, new List<InputHandler> { handler });
            }
            
            for(int key = 1; key <= 16; key*=2)
                if (0 < ((int)inputType & key))
                    inputDirectory[(InputType)key].Add(handler);

            return handler;
        }

        private void GameWindow_MouseMove(object sender, MouseMoveEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Mouse_Move])
                if (handle.Enabled)
                    handle.Handle_Mouse_Move(sender, e);
        }

        private void GameWindow_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Mouse_Wheel])
                if (handle.Enabled)
                    handle.Handle_Mouse_Wheel(sender, e);
        }

        private void GameWindow_MouseUp(object sender, MouseButtonEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Mouse_Button])
                if (handle.Enabled)
                    handle.Handle_Mouse_Button(sender, e);
        }

        private void GameWindow_MouseDown(object sender, MouseButtonEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Mouse_Button])
                if (handle.Enabled)
                    handle.Handle_Mouse_Button(sender, e);
        }

        private void GameWindow_KeyUp(object sender, KeyboardKeyEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Keyboard_UpDown])
                if (handle.Enabled)
                    handle.Handle_Keyboard_UpDown(sender, e);
        }

        private void GameWindow_KeyPress(object sender, KeyPressEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Keyboard_Press])
                if (handle.Enabled)
                    handle.Handle_Keyboard_Press(sender, e);
        }

        private void GameWindow_KeyDown(object sender, KeyboardKeyEventArgs e)
        {
            foreach (InputHandler handle in inputDirectory[InputType.Keyboard_UpDown])
                if (handle.Enabled)
                    handle.Handle_Keyboard_UpDown(sender, e);
        }
    }
}
<file_sep>﻿using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.WorldSpace.ChunkSpace
{
    public struct Tile
    {
        public static readonly int TILE_WIDTH = 36;
        public static readonly int TILE_HEIGHT = 25;
        public static readonly int TILE_AREA = TILE_WIDTH * TILE_HEIGHT;
        public static readonly int TILE_Y_INC = TILE_HEIGHT / 2 - 3;

        public static byte[,] ORIENTATIONS = new byte[,]
        {
            /*
            { 8, 9, 1 },
            { 12, 0, 3 },
            { 4, 6, 2 }
            */
            { 2, 6, 4 },
            { 3, 0, 12 },
            { 1, 9, 8 }
        };

        private int _data;
        private int _z;
        private byte _orientation;

        public Tile(int _z, int _data = 0, byte _orientation=0)
        {
            this._z = _z;
            this._data = _data;
            this._orientation = _orientation;
        }

        public int Z { get => _z; set => _z = value; }
        public byte Orientation { get => _orientation; set => _orientation = value; }
        public int Data { get => _data; set => _data = value; }
    }
}
<file_sep>﻿using isometricgame.GameEngine.WorldSpace.ChunkSpace;
using isometricgame.GameEngine.WorldSpace.Generators.PerlinNoise;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;

namespace isometricgame.GameEngine.WorldSpace.Generators
{
    public abstract class Generator
    {
        private int seed;
        private Perlin perlin;

        public int Seed { get => seed; private set => seed = value; }
        protected Perlin Perlin => perlin;

        public Generator(int seed)
        {
            this.seed = seed;
            perlin = new Perlin(seed);
        }

        public abstract Chunk CreateChunk(Vector2 pos);
        public abstract void FinalizeChunk(ChunkDirectory chunkDirectory, ref Chunk c);
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;

namespace isometricgame.GameEngine.Rendering
{
    /// <summary>
    /// Use this for sprites that have multiple versions.
    /// </summary>
    public class Sprite
    {
        private string name;
        private Texture2D texture;

        private int[] vertexArrayObjects;

        private int subWidth;
        private int subHeight;

        private int vaoIndex = 0, vaoRow = 0;

        private float offsetX, offsetY;

        private int columnCount, rowCount;

        private int count;

        private VertexArray[] vertexArrays;
        
        public int[] VertexArrayObjects { get => vertexArrayObjects; set => vertexArrayObjects = value; }

        public string Name => name;

        public int SubWidth { get => subWidth; private set => subWidth = value; }
        public int SubHeight { get => subHeight; private set => subHeight = value; }
        public Texture2D Texture { get => texture; private set => texture = value; }

        public int VAO_Index { get => vaoIndex + (VAO_Row * columnCount); set => vaoIndex = value; }
        public int VAO_Row { get => vaoRow; set => vaoRow = value; }
        public float OffsetX { get => offsetX; protected set => offsetX = value; }
        public float OffsetY { get => offsetY; protected set => offsetY = value; }

        public Sprite(Sprite s, int vboIndex = -1)
        {
            offsetX = s.offsetX;
            offsetY = s.offsetY;
            name = s.name;

            texture = s.texture;

            subWidth = s.subWidth;
            subHeight = s.subHeight;

            this.vaoIndex = (vboIndex < 0) ? s.vaoIndex : vboIndex;

            columnCount = s.columnCount;
            rowCount = s.rowCount;

            count = s.count;

            vertexArrays = new VertexArray[s.vertexArrays.Length];

            vertexArrayObjects = new int[s.vertexArrayObjects.Length];

            for (int i = 0; i < s.vertexArrayObjects.Length; i++)
                vertexArrayObjects[i] = s.vertexArrayObjects[i];

            for (int i = 0; i < count; i++)
                vertexArrays[i] = s.vertexArrays[i];
        }

        public Sprite(
            Texture2D texture, 
            int subWidth, 
            int subHeight, 
            string name="", 
            float offsetX = 0, 
            float offsetY = 0, 
            int vboIndex = 0,
            float r = 0,
            float g = 0,
            float b = 0,
            float a = 0)
        {
            this.offsetX = offsetX;
            this.offsetY = offsetY;
            this.name = name;

            this.texture = texture;

            this.subWidth = subWidth;
            this.subHeight = subHeight;

            this.vaoIndex = vboIndex;

            columnCount = texture.Width / subWidth;
            rowCount = texture.Height / subHeight;

            count = columnCount * rowCount;

            vertexArrays = new VertexArray[count];

            vertexArrayObjects = new int[count];

            for (int i = 0; i < vertexArrayObjects.Length; i++)
                vertexArrayObjects[i] = GL.GenVertexArray();

            Vertex[] vertices;

            for(int y = 0; y < rowCount; y++)
            {
                for (int x = 0; x < columnCount; x++)
                {
                    vertices = VertexArray.VerticesFromDimensions(texture.Width, texture.Height, subWidth, subHeight, x, y, r, g, b, a);

                    vertexArrays[x + (y * columnCount)] = new VertexArray(texture, vertices);
                }
            }

            BindVertexArray();
        }

        public void Use()
        {
            vertexArrays[VAO_Index].Use();
            GL.BindVertexArray(vertexArrayObjects[VAO_Index]);
        }

        private void BindVertexArray()
        {
            for (int i = 0; i < vertexArrays.Length; i++)
            {
                GL.BindVertexArray(vertexArrayObjects[i]);
                vertexArrays[i].BindVertexBuffer();

                GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
                GL.EnableVertexAttribArray(0);
                GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 2 * sizeof(float));
                GL.EnableVertexAttribArray(1);
                GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, 8 * sizeof(float), 4 * sizeof(float));
                GL.EnableVertexAttribArray(2);
            }

            GL.BindVertexArray(0);
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine;
using isometricgame.GameEngine.WorldSpace;
using isometricgame.GameEngine.Systems;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using isometricgame.GameEngine.Components.Rendering;
using isometricgame.GameEngine.Scenes;
using isometricgame.GameEngine.WorldSpace.Generators;
using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Events.Arguments;
using isometricgame.GameEngine.Systems.Rendering;
using isometricgame.GameEngine.WorldSpace.ChunkSpace;

namespace isometricgame.GameEngine.WorldSpace
{
    public class WorldScene : Scene
    {
        private ChunkDirectory chunkDirectory;
        private Camera camera;

        public int renderTileRange, renderDistance, tileRange;

        private SpriteLibrary spriteLibrary;

        public Camera Camera { get => camera; private set => camera = value; }
        public ChunkDirectory ChunkDirectory { get => chunkDirectory; set => chunkDirectory = value; }

        public bool test_flop_REMOVE = true;

        public WorldScene(Game game, Generator worldGenerator, int renderDistance=0)
            : base(game)
        {
            this.ChunkDirectory = new ChunkDirectory(renderDistance, worldGenerator);
            this.Camera = new Camera(this);
            this.renderDistance = renderDistance;

            spriteLibrary = game.GetSystem<SpriteLibrary>();
        }

        public override void UpdateFrame(FrameArgument e)
        {
            Camera.Pan_Linear((float)e.DeltaTime);
            
            SceneMatrix = Camera.GetView();
            ChunkDirectory.ChunkCleanup(Camera.Position.Xy);
            
            renderTileRange = (int)((2 / Math.Log(camera.Zoom + 1)) * 16);
            if (test_flop_REMOVE)
                tileRange = (int)((2 / Math.Log(camera.Zoom * 1.5f + 1)) * 16);
            else
                tileRange = (int)((2 / Math.Log(camera.Zoom + 1)) * 16);
            renderDistance = (renderTileRange / Chunk.CHUNK_TILE_WIDTH) + 2;
            chunkDirectory.RenderDistance = renderDistance;

            base.UpdateFrame(e);
        }
        
        public override void RenderFrame(RenderService renderService, FrameArgument e)
        {
            if (chunkDirectory.RenderDistance != renderDistance)
                return; //prevent race condition
            
            int flooredX = (int)camera.TargetPosition.X;
            int flooredY = (int)camera.TargetPosition.Y;

            IntegerPosition cpos;
            IntegerPosition spos;

            RenderUnit[] renderUnits;

            float tx, ty;
            int spriteId;
            
            IntegerPosition rowOffset = new IntegerPosition(1, 0);
            IntegerPosition colOffset = new IntegerPosition(-1, -1);
            int flip = 1, flipadd = 0;
            int test_n = tileRange;
            
            int range = (int)(2.2f * test_n * test_n) + ((test_n - 1) * (test_n - 1));
            int test_n_calc = test_n;

            IntegerPosition basePos = new IntegerPosition(flooredX - test_n / 3, flooredY + (4 * test_n / 3)), yPrimeDescent = basePos;
            int flop = test_n_calc;

            for (int range_prime = 0; range_prime < range; range_prime++)
            {
                yPrimeDescent += colOffset;

                cpos = ChunkDirectory.DeliminateChunkIndex(yPrimeDescent);
                spos = ChunkDirectory.Localize(yPrimeDescent, cpos);


                for (int structureIndex = 0; structureIndex < Chunk.CHUNK_MAX_STRUCTURE_COUNT; structureIndex++)
                {
                    if (ChunkDirectory.Chunks[cpos.X, cpos.Y].ChunkStructures[structureIndex].IsValid)
                    {
                        renderUnits = ChunkDirectory.Chunks[cpos.X, cpos.Y].ChunkStructures[structureIndex].StructuralUnits[spos.X];
                        if (renderUnits[spos.Y].IsInitialized)
                        {
                            tx = Chunk.CartesianToIsometric_X(yPrimeDescent.X, yPrimeDescent.Y);
                            ty = Chunk.CartesianToIsometric_Y(yPrimeDescent.X, yPrimeDescent.Y, renderUnits[spos.Y].Position.Z);

                            spriteId = renderUnits[spos.Y].Id;
                            renderService.DrawSprite(renderUnits[spos.Y].Id, tx, ty, renderUnits[spos.Y].VAO_Index);
                        }
                    }
                }
                flop--;

                if (flop <= 0)
                {
                    rowOffset += colOffset * flip;
                    basePos += rowOffset;
                    yPrimeDescent = basePos;
                    flip = flip * -1;
                    flipadd += flip;
                    flop = test_n_calc + flipadd;
                }
            }

            base.RenderFrame(renderService, e);
        }

        protected override void DrawSprite(RenderService renderService, ref RenderUnit renderUnit)
        {
            float cx = Chunk.CartesianToIsometric_X(renderUnit.X, renderUnit.Y);
            float cy = Chunk.CartesianToIsometric_Y(renderUnit.X, renderUnit.Y, renderUnit.Z);

            renderService.DrawSprite(ref renderUnit, cx, cy);
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering.Animation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Systems.Rendering
{
    public class AnimationSchematicLibrary : GameSystem
    {
        private Dictionary<string, AnimationSchematic> animationSchematics = new Dictionary<string, AnimationSchematic>();

        public AnimationSchematicLibrary(Game gameRef) 
            : base(gameRef)
        {
        }

        /// <summary>
        /// Not finished yet.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        public void LoadSchematic(string path, string name = "")
        {
            if (!File.Exists(path))
                throw new FileNotFoundException();

            if (name == String.Empty)
                name = Path.GetFileNameWithoutExtension(path);

            int[][] nodes;
            int nodeCount = -1;
            float animSpeed = 0.1f;

            using (StreamReader reader = File.OpenText(path))
            {
                string line;
                while (!reader.EndOfStream)
                {

                    
                }
            }
        }

        /// <summary>
        /// Remove later.
        /// </summary>
        public void AddSchematic(AnimationSchematic schem, string name = "")
        {
            if (name == "")
                name = "anim_" + animationSchematics.Count;

            animationSchematics.Add(name, schem);
        }

        public AnimationSchematic GetSchematic(string name) => animationSchematics[name];
        public AnimationSchematic GetSchematic(int id) => animationSchematics.Values.ElementAt(id);
    }
}
<file_sep>﻿using isometricgame.GameEngine.Events.Arguments;
using isometricgame.GameEngine.Scenes;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine
{
    /// <summary>
    /// Attributes are added to GameObjects to give additional functionalities. Such as hitboxes, physics, and more.
    /// </summary>
    public class GameComponent
    {
        private GameObject parentObject;
        private bool enabled = true;

        public GameObject ParentObject { get => parentObject; internal set => parentObject = value; }

        public GameComponent()
        {

        }

        internal void _initalize()
        {
            Initalize();
        }

        protected void Initalize()
        {

        }

        /// <summary>
        /// Logical update frame.
        /// </summary>
        internal void Update(FrameArgument args)
        {
            if (enabled)
            {
                OnUpdate(args);
            }
        }

        protected virtual void OnUpdate(FrameArgument args)
        {

        }

        public void Toggle()
        {
            enabled = !enabled;
        }

        public void Toggle(bool b)
        {
            enabled = b;
        }

        public virtual GameComponent Clone()
        {
            GameComponent newComp = new GameComponent();
            newComp.enabled = enabled;

            return newComp;
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Rendering.Animation
{
    public class AnimationSchematic
    {
        private int[][] animationNodes;

        private double speed;
        private int frame = 0;
        private int frameOffset;
        private double pauseTime;

        private int lastNode = 0;
        public int LastNode => lastNode;

        public AnimationSchematic(int nodeCount, double speed = 1, int startFrame = 0)
        {
            animationNodes = new int[nodeCount][];
            this.speed = speed;
            frameOffset = startFrame;
        }

        public void DefineNode(int node, int[] spriteIndices)
        {
            animationNodes[node] = spriteIndices;
        }

        public int GetVBO_Index(double time, int node)
        {
            if (pauseTime == 0)
                frame = GetFrame(node, time, frameOffset);

            return lastNode = animationNodes[node][frame];
        }

        public void Pause(double pauseTime)
        {
            this.pauseTime = pauseTime;
        }

        public void Unpause(double unpauseTime, int node)
        {
            //Find the time skip
            int unpauseFrame = GetFrame(node, unpauseTime, 0) + animationNodes[node].Length - frame;
            //Offset the time skip so we continue on the same frame.
            frameOffset = unpauseFrame % animationNodes[node].Length;
            pauseTime = 0;
        }

        private int GetFrame(int node, double time, int givenOffset)
        {
            return (int)((time / speed) + givenOffset) % animationNodes[node].Length;
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.WorldSpace.Generators.PerlinNoise2
{
    class PerlinRegion
    {
    }
}
<file_sep>﻿using isometricgame.GameEngine.Components.Rendering;
using isometricgame.GameEngine.Events.Arguments;
using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Systems;
using isometricgame.GameEngine.Systems.Rendering;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Scenes
{
    public class Scene
    {
        private Game game;
        private List<RenderStructure> staticSceneStructures = new List<RenderStructure>();
        private List<RenderStructure> dynamicSceneStructures = new List<RenderStructure>();
        private List<GameObject> staticSceneObjects = new List<GameObject>();
        private List<GameObject> dynamicSceneObjects = new List<GameObject>();
        private Matrix4 sceneMatrix;
        
        public Game Game => game;

        public Matrix4 SceneMatrix { get => sceneMatrix; protected set => sceneMatrix = value; }
        public List<RenderStructure> StaticSceneStructures { get => staticSceneStructures; protected set => staticSceneStructures = value; }
        public List<RenderStructure> DynamicSceneStructures { get => dynamicSceneStructures; protected set => dynamicSceneStructures = value; }
        public List<GameObject> StaticSceneObjects { get => staticSceneObjects; protected set => staticSceneObjects = value; }
        public List<GameObject> DynamicSceneObjects { get => dynamicSceneObjects; protected set => dynamicSceneObjects = value; }

        public Scene(Game game)
        {
            this.game = game;
            
            sceneMatrix = Matrix4.CreateOrthographic(1200,900,0.01f,30000f) * Matrix4.CreateTranslation(0,0,1);
        }

        public virtual void RenderFrame(RenderService renderService, FrameArgument e)
        {
            RenderSceneObjects(renderService, e, StaticSceneObjects.ToArray());
            RenderSceneObjects(renderService, e, DynamicSceneObjects.ToArray());
        }

        public virtual void UpdateFrame(FrameArgument e)
        {
            UpdateObjects(e, StaticSceneObjects.ToArray());
            UpdateObjects(e, DynamicSceneObjects.ToArray());
        }

        protected virtual void RenderSceneObjects(RenderService renderService, FrameArgument e, GameObject[] sceneObjects)
        {
            foreach (GameObject so in sceneObjects)
            {
                if (so.renderUnit.IsInitialized)
                    DrawSprite(renderService, ref so.renderUnit);
            }
        }

        protected virtual void UpdateObjects(FrameArgument e, GameObject[] gameObjects)
        {
            foreach (GameObject obj in gameObjects)
                obj.OnUpdate(e);
        }

        protected virtual void DrawSprite(RenderService renderService, ref RenderUnit renderUnit)
        {
            renderService.DrawSprite(ref renderUnit, renderUnit.Position.X, renderUnit.Position.Y, renderUnit.Position.Z);
        }
    }
}
<file_sep>﻿using OpenTK;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Rendering
{
    public struct Vertex
    {
        private Vector2 position;
        private Vector2 textcoord;
        private Vector4 color;

        public Vector2 Position => position;
        public Vector2 TextCoord => textcoord;

        public Color Color
        {
            get => Color.FromArgb((int)(color.W * 255), (int)(color.X * 255), (int)(color.Y * 255), (int)(color.Z * 255));
            set
            {
                this.color = new Vector4(value.R / 255f, value.G / 255f, value.B /255f, value.A / 255f);
            }
        }

        public static int SizeInBytes => Vector2.SizeInBytes * 2 + Vector4.SizeInBytes;

        public Vertex(Vector2 position, Vector2 textcoord, float r=0, float g=0, float b=0, float a=1)
        {
            this.position = position;
            this.textcoord = textcoord;
            this.color = new Vector4(r,g,b,a);
        }
    }
}
<file_sep>﻿using OpenTK;
using OpenTK.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Systems.Input
{
    public class InputHandler
    {
        private bool enabled;
        private int handlerID;
        private InputType inputType;

        private Dictionary<Key, InputSwitchType> keyboard_UpDownSwitch = new Dictionary<Key, InputSwitchType>();
        private Dictionary<MouseButton, InputSwitchType> mouse_UpDownToggle = new Dictionary<MouseButton, InputSwitchType>();

        public bool Enabled { get => enabled; set => enabled = value; }
        public int HandlerID { get => handlerID; private set => handlerID = value; }
        public InputType InputType { get => inputType; set => inputType = value; }

        private KeyboardKeyEventArgs keyboard_UpDown;
        private KeyPressEventArgs keyboard_Press;
        private MouseButtonEventArgs mouse_Button;
        private MouseMoveEventArgs mouse_Move;
        private MouseWheelEventArgs mouse_Wheel;

        public KeyboardKeyEventArgs Keyboard_UpDown { get => keyboard_UpDown; private set => keyboard_UpDown = value; }
        public KeyPressEventArgs Keyboard_Press { get => keyboard_Press; private set => keyboard_Press = value; }
        public MouseButtonEventArgs Mouse_Button { get => mouse_Button; private set => mouse_Button = value; }
        public MouseMoveEventArgs Mouse_Move { get => mouse_Move; private set => mouse_Move = value; }
        public MouseWheelEventArgs Mouse_Wheel { get => mouse_Wheel; private set => mouse_Wheel = value; }

        internal InputHandler(int handlerID, InputType inputType, bool enabled = true)
        {
            this.enabled = enabled;
            this.handlerID = handlerID;
            this.inputType = inputType;
        }

        public void DeclareKeySwitch(Key key)
        {
            keyboard_UpDownSwitch.Add(key, InputSwitchType.RepeatUp);
        }

        public InputSwitchType Keyboard_SwitchState(Key key) => keyboard_UpDownSwitch[key];

        public bool Keyboard_SwitchState_Bool(Key key) => (keyboard_UpDownSwitch[key] == InputSwitchType.InitalDown || keyboard_UpDownSwitch[key] == InputSwitchType.InitalUp) ? true : false;
        
        public bool Keyboard_SwitchState_BoolReset(Key key)
        {
            bool ret = Keyboard_SwitchState_Bool(key);
            if (ret)
                keyboard_UpDownSwitch[key] = InputSwitchType.RepeatDown;
            return ret;
        }

        public bool Keyboard_SwitchState_BoolResetFree(Key key)
        {
            bool ret = Keyboard_SwitchState_Bool(key);
            if (ret)
                keyboard_UpDownSwitch[key] = InputSwitchType.RepeatUp;
            return ret;
        }
        
        public void RemoveKeySwitch(Key key)
        {
            keyboard_UpDownSwitch.Remove(key);
        }

        public void DeclareMouseToggle(MouseButton button)
        {
            mouse_UpDownToggle.Add(button, InputSwitchType.RepeatUp);
        }

        public void RemoveMouseToggle(MouseButton button)
        {
            mouse_UpDownToggle.Remove(button);
        }

        internal virtual void Handle_Keyboard_UpDown(object sender, KeyboardKeyEventArgs e)
        {
            if (keyboard_UpDownSwitch.ContainsKey(e.Key))
            {
                InputSwitchType state = handleSwitch(e.Keyboard.IsKeyDown(e.Key), keyboard_UpDownSwitch[e.Key]);
                if (state != InputSwitchType.NoRead)
                    keyboard_UpDownSwitch[e.Key] = state;
            }
            keyboard_UpDown = (enabled) ? e : null;
        }
        internal virtual void Handle_Keyboard_Press(object sender, KeyPressEventArgs e) => keyboard_Press = (enabled) ? e : null;

        internal virtual void Handle_Mouse_Button(object sender, MouseButtonEventArgs e)
        {
            if (mouse_UpDownToggle.ContainsKey(e.Button))
            {

            }
            mouse_Button = (enabled) ? e : null;
        }
        internal virtual void Handle_Mouse_Move(object sender, MouseMoveEventArgs e) => mouse_Move = (enabled) ? e : null;
        internal virtual void Handle_Mouse_Wheel(object sender, MouseWheelEventArgs e) => mouse_Wheel = (enabled) ? e : null;

        private InputSwitchType handleSwitch(bool isDown, InputSwitchType state)
        {
            if (isDown)
            {
                if (state == InputSwitchType.InitalUp)
                    return InputSwitchType.RepeatDown;
                else if (state == InputSwitchType.RepeatUp)
                    return InputSwitchType.InitalDown;
            }
            else
            {
                if (state == InputSwitchType.InitalDown)
                    return InputSwitchType.InitalUp;
                else if (state == InputSwitchType.RepeatDown)
                    return InputSwitchType.RepeatUp;
            }
            return InputSwitchType.NoRead;
        }
    }

    public enum InputType
    {
        Keyboard_UpDown = 1,
        Keyboard_Press = 2,
        Mouse_Button = 4,
        Mouse_Move = 8,
        Mouse_Wheel = 16
    };

    public enum InputSwitchType
    {
        NoRead = 0,
        InitalDown = 1,
        InitalUp = 2,
        RepeatDown = 3,
        RepeatUp = 4
    }
}
<file_sep>﻿using isometricgame.GameEngine.WorldSpace.ChunkSpace;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.WorldSpace.Generators.PerlinNoise
{
    public class Perlin
    {
        private int seed;
        private int frequency = 2;

        private int[,] seedMap = new int[16, 16];

        private static readonly Vector2[] OFFSETS = new Vector2[]
        {
            new Vector2(1,0),
            new Vector2(0,1),
            new Vector2(1,1)
        };

        public Perlin(int seed)
        {
            this.seed = seed;

            Random rand = new Random(seed);

            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    seedMap[i,j] = rand.Next();
                }
            }
        }

        
        public float[,] InterprolateNoise(Vector2 chunkPosition)
        {
            float[,] ret = new float[Chunk.CHUNK_TILE_WIDTH, Chunk.CHUNK_TILE_WIDTH];
            int stride = Chunk.CHUNK_TILE_WIDTH;

            Vector2 chunkTilePos = chunkPosition * Chunk.CHUNK_TILE_WIDTH;

            Vector2 strideOffset;

            //interprolation values for stride regions.
            float z0, z1, z2, z3;
            //Currently we end up recalucating these A LOT... will work to fix that later on.


            for (int period = 0; period < frequency; period++)
            {


                for (int x_stride = 0; x_stride * stride < Chunk.CHUNK_TILE_WIDTH; x_stride++)
                {
                    for (int y_stride = 0; y_stride * stride < Chunk.CHUNK_TILE_WIDTH; y_stride++)
                    {

                        strideOffset = chunkTilePos + new Vector2(x_stride * stride, y_stride * stride);

                        // get region z corners.
                        z0 = GetZ((int)strideOffset.X, (int)strideOffset.Y);
                        z1 = GetZ((int)strideOffset.X + stride, (int)strideOffset.Y);
                        z2 = GetZ((int)strideOffset.X, (int)strideOffset.Y + stride);
                        z3 = GetZ((int)strideOffset.X + stride, (int)strideOffset.Y + stride);

                        //Console.WriteLine("[{4}]: {0}, {1}, {2}, {3} --- strideXY {5}/{6}", z0, z1, z2, z3, chunkPosition, x_stride, y_stride);

                        for (int x = stride * x_stride; x < (x_stride + 1) * stride; x++)
                        {
                            for (int y = y_stride * stride; y < (y_stride + 1) * stride; y++)
                            {
                                ret[x,y] += 5 * (GetWeight(x % stride, y % stride, stride, z0, z1, z2, z3));
                                //Console.Write("[{0}] ", ret[x,y]);
                            }
                            //Console.WriteLine();
                        }
                    }
                }

                stride /= 2;
            }

            return ret;
        }

        private float GetWeight(float x, float y, int stride, float z0, float z1, float z2, float z3)
        {
            /*
            z0 *= 0.5f;
            z1 *= 0.5f;
            z2 *= 0.5f;
            z3 *= 0.5f;
            */

            float xw = (x)  / (stride - 1);
            float yw = (y) / (stride - 1);

            return ((1f - yw) * (z0 + (xw * (z1 - z0)))) + (yw * (z2 + (xw * (z3 - z2))));
            
        }

        private float GetZ(int x, int y)
        {
            int zSeed = (int)Systems.MathHelper.MapCoordsToUniqueFloat(x,y);
            Random rand = new Random(zSeed + seed);
            return rand.Next(100) / 100f;
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Events.Arguments;
using isometricgame.GameEngine.Rendering;
using isometricgame.GameEngine.Rendering.Animation;
using isometricgame.GameEngine.Scenes;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Components.Rendering
{
    public class AnimationComponent : SpriteComponent
    {
        private AnimationSchematic schematic;
        private int node;

        public AnimationComponent(AnimationSchematic schematic = null) 
            : base()
        {
            this.schematic = schematic;
        }

        public void SetSchematic(AnimationSchematic schematic) => this.schematic = schematic;

        public void SetNode(int node)
        {
            this.node = node;
        }

        public void DefineNode(int node, int[] subNodes)
        {
            schematic.DefineNode(node, subNodes);
        }

        public void Pause(double time) => schematic.Pause(time);
        public void Unpause(double time) => schematic.Unpause(time, node);

        protected override void OnUpdate(FrameArgument args)
        {
            ParentObject.renderUnit.VAO_Index = schematic.GetVBO_Index(args.Time, node);
        }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering;
using OpenTK;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Systems.Rendering
{
    public class SpriteLibrary : GameSystem
    {
        internal List<Sprite> sprites = new List<Sprite>();
        private Dictionary<string, int> nameToIndex = new Dictionary<string, int>();

        public SpriteLibrary(Game gameRef) 
            : base(gameRef)
        {
        }

        public override void Unload()
        {
            base.Unload();
        }

        public int RecordSprite(Sprite s)
        {
            nameToIndex.Add(s.Name, sprites.Count);
            sprites.Add(s);
            
            return sprites.Count - 1;
        }

        public int GetSprite(string name) => nameToIndex[name];
        public void SetVAO(int id, int vao) => sprites[id].VAO_Index = vao;
        public void SetVAO_Row(int id, int row) => sprites[id].VAO_Row = row;

        public RenderUnit ExtractRenderUnit(string name) => ExtractRenderUnit(nameToIndex[name]);
        public RenderUnit ExtractRenderUnit(int id) => new RenderUnit(id, 0, Vector3.Zero);
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace isometricgame.GameEngine.Rendering
{
    public class VertexArray : IDisposable
    {
        public static readonly int VERTEXARRAY_INDEX_COUNT = 4;

        private Vertex[] vertices;
        private Texture2D texture;

        private int vertexBufferObject;

        private bool flippedX, flippedY;

        public Texture2D Texture => texture;

        public VertexArray(Texture2D texture, Vertex[] vertices = null,
            float r = 0,
            float g = 0,
            float b = 0,
            float a = 0)
        {
            vertexBufferObject = GL.GenBuffer();

            if (vertices == null)
            {
                this.vertices = VerticesFromDimensions(texture.Width, texture.Height, -1, -1, 0, 0, r, g, b, a);
            }
            else
            {
                this.vertices = vertices;
            }

            flippedX = false;
            flippedY = false;
            
            this.texture = texture;
        }

        public void Use()
        {
            GL.BindTexture(TextureTarget.Texture2D, texture.ID);
        }
        
        //potentially remove?
        public void Flip(bool x=true, bool y=true)
        {
            if (x)
            {
                vertices[0] = new Vertex(new Vector2(0, 0), flippedX ? new Vector2(1, 0) : new Vector2(0, 0));
                vertices[2] = new Vertex(new Vector2(texture.Width, texture.Height), flippedX ? new Vector2(0, 1) : new Vector2(1, 1));
                flippedX = !flippedX;
            }
            if (y)
            {
                vertices[1] = new Vertex(new Vector2(0, texture.Height), flippedY ? new Vector2(1, 1) : new Vector2(0, 1));
                vertices[3] = new Vertex(new Vector2(texture.Width, 0), flippedY ? new Vector2(0, 0) : new Vector2(1, 0));
                flippedY = !flippedY;
            }
        }

        public void BindVertexBuffer()
        {
            GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, VERTEXARRAY_INDEX_COUNT * Vertex.SizeInBytes, vertices, BufferUsageHint.StaticDraw);
        }

        //remove
        public void UnbindVertexBuffer()
        {
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
        }

        public void Dispose()
        {
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.DeleteBuffer(vertexBufferObject);
        }

        /// <summary>
        /// This function is not loop friendly, but it doesn't matter right now as this is not used in the main loop.
        /// </summary>
        /// <param name="width"></param>
        /// <param name="maxWidth"></param>
        /// <param name="height"></param>
        /// <param name="maxHeight"></param>
        /// <param name="indexX"></param>
        /// <param name="indexY"></param>
        /// <returns></returns>
        public static Vertex[] VerticesFromDimensions(
            float width, 
            float height, 
            float subWidth=-1, 
            float subHeight=-1, 
            int indexX=0, 
            int indexY=0, 
            float r = 0,
            float g = 0,
            float b = 0,
            float a = 0)
        {
            float vertX = (subWidth > 0) ? subWidth / width : 1;
            float vertY = (subHeight > 0) ? subHeight / height : 1;

            float vertXi1 = vertX * indexX;
            float vertXi2 = vertX * (indexX + 1);
            float vertYi1 = vertY * indexY;
            float vertYi2 = vertY * (indexY + 1);

            return new Vertex[]
                {
                    new Vertex(new Vector2(0, 0), new Vector2(vertXi1, vertYi2), r, g, b, a),
                    new Vertex(new Vector2(0, subHeight), new Vector2(vertXi1, vertYi1), r, g, b, a),
                    new Vertex(new Vector2(subWidth, subHeight), new Vector2(vertXi2, vertYi1), r, g, b, a),
                    new Vertex(new Vector2(subWidth, 0), new Vector2(vertXi2, vertYi2), r, g, b, a),
                };
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Exceptions.Services
{
    public class ServiceNotFoundException : Exception
    {
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace isometricgame.GameEngine.Events.Arguments
{
    public class FrameArgument
    {
        /// <summary>
        /// Total elapsed time since game launch.
        /// </summary>
        public readonly double Time;

        /// <summary>
        /// Time since last loop.
        /// </summary>
        public readonly double DeltaTime;

        public FrameArgument(double time, double deltaTime) { Time = time; DeltaTime = deltaTime; }
    }
}
<file_sep>﻿using isometricgame.GameEngine.Rendering;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using isometricgame.GameEngine.WorldSpace;

namespace isometricgame.GameEngine.Systems.Serialization
{
    public class AssetProvider : GameSystem
    {

        public AssetProvider(Game game) 
            : base(game)
        {
        }

        #region Texture2Ds

        public Texture2D LoadTexture(string filePath, bool pixelated = true)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException();

            Bitmap bmp = new Bitmap(filePath);

            Texture2D texture = new Texture2D(bmp, pixelated);
                        
            return texture;
        }

        #endregion

        #region Sprites

        public Sprite ExtractSpriteSheet(
            string filePath, 
            string name = null, 
            int width=-1, 
            int height=-1, 
            float offsetX = 0, 
            float offsetY = 0,
            float r = 0,
            float g = 0,
            float b = 0,
            float a = 0)
        {
            Texture2D texture = LoadTexture(filePath);

            return new Sprite(
                texture, 
                (width > 0) 
                    ? width 
                    : texture.Width, 
                (height > 0) 
                    ? height 
                    : texture.Height, 
                (name == null) 
                    ? filePath 
                    : name, 
                offsetX / texture.Width, 
                offsetY / texture.Height,
                0,
                r,
                g,
                b,
                a);
        }

        #endregion

        #region legacy
        /*
public void LoadTileSet(string filePath, string assetName, SpriteLibrary library)
{
    TileSpriteAtlas tileset = new TileSpriteAtlas(LoadTileTextureSheet(filePath, Tile.TILE_WIDTH, Tile.TILE_HEIGHT));
    library.RecordSpriteSet(assetName, tileset,
        (i) => (i).ToString()
        );
}
*/

        /*
    public Texture2D[] LoadTileTextureSheet(string filePath, int textureWidth, int textureHeight, bool pixelated = false)
    {
        SpriteSheetReader ssr = new SpriteSheetReader(filePath, textureWidth, textureHeight);
        Texture2D[] textures;

        textures = new Texture2D[16];
        BitmapData bmpd;

        for (int i = 0; i < 8; i++)
        {
            bmpd = ssr.Lock(0, i);
            textures[i] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
            ssr.Unlock(bmpd);
        }
        bmpd = ssr.Lock(0, 2);
        textures[8] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);
        bmpd = ssr.Lock(0, 3);
        textures[9] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);
        bmpd = ssr.Lock(0, 8);
        textures[10] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);
        bmpd = ssr.Lock(0, 9);
        textures[11] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);


        /*for (int i = 10; i < 12; i++)
        {
            bmpd = ssr.Lock(0, i-2);
            textures[i+2] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
            ssr.Unlock();
        }
        bmpd = ssr.Lock(0, 6);
        textures[12] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);
        bmpd = ssr.Lock(0, 7);
        textures[13] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);
        bmpd = ssr.Lock(0, 10);
        textures[14] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
        ssr.Unlock(bmpd);
        textures[15] = textures[0];

        ssr.Dispose();
        return textures;
    }
    */
        /*
        public Texture2D[] LoadTextureSheet(string filePath, int textureWidth, int textureHeight, int count = -1, bool pixelated = false)
        {
            BitmapData bmpd;
            SpriteSheetReader ssr = new SpriteSheetReader(filePath, textureWidth, textureHeight);
            Texture2D[] textures = new Texture2D[(count > 0) ? count : (ssr.SheetWidth * ssr.SheetHeight) / (textureWidth * textureHeight)];
            int rows = ssr.SheetHeight / textureHeight, columns = ssr.SheetWidth / textureWidth;

            for (int row = 0; row < rows; row++)
            {
                for (int column = 0; column < columns; column++)
                {
                    int index = column + (row * columns);
                    if (count > -1 && index >= count)
                        return textures;

                    bmpd = ssr.Lock(column, row);
                    textures[index] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
                    ssr.Unlock(bmpd);
                }
            }
            bmpd = ssr.Lock(0, 0);
            textures[0] = BindTexture(bmpd, textureWidth, textureHeight, pixelated);
            ssr.Unlock(bmpd);

            ssr.Dispose();
            return textures;
        }

        public Texture2D[] LoadTextureSheet_1(string filePath, int textureWidth, int textureHeight, int count = -1, bool pixelated = false)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException();

            Texture2D[] textures;

            Bitmap bmp = new Bitmap(filePath);

            textures = new Texture2D[(count > 0) ? count : (bmp.Width * bmp.Height) / (textureWidth * textureHeight)];

            int rows = bmp.Height / textureHeight, columns = bmp.Width / textureWidth;

            for (int column = 0; column < columns; column++)
            {
                for (int row = 0; row < rows; row++)
                {
                    int index = column * rows + row;
                    if (index >= textures.Length)
                        return textures;

                    BitmapData bmpd = bmp.LockBits(new Rectangle(textureWidth * column, textureHeight * row, textureWidth, textureHeight), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                    Texture2D texture = BindTexture(bmpd, textureWidth, textureHeight, pixelated);

                    bmp.UnlockBits(bmpd);

                    textures[index] = texture;
                }
            }

            return textures;
        }
        */

        #endregion

    }
}
