diff --git a/CMakeLists.txt b/CMakeLists.txt
index 86711d85b7766c380b260fb969e6acb659f383dc..2ec0a899b7e1632d87bf6515b445aa0b37802d0d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -65,6 +65,9 @@ target_sources(
     PRIVATE ${SRCDIR}/src/VertexArray.cpp
     PRIVATE ${SRCDIR}/src/VertexBuffer.cpp
     PRIVATE ${SRCDIR}/src/Watcher.cpp
+
+	PRIVATE ${SRCDIR}/src/UniformBuffer.cpp
+	PRIVATE ${SRCDIR}/src/ShaderIntrospector.cpp
 )
 
 #
diff --git a/Notes to self.txt b/Notes to self.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9b79d05403aa488f82045708d8a61140239062cf
--- /dev/null
+++ b/Notes to self.txt	
@@ -0,0 +1,2 @@
+Use shader introspection find which shaders need to attach to the uniform block points, resulting in sets of shaders where binding point would be the key.
+this way we can loop through them
\ No newline at end of file
diff --git a/assets/materials/brick.yml b/assets/materials/brick.yml
index c3da7b67d997986a45d4e0b74b6dd1afc87f7f60..54a753956a8525abade168b20700293ee0b0d5c8 100644
--- a/assets/materials/brick.yml
+++ b/assets/materials/brick.yml
@@ -1,7 +1,10 @@
-maps: 2
+maps: 3
+  mainTex: scifi
+  bumpTex: scifi_nrm
+  specTex: scifi_spec
 
-  mainTexture: brickwall
-  specularTex: brickwall-nrm
-
-values: 1
-  test: 0.8
+values: 4
+  opacity: 1
+  intensity: 1
+  bumpiness: 1
+  specularity: 0
diff --git a/assets/models/cube.yml b/assets/models/cube.yml
index f0cd7325336aa694d033f599bf888333dd315f0f..e8a25b54f8eec50c3f9a77557945a2f878fb1d9b 100644
--- a/assets/models/cube.yml
+++ b/assets/models/cube.yml
@@ -41,7 +41,6 @@ v:  1.0  1.0 -1.0   0.577  0.577 -0.577  1 0  255 255 255 80
 v: -1.0  1.0  1.0  -0.577  0.577  0.577  0 1  255 255 255 80
 v:  1.0  1.0  1.0   0.577  0.577  0.577   1 1  255 255 255 80
 
-
 meshes: 2
 
 mesh: innercube
@@ -68,7 +67,7 @@ mesh: innercube
 
 mesh: outercube
     material: brick
-    shader: base
+    shader: edge
     triangles: 12
     t: 14 15 16
     t: 16 17 14
diff --git a/assets/shaders/base.glsl b/assets/shaders/base.glsl
index 8a98e2b8ba44e9ef3903ab22364662305cd922b1..169e01cd28b3284432de13e3d1fb5e99ce0f64fb 100644
--- a/assets/shaders/base.glsl
+++ b/assets/shaders/base.glsl
@@ -1,73 +1,143 @@
 #shader vertex
-#version 410 core
+#version 410
 
 layout(location = 0) in vec4 position;
 layout(location = 1) in vec3 normal;
 layout(location = 2) in vec2 uv;
-layout(location = 3) in vec4 vertex_color_from_program;
+layout(location = 3) in vec4 vertex_color;
 
-noperspective out vec2 texCoord;
-smooth out vec4 vertex_color_out;
+out vec2 texCoord;
+out vec4 vertex_color_out;
 out vec4 pos;
 
-uniform mat4 projection = mat4(1, 0, 0, 0,
-    0, 1, 0, 0,
-    0, 0, 1, 0,
-    0, 0, 0, 1);
+uniform mat4 m2w;
+uniform float time = 0;
 
-uniform mat4 view = mat4(1, 0, 0, 0,
-    0, 1, 0, 0,
-    0, 0, 1, 0,
-    0, 0, 0, 1);
+layout(std140) uniform OK_Matrices{
+    mat4 projection;
+    mat4 view;
+    vec4 view_position;
+};
 
-uniform mat4 model = mat4(1, 0, 0, 0,
-    0, 1, 0, 0,
-    0, 0, 1, 0,
-    0, 0, 0, 1);
 
-uniform float time = 0;
 
 mat4 rotate(float x, float y, float z) {
     return mat4(
-        (cos(y + z) + cos(y - z)) / 2,                                                                              (-sin(y + z) + sin(y - z)) / 2,                                                                             -sin(y),                        0,
-        (cos(x + y + z) - cos(x - y + z) + cos(x + y - z) - cos(x - y - z) + 2 * sin(x + z) - 2 * sin(x - z)) / 4,  (2 * cos(x + z) + 2 * cos(x - z) - sin(x + y + z) + sin(x - y + z) + sin(x + y - z) - sin(x - y - z)) / 4,  (-sin(x + y) - sin(x - y)) / 2, 0,
-        (-2 * cos(x + z) + 2 * cos(x - z) + sin(x + y + z) - sin(x - y + z) + sin(x + y - z) - sin(x - y - z)) / 4, (cos(x + y + z) - cos(x - y + z) - cos(x + y - z) + cos(x - y - z) + 2 * sin(x + z) + 2 * sin(x - z)) / 4,  (cos(x + y) + cos(x - y)) / 2,  0,
-        0,                                                                                                          0,                                                                                                          0,                              1
+        (cos(y + z) + cos(y - z)) / 2, (-sin(y + z) + sin(y - z)) / 2, -sin(y), 0,
+        (cos(x + y + z) - cos(x - y + z) + cos(x + y - z) - cos(x - y - z) + 2 * sin(x + z) - 2 * sin(x - z)) / 4, (2 * cos(x + z) + 2 * cos(x - z) - sin(x + y + z) + sin(x - y + z) + sin(x + y - z) - sin(x - y - z)) / 4, (-sin(x + y) - sin(x - y)) / 2, 0,
+        (-2 * cos(x + z) + 2 * cos(x - z) + sin(x + y + z) - sin(x - y + z) + sin(x + y - z) - sin(x - y - z)) / 4, (cos(x + y + z) - cos(x - y + z) - cos(x + y - z) + cos(x - y - z) + 2 * sin(x + z) + 2 * sin(x - z)) / 4, (cos(x + y) + cos(x - y)) / 2, 0,
+        0, 0, 0, 1
     );
 }
 
+out vec3 fragVert;
+out vec3 fragNormal;
+
+vec4 MVP(in vec4 position) {
+    return projection * view * m2w * position;
+}
+
+
 void main() {
 
-// model to world space transformations = transform
-// translation * rotation * scale * vertexPos;
+    float F = sqrt(position.x*position.x + position.y*position.y + position.z*position.z) * 0.01;
+    mat4 rot = rotate(0, time*F, 0);
 
-    float F = sqrt(position.x*position.x + position.y*position.y + position.z*position.z);
-    gl_Position = projection * view * model * position;
-    
-    vertex_color_out = vertex_color_from_program;
-    texCoord = uv;
-    pos = gl_Position;
-}
+    vec4 rotatedNormal = rot * vec4(normal, 1);
 
+    // Pass some variables to the fragment shader
+    //fragNormal = vec3(rotatedNormal);
+    vertex_color_out = rotatedNormal;
+    texCoord = uv;
+    fragNormal = mat3(transpose(inverse(m2w))) * normal;
 
+    vec4 out_position = MVP(position);
+    gl_Position = out_position;
 
+    fragVert = vec3(m2w * position);
+}
 
 #shader fragment
-#version 410
+#version 140
+
+#define MAX_LIGHTS 8
+
 in vec4 gl_FragCoord;
-// in vec2 gl_PointCoord; // @NOTE Not supported on [macos, openGL 4.1]
-noperspective in vec2 texCoord;
-smooth in vec4 vertex_color_out;
-in vec4 pos;
+in vec2 texCoord;
+in vec3 fragNormal;
+in vec3 fragVert;
+
 out vec4 out_color;
 
 uniform float time = 0;
 
-uniform sampler2D mainTexture;
-
-uniform float test = 1;
+uniform sampler2D mainTex;
+uniform sampler2D bumpTex;
+uniform sampler2D specTex;
+
+uniform float opacity = 0;
+uniform float specularity = 1;
+uniform float intensity = 1;
+uniform float bumpiness = 1;
+
+uniform mat4 m2w;
+
+layout(std140) uniform OK_Matrices{
+    mat4 projection;
+    mat4 view;
+    vec4 view_position;
+};
+
+struct OK_Light {
+    vec4 position;
+    vec4 intensities;
+};
+
+layout(std140) uniform OK_Lights{
+    OK_Light light[MAX_LIGHTS];
+//float spread;
+//float constant;
+//float linear;
+//float quadratic;
+};
+
+vec3 OK_PointLight(in vec3 position, in vec3 intensities/*, in float constant, in float linear, in float quadratic*/) {
+    //Ambience
+    float ambientStrength = 0.1;
+    vec3 ambient = ambientStrength * intensities;
+
+    vec3 bump = texture(bumpTex, texCoord).rgb*intensity;
+    // Diffussion
+    vec3 norm = normalize(fragNormal*bump);
+    vec3 lightDir = normalize(position - fragVert);
+    float diffusion = max(dot(norm, lightDir), 0.0);
+    vec3 diffuse = diffusion * intensities;
+
+    // Specularity
+    //float specularStrength = 0.5;
+    //vec3 viewDir = normalize(view_position.xyz - fragVert);
+    //vec3 reflectDir = reflect(-lightDir, norm);
+    //float specPower = pow(max(dot(viewDir, reflectDir), 0.0), 32);
+    //vec3 specular = specularStrength * specPower *  intensities;
+
+    // Attenuation
+    //float distance = length(position - fragVert);
+    float attenuation = 1.0; // /(constant + linear * distance + quadratic * (distance * distance));
+
+    return (ambient*attenuation + diffuse * attenuation /*+ specular*specularity*attenuation*/);
+}
 
 void main() {
-    out_color = test*vec4(texture(mainTexture, texCoord).rgb * vertex_color_out.rgb, vertex_color_out.a);
+
+    vec3 diff = texture(mainTex, texCoord).rgb;
+    vec3 bump = texture(bumpTex, texCoord).rgb;
+    vec3 spec = texture(specTex, texCoord).rgb;
+
+    vec3 light0 = OK_PointLight(light[0].position.xyz, light[0].intensities.rgb /*,light[0].constant,light[0].linear, light[0].quadratic*/);
+    vec3 light1 = OK_PointLight(light[1].position.xyz, light[1].intensities.rgb /*,light[1].constant,light[1].linear, light[1].quadratic*/);
+    vec3 light2 = OK_PointLight(light[2].position.xyz, light[2].intensities.rgb /*,light[1].constant,light[1].linear, light[1].quadratic*/);
+
+    //out_color = vec4(fragNormal, 1)*0.2 + 0.8*vec4(texture(mainTex, texCoord).rgb , vertex_color_out.a);
+    out_color = vec4((light0+ light1+ light2) * diff, 1);
 }
-//out_color = vec4(texture(mainTexture, texCoord).rgb * vertex_color_out.rgb, vertex_color_out.a);
+//out_color = vec4(texture(mainTexture, texCoord).rgb * vertex_color_out.rgb, vertex_color_out.a);
\ No newline at end of file
diff --git a/assets/shaders/default.glsl b/assets/shaders/default.glsl
index 9536132f6f05063c946e6415a67da69a0b1a21c3..a2c599dc67c1cc89d1e800dbaae10bb125ff6fff 100644
--- a/assets/shaders/default.glsl
+++ b/assets/shaders/default.glsl
@@ -61,4 +61,4 @@ uniform float transparency = 1;
 
 void main() {
     out_color = transparency*vec4(texture(mainTexture, texCoord).rgb, 1);
-}
+}
\ No newline at end of file
diff --git a/assets/textures/example.jpg b/assets/textures/example.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..80e18a0e645c93427a2e755731659f550496afeb
Binary files /dev/null and b/assets/textures/example.jpg differ
diff --git a/assets/textures/example_nrm.jpg b/assets/textures/example_nrm.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..dfd5e3f6b0943cb9def857668c2daa36971cb6c8
Binary files /dev/null and b/assets/textures/example_nrm.jpg differ
diff --git a/assets/textures/nrm_test.jpg b/assets/textures/nrm_test.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..e96665c655e42d8ddb438c3a736786648d54b6a7
Binary files /dev/null and b/assets/textures/nrm_test.jpg differ
diff --git a/assets/textures/scifi.jpg b/assets/textures/scifi.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ac700da85584e76141fc9b99d9f5737f198abe57
Binary files /dev/null and b/assets/textures/scifi.jpg differ
diff --git a/assets/textures/scifi_nrm.jpg b/assets/textures/scifi_nrm.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..886fd7fecd9a6054b7020f22a33a577d02c2232d
Binary files /dev/null and b/assets/textures/scifi_nrm.jpg differ
diff --git a/assets/textures/scifi_spec.jpg b/assets/textures/scifi_spec.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..667c04f08d7e43b12eb9a97af73924fe136917ff
Binary files /dev/null and b/assets/textures/scifi_spec.jpg differ
diff --git a/include/overkill/Config.hpp b/include/overkill/Config.hpp
index 436228a574369e7cc72c05911845c3f65354ff34..0bee93ede1f0438839449c818a65a6acd502bf13 100644
--- a/include/overkill/Config.hpp
+++ b/include/overkill/Config.hpp
@@ -22,7 +22,8 @@ namespace overkill::C
     constexpr float NearClip = 0.1f;
     constexpr float FarClip  = 100.0f;
     constexpr float CameraOffset = -70;      //How far back the camera is from the center of the scene.
-    const glm::vec4 ClearColor { 0.05f, 0.06f, 0.075f, 1.0f };
+    const glm::vec4 ClearColor { 0.05f, 0.06f, 0.075f, 1.0f }; // Sexy blue
+    
     constexpr char PathBaseShader[] = "assets/shaders/base.shader";
     constexpr char PathBaseTexture[] = "assets/textures/Checkers.jpg";
     constexpr float PI = 3.14159265359f;
@@ -33,5 +34,5 @@ namespace overkill::C
     constexpr char ModelsFolder[]    = "assets/models";
     constexpr char ScenesFolder[]    = "assets/scenes";
 
-       
+    constexpr unsigned int MAX_LIGHTS = 8; //inject it into shaders
 }
diff --git a/include/overkill/Entity.hpp b/include/overkill/Entity.hpp
index 2c77fcb5f2edb30fc08be36c4cbe18508afc748a..55d33a691fe78d3dc97b459d5949f3b068f1d827 100644
--- a/include/overkill/Entity.hpp
+++ b/include/overkill/Entity.hpp
@@ -1,5 +1,4 @@
 #pragma once
-
 #include <string>
 
 
@@ -34,4 +33,26 @@ public:
     void update(float dt);  
 };
 
+struct Transform
+{
+
+	const glm::vec3 right{ 1.0f,0.0f,0.0f };
+	const glm::vec3 up{ 0.0f,1.0f,0.0f };
+	const glm::vec3 forward{ 0.0f,0.0f,1.0f };
+	const glm::vec3 one{ 1.0f, 1.0f, 1.0f };
+	const glm::vec3 zero{ 0.0f ,0.0f ,0.0f };
+
+	glm::vec3 m_position = zero;
+	glm::vec3 m_rotation = zero;
+	glm::vec3 m_scale    = one;
+
+	auto modelToWorld()->glm::mat4;
+};
+
+
+struct Light : public Transform
+{
+	glm::vec3 intensities = one; //a.k.a the color of the light
+};
+
 }
\ No newline at end of file
diff --git a/include/overkill/EntityModel.hpp b/include/overkill/EntityModel.hpp
index 0a4f2aae0c8c7c772d1320c1fc315ced65e832ba..e520f58ec928d7beabf1c7986dafeab54890f4db 100644
--- a/include/overkill/EntityModel.hpp
+++ b/include/overkill/EntityModel.hpp
@@ -10,6 +10,7 @@
 
 namespace overkill
 {
+    class Renderer;
 
 class EntityModel : public Entity
 {
@@ -17,9 +18,9 @@ private:
     int m_modelID;         // What model with id will be used to draw Entity.
     glm::vec3 m_scale;
 
+public:
     glm::mat4 getModelMatrix();
 
-public:
     EntityModel(C::Tag modelTag,
                 glm::vec3 pos = glm::vec3(0,0,0), glm::vec3 rot = glm::vec3(0,0,0), 
                 glm::vec3 scale = glm::vec3(1,1,1), glm::vec3 vel = glm::vec3(0,0,0), 
@@ -34,4 +35,4 @@ public:
     void draw();
 };
 
-}
\ No newline at end of file
+}
diff --git a/include/overkill/Renderer.hpp b/include/overkill/Renderer.hpp
index f34f184febcc167422c12fc862a3213fb72a7a58..8542bc2112a5d59895f8fc558a9a8670e561eeee 100644
--- a/include/overkill/Renderer.hpp
+++ b/include/overkill/Renderer.hpp
@@ -7,24 +7,23 @@
 #include <overkill/ElementBuffer.hpp>
 #include <overkill/ShaderProgram.hpp>
 #include <overkill/Model.hpp>
+#include <overkill/EntityModel.hpp>
 
 namespace overkill
 {
 
+class EntityModel;
+
 class Renderer
 {
 public:
     static void clear();
     static void draw(const VertexArray& va, const ElementBuffer& eb, const ShaderProgram& shader);
     static void draw(const Model& model, glm::mat4 modelMatrix = glm::mat4(1));   // Model matrix is translation rotation and scale of the model.
-
-
+   
+    static void draw( EntityModel& entity, float t );
+    //static void draw( EntityModel& entity, float t);
 };
 
-class EdgeRenderer : public Renderer
-{
-public:
-    void drawEdged(const VertexArray& va, const ElementBuffer& eb, const ShaderProgram& shader, const ShaderProgram& edgeShader) const;
-};
 
 }
diff --git a/include/overkill/ShaderIntrospector.hpp b/include/overkill/ShaderIntrospector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d0ec846ce46aa455ab86f75753cc4f215e556fe
--- /dev/null
+++ b/include/overkill/ShaderIntrospector.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <overkill/gl_caller.hpp>
+#include <string>
+#include <vector>
+
+namespace overkill
+{
+class ShaderIntrospector
+{
+public:
+    static GLint  getActiveBlockCount(const GLuint program);
+    static const  std::vector<GLint> getUniformBlockUniformIndices(const GLuint program, const GLuint uBlockIndex);
+    static const  std::string getUnifromBlockName(const GLuint program, const GLint uBlockIndex);
+    static GLuint getUniformBlockIndex(const GLuint program, const std::string& name);
+    static GLuint getUniformBufferMaxSize(const GLuint program);
+    static GLint  getUniformBlockBinding(const GLuint program, GLuint uniformBlockIndex);
+};
+
+}
+
+/*
+
+map:
+    tag:
+        program: blockIndex
+        program: blockIndex
+
+
+*/
\ No newline at end of file
diff --git a/include/overkill/ShaderProgram.hpp b/include/overkill/ShaderProgram.hpp
index 6b73cee2b68629ce83add8726656ec744fe9a875..a9eb506e962dbd8a16f63a05771711ef6d6e336a 100644
--- a/include/overkill/ShaderProgram.hpp
+++ b/include/overkill/ShaderProgram.hpp
@@ -11,6 +11,7 @@
 #include <overkill/gl_util.hpp>
 #include <overkill/Texture.hpp>
 #include <overkill/Material.hpp>
+#include <overkill/ShaderIntrospector.hpp>
 
 namespace overkill 
 {
@@ -33,6 +34,7 @@ class ShaderProgram
     };
 private:
     std::unordered_map<std::string, GLint> uniforms;
+    std::unordered_map<std::string, GLint> uniformBlocks;
 
     void construct(const std::string& vert, const std::string& frag, const std::string& geom);
 
@@ -58,7 +60,8 @@ public:
 
     static ShaderSource ParseProgram(const std::string& file);
     static GLuint CompileShader(GLuint type, const std::string& source);
-
+    GLuint getUniformBlockIndex(const std::string& blockName) const;
+    
 };
 
 // @note unused functions
diff --git a/include/overkill/ShaderSystem.hpp b/include/overkill/ShaderSystem.hpp
index cd4fe69d6809deae186274eeffaa7f9fd0e5f08d..844500a86c43a544dfe17e4fc92205b058d792dc 100644
--- a/include/overkill/ShaderSystem.hpp
+++ b/include/overkill/ShaderSystem.hpp
@@ -1,16 +1,30 @@
 #pragma once
 
+#include <set>
 #include <vector>
 #include <string>
 #include <unordered_map>
 
 #include <overkill/Config.hpp>
 #include <overkill/ShaderProgram.hpp>
+#include <overkill/UniformBuffer.hpp>
+#include <overkill/ShaderIntrospector.hpp>
 
 #include <overkill/Watcher.hpp>
 
 namespace overkill
 {
+    struct MVP {
+        glm::mat4 view;
+        glm::mat4 projection;
+    };
+
+    struct LightData {
+        glm::vec4 position;
+        glm::vec4 intensities;
+        float spread;
+    };
+
 
 class ShaderSystem 
 {
@@ -18,17 +32,27 @@ public:
     using OnUpdate = void (*)(C::ID, C::ID, C::ID);
     struct UpdateCallback 
     {
-        
         C::Tag   tag;
         C::ID    modelID;
         C::ID    meshID;
         OnUpdate callback;
     };
 private:
-    static std::vector<ShaderProgram> m_shaderPrograms;
-    static std::unordered_map<C::Tag, C::ID> m_mapShaderProgramID;
-    static std::vector<UpdateCallback> m_updateCallbacks;
+
+
+    static std::unordered_map<C::Tag, std::set<GLuint>> m_mapUniformBufferTargets;
+    static std::vector<UniformBuffer>           m_uniformBuffers;
+    static std::unordered_map<C::Tag, C::ID>    m_mapUniformBuffersID;
+
+    static std::vector<ShaderProgram>           m_shaderPrograms;
+    static std::unordered_map<C::Tag, C::ID>    m_mapShaderProgramID;
+
+    static std::vector<UpdateCallback>          m_updateCallbacks;
+
     static void push(const C::Tag tag, const std::string& filepath);
+    static void pushUniformBuffer(const C::Tag&& tag, GLuint size);
+    static void linkUniformBlocks();
+    static void linkUniformBlocksForAll();
 
 public:
     // <summary> Load all shader data onto GPU memory. 
@@ -41,7 +65,18 @@ public:
     static auto copyByTag(const C::Tag& tag) -> ShaderProgram;
     static auto copyById(C::ID shaderProgramID) -> ShaderProgram;
     static void bindOnUpdate(const C::Tag& shaderTag, C::ID modelID, C::ID meshID, OnUpdate onUpdate);
+    
     static void unbindAll();
+
+    static auto getUniformBufferIdByTag(const C::Tag& tag)->C::ID;
+    static auto getUniformBufferByTag(const C::Tag& tag) -> const UniformBuffer&;
+    static auto getUniformBufferById(C::ID uBufferID) -> const UniformBuffer&;
+    
+
+
+	GLuint getBlockUniformLocation(const C::Tag& uBlock, const C::Tag& uniform);
+    static auto updateUniformBlock(C::ID uBufferID, C::ID uniformLocation);
+    //void ShaderProgram::bindUniformBlockToAll(GLuint blockIndex);
 };
 
 }
diff --git a/include/overkill/UniformBuffer.hpp b/include/overkill/UniformBuffer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a9fddd50bbf2062809d8aef4530c98305bf62a9
--- /dev/null
+++ b/include/overkill/UniformBuffer.hpp
@@ -0,0 +1,145 @@
+#pragma once
+
+#include <overkill/gl_caller.hpp>
+#include <overkill/ShaderProgram.hpp>
+#include <overkill/Config.hpp>
+#include <vector>
+#include <unordered_map>
+
+namespace overkill
+{
+
+
+struct BlockLayout
+{
+private:
+    std::unordered_map<C::Tag, GLuint>      m_vars;
+    //std::unordered_map<C::Tag, BlockLayout> m_blocks;
+    GLuint m_blockSize; //the combined size of this block
+    C::Tag m_name;
+public:
+    BlockLayout(const char* name = "");
+    inline GLsizei size() const { return m_blockSize; }
+    explicit operator C::Tag() const;
+    GLuint indexOfUniform(const C::Tag& name) const
+    {
+        auto search = m_vars.find(name); //@TODO discuss usage of at() as it works just as well here
+        if (search != m_vars.end())
+        {
+            return search->second;
+        }
+        LOG_WARN("indexOfUniform: \"%s\" cannot be found in buffer!\n has it been added in the layout?", name.c_str());
+        return 0;
+        //return m_vars.at(name);
+    }
+
+    void pushBlock(const BlockLayout& block, const GLuint count = 1) //allows direct access to the inner-element indices
+    {
+
+        /* TESTING THE LOGIC BEHIND THIS:
+
+            auto block0 = BlockLayout("b0")
+            block0.push<16>("position");
+            block0.push<16>("color");
+                // block0.size = 32
+
+            block1.push<16>("somevec");
+                // block1.size = 16
+            block1.pushBlock(block0);
+                // somevec,     0
+                // position,    16
+                // color,       32
+                // block1.size = 48
+        */
+        for (GLuint i = 0; i < count; i++)
+        {
+            for (const auto& var : block.m_vars)
+            {
+                m_vars.emplace(std::make_pair(block.m_name + '[' + std::to_string(i) + "]." + var.first, m_blockSize + var.second));
+            }
+            m_blockSize += block.m_blockSize;
+        }
+    }
+
+    void push(const C::Tag& name, GLuint size)
+    {
+        m_vars.emplace(std::make_pair(name, m_blockSize));
+        m_blockSize += size;
+    }
+};
+
+
+//struct UniformBufferLayout
+//{
+//
+//private:
+//	std::unordered_map<C::Tag, GLuint> m_uniforms;
+//	GLuint m_blockSize;
+//    C::Tag m_name;
+//public:
+//	UniformBufferLayout(const char* name = "") : m_blockSize(0), m_name(name) {}
+//
+//	template<GLuint size>
+//	void push(const C::Tag& name, GLuint count)
+//	{
+//	/*	m_uniforms.emplace(std::make_pair(name, m_blockSize));
+//		m_blockSize += size * componentCount;*/
+//        for (GLuint i = 0; i < count; i++)
+//        {
+//            push<size>(name + '[' + std::to_string(i) + ']');
+//        }
+//	}
+//
+//    template<GLuint size>
+//    void push<size>(const C::Tag& name)
+//    {
+//        m_uniforms.emplace(std::make_pair(name, m_blockSize));
+//        m_blockSize += size;
+//    }
+//
+//	inline GLsizei size()      const { return m_blockSize; }
+//
+//	inline GLuint indexOfUniform(const C::Tag& name) const
+//	{
+//        return m_uniforms.at(name);
+//
+//		//auto search = m_uniforms.find(name); //@TODO discuss usage of at() as it works just as well here
+//		//if (search != m_uniforms.end())
+//		//{
+//		//	return search->second;
+//		//}
+//		//LOG_ERROR("indexOfUniform: \"%s\" cannot be found in buffer!\n has it been added in the layout?", name.c_str());
+//		//return 0;
+//	}
+//    
+//};
+
+class UniformBuffer
+{
+
+private:
+    GLuint m_id;
+    C::Tag m_name;
+	BlockLayout m_blockLayout;
+public:
+
+	UniformBuffer(const char *name, const BlockLayout& layout, const GLenum drawMode);
+   
+    explicit operator C::Tag() const;
+    explicit operator GLuint() const;
+
+    void clean();
+
+    void bind() const;
+    void unbind() const;
+
+    inline GLuint blockSize()
+    {
+        return m_blockLayout.size();
+    }
+
+	GLuint getUniformIndex(const C::Tag& name) const;
+	void update(const C::ID index, GLsizeiptr size, const void *data);
+};
+
+}
diff --git a/include/overkill/VertexBufferAttribLayout.hpp b/include/overkill/VertexBufferAttribLayout.hpp
index a4b6e33616747f15779a2f34d57a4c74a28691d1..4bbab71ec7e4716eab5e84d818af7dad7bc4ac54 100644
--- a/include/overkill/VertexBufferAttribLayout.hpp
+++ b/include/overkill/VertexBufferAttribLayout.hpp
@@ -27,17 +27,26 @@ private:
 public:
     VertexBufferAttribLayout() : m_stride(0) {}
 
-    void push(GLuint count, GLenum type, bool normalized = false)
+    /// <summary>
+    /// <param name="count">The number of components the attribute has in the shaderprogram</param> 
+    /// <param name="type">The number of components the attribute has in the shaderprogram</param> 
+    /// <param name="normalized">wether or not the values should be normalized</param> 
+    /// </summary>
+
+	template<GLenum type>
+    void push(GLuint count, bool normalized = false)
     {
         m_attributes.push_back({ count, type, normalized });
-        if (type == GL_INT_2_10_10_10_REV)
-            m_stride += GLTypeSize(type);
-        else if (type == GL_SHORT)
-            m_stride += GLTypeSize(type);
-        else
-            m_stride += count * GLTypeSize(type);
-    }
-
+		m_stride += count * GLTypeSize(type);
+	}
+/*
+	template<>
+	void push<GL_INT_2_10_10_10_REV>(GLuint count, bool normalized)
+	{
+		m_attributes.push_back({ count, GL_INT_2_10_10_10_REV, normalized });
+        m_stride += count; //TODO allow for attributes to contain more than one withouth striding errors
+	}
+*/
     inline const std::vector<VertexBufferAttrib> getAttributes() const { return m_attributes; }
     inline GLuint getStride() const { return m_stride; }
 };
diff --git a/include/overkill/gl_drawSettings.hpp b/include/overkill/gl_drawSettings.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..86f2fd5ae8a5ff0c58fe333257e3eaa5eb171813
--- /dev/null
+++ b/include/overkill/gl_drawSettings.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <overkill/gl_caller.hpp>
+
+/*
+
+	READ ALL THE #pragma lines anc map them 
+
+*/
\ No newline at end of file
diff --git a/include/overkill/gl_util.hpp b/include/overkill/gl_util.hpp
index ae06578f7b3466d6199a715a4168f47e49a54388..4a21842501a8abd81cb679e25405be6198d33244 100644
--- a/include/overkill/gl_util.hpp
+++ b/include/overkill/gl_util.hpp
@@ -6,6 +6,6 @@
 #include <PMS/logger.h>
 #include <overkill/gl_caller.hpp>
 
-GLuint GLTypeSize(const GLenum type);
-std::string ShaderTypeName(const GLenum typeID);
-void GLPrintMaxContants();
+void GLPrintMaxConstants();
+auto GLTypeSize(const GLenum type) -> GLsizei;
+auto ShaderTypeName(const GLenum typeID) -> std::string;
diff --git a/src/ElementBuffer.cpp b/src/ElementBuffer.cpp
index f7ee529bc37e5251e377f429ae884209159ee859..c64abc2c16a5d10d9e8cf645cd8db97bbca9aff9 100644
--- a/src/ElementBuffer.cpp
+++ b/src/ElementBuffer.cpp
@@ -1,6 +1,5 @@
 #include "overkill/ElementBuffer.hpp"
 
-
 namespace overkill 
 {
 
diff --git a/src/Entity.cpp b/src/Entity.cpp
index 910336694f91d213684e5cd9c5b07a7e486261c2..fc133ae0e382f3641aa59e7befcf75f7e58d126f 100644
--- a/src/Entity.cpp
+++ b/src/Entity.cpp
@@ -47,4 +47,15 @@ namespace overkill
                 dt);*/
     }
 
+	auto Transform::modelToWorld() -> glm::mat4
+	{
+		auto m2w = glm::scale(glm::mat4(1), m_scale);
+
+		m2w = glm::rotate(m2w, m_rotation.x, right);
+		m2w = glm::rotate(m2w, m_rotation.y, up);
+		m2w = glm::rotate(m2w, m_rotation.z, forward);
+
+		m2w = glm::translate(m2w, m_position);
+		return m2w;
+	}
 }
\ No newline at end of file
diff --git a/src/EntityModel.cpp b/src/EntityModel.cpp
index 71f02cc0bebc1137147cf3249d08fb87ad8b4d3f..92ca2f5c9a37d6275b88c3099598c900f678ff0c 100644
--- a/src/EntityModel.cpp
+++ b/src/EntityModel.cpp
@@ -3,50 +3,51 @@
 namespace overkill
 {
 
-    EntityModel::EntityModel(C::Tag modelTag,
-                            glm::vec3 pos, glm::vec3 rot, 
-                            glm::vec3 scale, glm::vec3 vel, 
-                            glm::vec3 angVel) : Entity(modelTag, pos, rot, vel, angVel)
-    {
-        m_modelID =  ModelSystem::getIdByTag(modelTag);
-        m_scale = scale;
-    }
+
+EntityModel::EntityModel(C::Tag modelTag,
+                        glm::vec3 pos, glm::vec3 rot, 
+                        glm::vec3 scale, glm::vec3 vel, 
+                        glm::vec3 angVel) : Entity(modelTag, pos, rot, vel, angVel)
+{
+    m_modelID =  ModelSystem::getIdByTag(modelTag);
+    m_scale = scale;
+}
 
      
-    glm::mat4 EntityModel::getModelMatrix()
-    {   
-        glm::mat4 model = glm::mat4(1);
+glm::mat4 EntityModel::getModelMatrix()
+{   
+    glm::mat4 model = glm::mat4(1);
 
-        model =  glm::translate(model, m_position);
-        model = glm::rotate(model, m_rotation.x, glm::vec3(1,0,0));
-        model = glm::rotate(model, m_rotation.y, glm::vec3(0,1,0));
-        model = glm::rotate(model, m_rotation.z, glm::vec3(0,0,1));
-        model = glm::scale(model, m_scale);
+    model =  glm::translate(model, m_position);
+    model = glm::rotate(model, m_rotation.x, glm::vec3(1,0,0));
+    model = glm::rotate(model, m_rotation.y, glm::vec3(0,1,0));
+    model = glm::rotate(model, m_rotation.z, glm::vec3(0,0,1));
+    model = glm::scale(model, m_scale);
 
-        return model;
-                
-    }
+    return model;   
+}
 
     
-    int EntityModel::getModel()
-    {   return m_modelID;   }
+int EntityModel::getModel()
+{   return m_modelID;   }
 
-    glm::vec3 EntityModel::getScale()
-    {   return m_scale;  }
+glm::vec3 EntityModel::getScale()
+{   return m_scale;  }
 
-    void EntityModel::setModelByID(int modelID)
-    {   m_modelID = modelID;   }
+void EntityModel::setModelByID(int modelID)
+{   m_modelID = modelID;   }
     
-     void EntityModel::setModelByTag(C::Tag tag)
-    {   m_modelID =  ModelSystem::getIdByTag(tag);  }
+    void EntityModel::setModelByTag(C::Tag tag)
+{   m_modelID =  ModelSystem::getIdByTag(tag);  }
+
+void EntityModel::setScale(glm::vec3 scale)
+{   m_scale = scale;   }
 
-    void EntityModel::setScale(glm::vec3 scale)
-    {   m_scale = scale;   }
 
+void EntityModel::draw()
+{   
+    Renderer::draw(ModelSystem::getById(m_modelID), getModelMatrix());
+}
 
-    void EntityModel::draw()
-    {   
-        Renderer::draw(ModelSystem::getById(m_modelID), getModelMatrix());
-    }
 
-}
\ No newline at end of file
+}
diff --git a/src/Init.cpp b/src/Init.cpp
index e5289d567e1783eb4ea82deef2cd86cc3a652955..b2d1f45f5339678a3ec524df6cfabd124fbb9fda 100644
--- a/src/Init.cpp
+++ b/src/Init.cpp
@@ -61,10 +61,10 @@ void Init::OpenGL(const glm::vec4 background)
     GLCall(glCullFace(GL_BACK));// the face side to cull away: GL_FRONT | GL_BACK | GL_FRONT_AND_BACK
 
     GLCall(glEnable(GL_BLEND));
-    //GLCall(glEnable(GL_DEPTH_TEST)); //enabled to avoid ugly artifacts that depend on the angle of view and drawing order
+    GLCall(glEnable(GL_DEPTH_TEST)); //enabled to avoid ugly artifacts that depend on the angle of view and drawing order
     GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
 
-    GLPrintMaxContants(); 
+    GLPrintMaxConstants(); 
 }
 
 
diff --git a/src/ModelSystem.cpp b/src/ModelSystem.cpp
index 3d548c9ab7f0d379e262fbcf9196f5e1fba2b151..4a8cf3ff74f1951599f13a56dc294171148f453b 100644
--- a/src/ModelSystem.cpp
+++ b/src/ModelSystem.cpp
@@ -22,6 +22,38 @@ auto ModelSystem::getById(C::ID modelID) -> const Model&
     return ModelSystem::m_models[modelID];
 }
 
+auto packUV(float u, float v) -> GLshort
+{
+    const auto MAX = 127;
+    //const auto MIN = -128;
+    const auto CLAMPER = 255;
+   
+    return (GLint(v * MAX) & CLAMPER) << 8 | (GLint(u * MAX) & CLAMPER);
+}
+
+//TODO move it
+auto packNormal(float x, float y, float z) -> GLint
+{
+    
+	float magnitude = sqrt(x * x + y * y + z * z);
+	x /= magnitude;
+	y /= magnitude;
+	z /= magnitude;
+
+
+	const auto MAX = 511;		//01 1111 1111
+	const auto MIN = -512;		//10 0000 0000
+	// -1 * 511 = -511
+	GLint ix = (GLint(x * MAX) & 1023);
+	GLint iy = (GLint(y * MAX) & 1023);
+	GLint iz = (GLint(z * MAX) & 1023);
+	//1*511 =	511
+	//			01 0000 0000
+	//-1*511 = -511
+	//			10 0000 0001
+	GLint r = (iz << 20) | (iy << 10) | ix;
+	return r;
+}
 
 void ModelSystem::reload() 
 {   
@@ -92,10 +124,12 @@ void ModelSystem::load()
         newModel.m_vbo = VertexBuffer(vertices.data(), vertices.size() * sizeof(Vertex));
         
         auto vbufLayout = VertexBufferAttribLayout();
-        vbufLayout.push(3, GL_FLOAT);                       //position;
-        vbufLayout.push(3, GL_FLOAT);                       //normal
-        vbufLayout.push(2, GL_FLOAT);                       //uv
-        vbufLayout.push(4, GL_UNSIGNED_BYTE, GL_TRUE);      //color;
+
+        vbufLayout.push<GL_FLOAT>(3);                       //position;
+        vbufLayout.push<GL_FLOAT>(3);                       //normal
+        vbufLayout.push<GL_FLOAT>(2);                       //uv
+        vbufLayout.push<GL_UNSIGNED_BYTE>(4, GL_TRUE);      //color;
+
         newModel.m_vao.addBuffer(newModel.m_vbo, vbufLayout);
 
         //
diff --git a/src/Renderer.cpp b/src/Renderer.cpp
index ce2187393454f2793bbaa04ba2192b56a2d8c9a6..0a1abe41e3c2fa4f25e7a267bd31b4136addc122 100644
--- a/src/Renderer.cpp
+++ b/src/Renderer.cpp
@@ -1,5 +1,4 @@
 #include <overkill/Renderer.hpp>
-
 namespace overkill
 {
 
@@ -37,17 +36,25 @@ void Renderer::draw(const Model& model, glm::mat4 modelMatrix)
     // UNBIND [optional]... discuss
 }
 
-void EdgeRenderer::drawEdged(const VertexArray & va, const ElementBuffer & eb, const ShaderProgram& shader, const ShaderProgram& edgeShader) const
+void Renderer::draw( EntityModel& entity, float t) 
 {
-    va.bind();
-    eb.bind();
-    shader.bind();
+    auto model = ModelSystem::getById( entity.getModel() );
+    auto m2w   = entity.getModelMatrix();
 
-    GLCall(glDrawElements(GL_TRIANGLES, eb.count(), GL_UNSIGNED_INT, nullptr));
+    model.m_vao.bind();
 
-    edgeShader.bind();
-    GLCall(glLineWidth(1.0f))
-        GLCall(glDrawElements(GL_LINE_STRIP, eb.count(), GL_UNSIGNED_INT, nullptr));
+    for (auto mesh : model.m_meshes)
+    {
+        mesh.m_ebo.bind();
+
+        auto& shader = mesh.m_shaderProgram;
+        shader.bind();
+
+        GLCall(glUniform1f(shader.getUniformLocation("time"), t));
+        GLCall(glUniformMatrix4fv(shader.getUniformLocation("m2w"), 1, GL_FALSE, glm::value_ptr(m2w)));
+        GLCall(glDrawElements(GL_TRIANGLES, mesh.m_ebo.count(), GL_UNSIGNED_INT, nullptr));
+    }
 }
 
+
 }
diff --git a/src/ShaderIntrospector.cpp b/src/ShaderIntrospector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..42fbcaf41e18eb6a2d7f51be71fbf583cdee4110
--- /dev/null
+++ b/src/ShaderIntrospector.cpp
@@ -0,0 +1,85 @@
+#include <overkill/ShaderIntrospector.hpp>
+namespace overkill
+{
+
+// UNIFORMS
+/*
+const std::vector<GLint>& ShaderIntrospector::getUniformLocations(const GLuint program)
+{
+    GLsizei nameMaxLength;
+
+    GLCall(glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &nameMaxLength));
+    LOG_INFO("Uniform name maxlength: %i", nameMaxLength);
+    GLCall(glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &count));
+    LOG_INFO("\n\nActive uniforms: %i", count);
+    for (GLuint i = 0; i < count; i++)
+    {
+        char* uniformName = (char*)alloca(nameMaxLength * sizeof(char));
+        GLCall(glGetActiveUniform(id, i, nameMaxLength, &length, &size, &type, uniformName));
+    }
+    GLuint count;
+    GLCall(glGetActiveUniformsiv(program, uBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &count));
+
+    GLint indices;
+    GLCall(glGetActiveUniformsiv(program, uBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices));
+    
+    std::vector<GLint> result;
+    result.reserve(count);
+    for (GLuint i = 0 < count; i++)
+    {
+        result.push_back(indices[i]);
+    }
+    return result;
+}*/
+
+
+//UNIFORM BLOCKS
+
+GLint ShaderIntrospector::getActiveBlockCount(const GLuint program)
+{
+    GLint count;
+    GLCall(glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &count));
+    return count;
+}
+
+const std::string ShaderIntrospector::getUnifromBlockName(const GLuint program, const GLint uBlockIndex)
+{
+    //nameMaxLength = the longest name of a block for the given program
+    GLint nameMaxLength, length;
+    GLCall(glGetActiveUniformBlockiv(program, uBlockIndex, GL_UNIFORM_BLOCK_NAME_LENGTH, &nameMaxLength));
+    char* name = (char*)alloca(nameMaxLength * sizeof(char));
+    GLCall(glGetActiveUniformBlockName(program, uBlockIndex, nameMaxLength, &length, name));
+    return std::string(name);
+}
+
+const std::vector<GLint> ShaderIntrospector::getUniformBlockUniformIndices(const GLuint program, const GLuint uBlockIndex)
+{
+    GLint count;
+    GLCall(glGetActiveUniformBlockiv(program, uBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &count));
+
+    GLint* indices = (GLint*)alloca(count * sizeof(GLint));
+    GLCall(glGetActiveUniformBlockiv(program, uBlockIndex, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices));
+
+    std::vector<GLint> result;
+    result.reserve(count);
+    for (GLuint i = 0; i < count; i++)
+    {
+        result.push_back(indices[i]);
+    }
+    return result;
+}
+
+GLuint ShaderIntrospector::getUniformBlockIndex(const GLuint program, const std::string& name)
+{
+    GLuint index;
+    GLCall(index = glGetUniformBlockIndex(program, name.c_str()));
+    return index;
+}
+
+GLint ShaderIntrospector::getUniformBlockBinding(const GLuint program, GLuint uBlockIndex)
+{
+    GLint value;
+    //GLCall(glGetActiveUniformBlockiv(program, uBlockIndex))
+    return 0;
+}
+}
\ No newline at end of file
diff --git a/src/ShaderProgram.cpp b/src/ShaderProgram.cpp
index 46d30b6088dc8016203e8e675ec84b5abe48f2da..ca0ce7e6e9ddc9819cc5bc8214585fe4cc9828d7 100644
--- a/src/ShaderProgram.cpp
+++ b/src/ShaderProgram.cpp
@@ -1,4 +1,5 @@
-#include <overkill/ShaderProgram.hpp>
+#include <overkill/ShaderProgram.hpp>
+
 
 namespace overkill 
 {
@@ -37,39 +38,71 @@ void ShaderProgram::construct(const std::string& vert, const std::string& frag,
     }
 
     ///https://stackoverflow.com/questions/440144/in-opengl-is-there-a-way-to-get-a-list-of-all-uniforms-attribs-used-by-a-shade
-    GLint i;
+   
     GLint count;
-
     GLint size; // size of the variable
     GLenum type; // type of the variable (float, vec3 or mat4, etc)
-
-    const GLsizei bufSize = 16; // maximum name length
-    GLchar name[bufSize]; // variable name in GLSL
     GLsizei length; // name length
+    
+    GLsizei nameMaxLength;
+    
+    LOG_INFO("\n\n= = = = = =<| ATTRIBUTE INFO |>= = = = = =");
 
+    GLCall(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &nameMaxLength));
+    LOG_INFO("Attribute name maxlength: %i", nameMaxLength);
     GLCall(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, &count));
-    std::cout << "\nActive attributes: " << count << std::endl;
+    LOG_INFO("Active attributes: %i", count);
 
-    for (i = 0; i < count; i++)
+    for (GLuint i = 0; i < count; i++)
     {
-        GLCall(glGetActiveAttrib(id, (GLuint)i, bufSize, &length, &size, &type, name));
-        printf("Attribute #%d Type: %u Name: %s\n", i, type, name);
-
+        char* attribName = (char*)alloca(nameMaxLength * sizeof(char));
+        GLCall(glGetActiveAttrib(id, i, nameMaxLength, &length, &size, &type, attribName));
+        LOG_INFO("Attribute #%u Type: %u Name: %s", i, type, attribName);
     }
 
+    LOG_INFO("\n\n= = = = = =<| UNIFORM INFO |>= = = = = =");
+
+    GLCall(glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &nameMaxLength));
+    LOG_INFO("Uniform name maxlength: %i", nameMaxLength);
     GLCall(glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &count));
-    std::cout << "\nActive uniforms: " << count << std::endl;
+    LOG_INFO("\n\nActive uniforms: %i", count);
 
-    for (i = 0; i < count; i++)
+    for (GLuint i = 0; i < count; i++)
     {
-        GLCall(glGetActiveUniform(id, (GLuint)i, bufSize, &length, &size, &type, name));
-
-        printf("Uniform #%d Type: %u Name: %s\n", i, type, name);
-
+        char* uniformName = (char*)alloca(nameMaxLength * sizeof(char));
+        GLCall(glGetActiveUniform(id, i, nameMaxLength, &length, &size, &type, uniformName));
+        LOG_INFO("Uniform #%u, Type: %u, Name: %s", i, type, uniformName);
+        if (length > 15)
+        {
+            LOG_WARN(R"(
+    Uniform #%d, %s of type %u: 
+    names of uniforms should generally be shorter than 15 characters
+    for optimal lookup time, if %s is not touched very often this may still be OK)",
+                i, uniformName, type, uniformName
+            );
+        }
         GLint location;
-        GLCall(location = glGetUniformLocation(id, name));
-        uniforms.insert({ name, location});
+        GLCall(location = glGetUniformLocation(id, uniformName));
+        uniforms.insert({ uniformName, location });
     }
+    auto uBlockCount = ShaderIntrospector::getActiveBlockCount(id);
+    uniformBlocks.reserve(uBlockCount);
+    for (GLint i = 0; i < uBlockCount; i++)
+    {
+        auto name = ShaderIntrospector::getUnifromBlockName(id, i);
+        GLuint uBlockIndex = ShaderIntrospector::getUniformBlockIndex(id, name);
+        LOG_INFO("Uniform Block #%i, indexed as #%u, Name: %s", i, uBlockIndex, name.c_str());
+        uniformBlocks.insert({ name, i });
+        const auto& indices = ShaderIntrospector::getUniformBlockUniformIndices(id, uBlockIndex);
+        for (const auto index : indices)
+        {
+            char* uniformName = (char*)alloca(nameMaxLength * sizeof(char));
+			GLCall(glGetActiveUniform(id, index, nameMaxLength, &length, &size, &type, uniformName));
+			GLsizei s = GLTypeSize(type);
+			LOG_INFO("#%u has: %s, with index: %u, and of type: %u, with the size: %i\n", uBlockIndex, uniformName, index, type, s);
+        }
+    }
+    
 
     GLCall(glValidateProgram(id));
 }
@@ -98,7 +131,7 @@ ShaderProgram::operator GLuint() const
 
 void ShaderProgram::setMaterial(const Material& mat) const
 {
-    LOG_DEBUG("shaderid: %u", id);
+ //   LOG_DEBUG("Setting material on shader: %u", id);
     bind();
     std::size_t i = 0;
     for (const auto unimap : mat.m_unimaps)
@@ -131,7 +164,6 @@ void ShaderProgram::unbind() const
 {
     GLCall(glUseProgram(0));
 }
-
 GLint ShaderProgram::getUniformLocation(const std::string& name) const
 {
 	const auto locationIter = uniforms.find(name);
@@ -142,6 +174,17 @@ GLint ShaderProgram::getUniformLocation(const std::string& name) const
     return (*locationIter).second;
 }
 
+GLuint ShaderProgram::getUniformBlockIndex(const std::string & blockName) const
+{
+    GLuint index;
+    GLCall(index = glGetUniformBlockIndex(id, blockName.c_str()));
+    if (index == GL_INVALID_INDEX)
+    {
+        LOG_WARN("\nTrying to access \"%s\",\n No uniform block with that name exists!", blockName.c_str());
+    }
+    return index;
+}
+
 GLuint ShaderProgram::CompileShader(GLuint type, const std::string& source)
 {
     GLuint id;
@@ -199,9 +242,9 @@ ShaderSource ShaderProgram::ParseProgram(const std::string& file)
             {
                 currentlyReading = GEOM;
             }
-            getline(fileStream, line);
+            getline(fileStream, line); //instantly get #version tag
             ss[(int)currentlyReading] << line << '\n';
-            ss[(int)currentlyReading] << "#line " << ++lineNr << '\n'; //inject line number to get the line as is in the shader file
+            ss[(int)currentlyReading] << "#line " << ++lineNr+1 << '\n'; //inject line number to get the line as is in the shader file
         }
         else
         {
diff --git a/src/ShaderSystem.cpp b/src/ShaderSystem.cpp
index b37d5f9d5092d46ec11c64c14755f551c79b7166..e7f58cce5ae37a6de53c5d80de5a736f3b5a49f5 100644
--- a/src/ShaderSystem.cpp
+++ b/src/ShaderSystem.cpp
@@ -2,11 +2,12 @@
 
 namespace overkill 
 {
-
-std::vector<ShaderProgram> ShaderSystem::m_shaderPrograms;
-std::unordered_map<C::Tag, C::ID> ShaderSystem::m_mapShaderProgramID;
-std::vector<ShaderSystem::UpdateCallback> ShaderSystem::m_updateCallbacks;
-
+std::unordered_map<C::Tag, std::set<GLuint>> ShaderSystem::m_mapUniformBufferTargets;
+std::vector<UniformBuffer>                   ShaderSystem::m_uniformBuffers;
+std::unordered_map<C::Tag, C::ID>            ShaderSystem::m_mapUniformBuffersID;
+std::vector<ShaderProgram>                   ShaderSystem::m_shaderPrograms;
+std::unordered_map<C::Tag, C::ID>            ShaderSystem::m_mapShaderProgramID;
+std::vector<ShaderSystem::UpdateCallback>    ShaderSystem::m_updateCallbacks;
 
 auto ShaderSystem::getIdByTag(const C::Tag& tag) -> C::ID
 {
@@ -39,6 +40,82 @@ void ShaderSystem::push(const C::Tag tag, const std::string& filepath)
     ShaderSystem::m_shaderPrograms.emplace_back( ShaderProgram(filepath.data()) );
 }
 
+void ShaderSystem::pushUniformBuffer(const C::Tag&& tag, GLuint size)
+{
+    //ShaderSystem::m_mapUniformBufferDynamic[tag] = ShaderSystem::m_uniformBuffers.size();
+    //ShaderSystem::m_uniformBuffers.emplace_back( UniformBuffer(tag.c_str(), nullptr, size, GL_DYNAMIC_DRAW) );
+}
+
+void ShaderSystem::linkUniformBlocks()
+{
+
+
+    std::unordered_map<C::Tag, GLint> uniformBlocks;
+
+    for (auto& shader : m_shaderPrograms)
+    {
+        auto id = GLuint(shader);
+        auto uBlockCount = ShaderIntrospector::getActiveBlockCount(id);
+        uniformBlocks.reserve(uBlockCount);
+        for (GLint i = 0; i < uBlockCount; i++)
+        {
+            auto name = ShaderIntrospector::getUnifromBlockName(id, i);
+            GLuint uBlockIndex = ShaderIntrospector::getUniformBlockIndex(id, name);
+            LOG_INFO("Uniform Block #%i, indexed as #%u, Name: %s", i, uBlockIndex, name.c_str());
+			auto search = m_mapUniformBufferTargets.find(name);
+			if (search != m_mapUniformBufferTargets.end())
+			{
+				search->second.insert(GLuint(shader));
+			}
+			else
+			{
+				m_mapUniformBufferTargets.insert({ name,{ GLuint(shader) } });
+			}
+            uniformBlocks.insert({ name, i });
+            /*ShaderSystem::m_mapUniformBufferTargets.
+            const auto& indices = ShaderIntrospector::getUniformBlockUniformIndices(id, uBlockIndex);
+            for (const auto index : indices)
+            {
+                char* uniformName = (char*)alloca(nameMaxLength * sizeof(char));
+                GLsizei length;
+                GLCall(glGetActiveUniformName(id, index, nameMaxLength, &length, uniformName));
+                LOG_INFO("#%u has: %s", uBlockIndex, uniformName);
+            }*/
+        }
+    }
+}
+
+/// <summary>
+///
+///
+///
+///
+GLuint ShaderSystem::getBlockUniformLocation(const C::Tag& uBlock, const C::Tag& uniform )
+{
+	/*for (auto& uBuffer : m_uniformBuffers)
+	{
+		uBuffer.holdsBlock(uBlock)
+	}*/
+	return 0;
+}
+
+auto ShaderSystem::getUniformBufferIdByTag(const C::Tag& tag) -> C::ID
+{
+    return ShaderSystem::m_mapUniformBuffersID[tag];
+}
+
+auto ShaderSystem::getUniformBufferByTag(const C::Tag& tag) -> const UniformBuffer&
+{
+    return ShaderSystem::m_uniformBuffers[m_mapUniformBuffersID[tag]];
+}
+
+auto ShaderSystem::getUniformBufferById(C::ID uBufferID) -> const UniformBuffer&
+{
+    return ShaderSystem::m_uniformBuffers[uBufferID];
+}
+
+
+
 void ShaderSystem::load() 
 {
     std::vector<FileEvent> fevents = Watcher::popEvents("discovered", "shaders");
@@ -48,6 +125,86 @@ void ShaderSystem::load()
         LOG_DEBUG("Shaders from file: %s", filepath.data());
         ShaderSystem::push(e.tag, filepath);
     }
+    
+    //pushUniformBuffer("OK_Lights", sizeof(LightData) * 8);
+	auto matBufferLayout = BlockLayout();
+	matBufferLayout.push("projection", 64);
+	matBufferLayout.push("view", 64);
+	matBufferLayout.push("view_position", 16);	
+	
+	//pushUniformBuffer("OK_Matrices",    sizeof(glm::mat4) * 2);
+	ShaderSystem::m_mapUniformBuffersID["OK_Matrices"] = ShaderSystem::m_uniformBuffers.size(); //assign ID/index
+	auto buf = ShaderSystem::m_uniformBuffers.emplace_back(UniformBuffer("OK_Matrices", matBufferLayout, GL_DYNAMIC_DRAW));
+
+	auto lightBufferLayout = BlockLayout();
+    //lightBufferLayout.push<16>("light.position")
+    auto lightStructLayout = BlockLayout("light");
+    lightStructLayout.push("position", 16);
+    lightStructLayout.push("intensities", 16);
+
+    lightBufferLayout.pushBlock(lightStructLayout, 8);
+	//lightBufferLayout.push<GL_FLOAT_VEC4>("intensities");
+	//lightBufferLayout.push<GL_FLOAT>("spread");
+	//lightBufferLayout.push<GL_FLOAT>("constant");
+	//lightBufferLayout.push<GL_FLOAT>("linear");
+	//lightBufferLayout.push<GL_FLOAT>("quadratic");
+
+	ShaderSystem::m_mapUniformBuffersID["OK_Lights"] = ShaderSystem::m_uniformBuffers.size(); //assign ID/index
+	ShaderSystem::m_uniformBuffers.emplace_back(UniformBuffer("OK_Lights", lightBufferLayout, GL_DYNAMIC_DRAW));
+
+	//pushUniformBuffer("OK_Lights",   sizeof(glm::vec4) * 2+sizeof(float));
+	//auto buf = getUniformBufferByTag("OK_Matrices");
+
+	GLsizei nameMaxLength;
+	
+	linkUniformBlocksForAll();
+	GLuint bindPoint = 0;
+	for (auto& buffer : m_uniformBuffers)
+	{
+        buffer.bind();
+		for (const auto& shader : m_shaderPrograms)
+		{
+			/*if (buffer.blockCount() == 1)
+			{*/
+				auto uBlockIndex = ShaderIntrospector::getUniformBlockIndex(GLuint(shader), C::Tag(buffer));
+				if (uBlockIndex != GL_INVALID_INDEX) //this block did exist in the shader
+				{
+					GLCall(glUniformBlockBinding(GLuint(shader), uBlockIndex, bindPoint));
+					GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, bindPoint, GLuint(buffer)));
+				}
+			//}
+			//else
+			//{
+			//	//for (int blockInstance = 0; blockInstance < buffer.blockCount(); blockInstance++)
+			//	//{
+			//	//	const auto& blockName = C::Tag(buffer) + '[' + std::to_string(blockInstance) + ']';
+			//	//	auto uBlockIndex = ShaderIntrospector::getUniformBlockIndex(GLuint(shader), blockName);
+			//	//	if (uBlockIndex != GL_INVALID_INDEX) //this block did exist in the shader
+			//	//	{
+			//	//		GLCall(glUniformBlockBinding(GLuint(shader), uBlockIndex, bindPoint));
+
+
+   // //                    GLint offsetAlignment;
+   // //                    GLCall(glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment));
+   // //                    GLint bufferBindings;
+   // //                    GLCall(glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &bufferBindings));
+   // //                    LOG_INFO("bindingMAX: %i", bufferBindings);
+   // //                    
+
+   // //                    GLint bufSize;
+   // //                    glGetBufferParameteriv(GL_UNIFORM_BUFFER, GL_BUFFER_SIZE, &bufSize);
+   // //                    LOG_INFO("Block: #%i,\toffset: %u,\tsize: %u,\tBufferSize: %i,\toffset alignment: %i", blockInstance, a, buffer.blockSize(), bufSize, offsetAlignment);
+   // //                    //GLCall(glBindBufferRange(GL_UNIFORM_BUFFER, bindPoint, GLuint(buffer), a, buffer.blockSize()));
+			//	//		//GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, bindPoint, GLuint(buffer)));
+   // //                    
+			//	//	}
+			//	//}
+   //             GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, bindPoint, GLuint(buffer)));
+			//}
+		}
+        buffer.unbind();
+		bindPoint++;
+	}
 }
 
 void ShaderSystem::reload() 
@@ -58,9 +215,17 @@ void ShaderSystem::reload()
     {
         shaderprog.clean();         
     }
+	// Remove all shaders
+	ShaderSystem::m_shaderPrograms.clear();
+
+	// Delete from GPU
+	for (auto uBuffer : ShaderSystem::m_uniformBuffers)
+	{
+		uBuffer.clean();
+	}
+	// Remove all uniformBuffers
+	ShaderSystem::m_uniformBuffers.clear();
 
-    // Remove all shaders
-    ShaderSystem::m_shaderPrograms.clear();
 
     // Load from file again
     Watcher::discoverFiles();
@@ -72,6 +237,34 @@ void ShaderSystem::reload()
     }
 }
 
+void ShaderSystem::linkUniformBlocksForAll()
+{   
+    GLuint bindPoint = 0;
+    for (const auto& shader : m_shaderPrograms)
+    {
+        auto uBlockCount = ShaderIntrospector::getActiveBlockCount(GLuint(shader));
+        for (GLint i = 0; i < uBlockCount; i++)
+        {
+            auto name = ShaderIntrospector::getUnifromBlockName(GLuint(shader), i);
+            LOG_INFO("Uniform Block #%i, Name: %s", i, name.c_str());
+            auto search = m_mapUniformBufferTargets.find(name);
+            if (search != m_mapUniformBufferTargets.end())
+            {
+                search->second.insert(GLuint(shader));
+            }
+            else
+            {
+                m_mapUniformBufferTargets.insert({ name, {GLuint(shader)} });
+            }
+        }
+
+
+        //GLCall(glUniformBlockBinding(GLuint(shader), shader.getUniformBlockIndex(C::Tag(uBuffer)), bindPoint));
+        //GLCall(glBindBufferBase(GL_UNIFORM_BUFFER, bindPoint, GLuint(uBuffer)));
+    }
+    bindPoint++;
+}
+
 void ShaderSystem::bindOnUpdate(const C::Tag& shaderTag, C::ID modelID, C::ID meshID, OnUpdate onUpdate)
 {
     ShaderSystem::m_updateCallbacks.emplace_back(
diff --git a/src/TextureSystem.cpp b/src/TextureSystem.cpp
index efd31ed091071d5d119dede28faf4a7cd01cdf72..7d6b0ce13a6c8b7705db3b84d48ccd3fea787d35 100644
--- a/src/TextureSystem.cpp
+++ b/src/TextureSystem.cpp
@@ -1,6 +1,5 @@
 #include <overkill/TextureSystem.hpp>
 
-// #include <PMS/logger.h>
 namespace overkill 
 {
 
@@ -38,12 +37,12 @@ void TextureSystem::load()
 {   
     // TODO: Load these from the file system somehow
     std::vector<FileEvent> fevents = Watcher::popEvents("discovered", "textures");
-
     for (const auto e : fevents) {
 
         const auto filepath = C::TexturesFolder + ("/" + e.tag) + "." + e.extension;
         LOG_DEBUG("Texture from file: %s", filepath.data());
         TextureSystem::push(e.tag, filepath);
+
     }
 }
 
diff --git a/src/UniformBuffer.cpp b/src/UniformBuffer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a82cac870c5b5fc9ff5c85966f413ca11ea2559
--- /dev/null
+++ b/src/UniformBuffer.cpp
@@ -0,0 +1,60 @@
+#include <overkill/UniformBuffer.hpp>
+
+namespace overkill
+{
+    BlockLayout::BlockLayout(const char * name) : m_blockSize(0), m_name(name){}
+    //lightblock
+//mvpblock
+//cam pos 
+BlockLayout::operator C::Tag() const
+{
+    return m_name;
+}
+
+UniformBuffer::UniformBuffer(const char * name, const BlockLayout & layout, const GLenum drawMode)
+    : m_name(name)
+	, m_blockLayout(layout)
+{
+	GLCall(glGenBuffers(1, &m_id));
+	bind();
+	GLCall(glBufferData(GL_UNIFORM_BUFFER, m_blockLayout.size(), nullptr, drawMode));
+	unbind();
+}
+
+UniformBuffer::operator C::Tag() const
+{
+    return m_name;
+}
+
+UniformBuffer::operator GLuint() const
+{
+    return m_id;
+}
+
+void UniformBuffer::clean()
+{
+    GLCall(glDeleteBuffers(1, &m_id));
+}
+
+void UniformBuffer::bind() const
+{
+    GLCall(glBindBuffer(GL_UNIFORM_BUFFER, m_id));
+}
+
+void UniformBuffer::unbind() const
+{
+    GLCall(glBindBuffer(GL_UNIFORM_BUFFER, 0));
+}
+
+GLuint UniformBuffer::getUniformIndex(const C::Tag& name) const
+{
+	return m_blockLayout.indexOfUniform(name);
+}
+
+void UniformBuffer::update(const C::ID index, GLsizeiptr size, const void * data)
+{
+	bind();
+	glBufferSubData(GL_UNIFORM_BUFFER, index, size, data);
+}
+
+}
\ No newline at end of file
diff --git a/src/VertexArray.cpp b/src/VertexArray.cpp
index 63f0459fef644f92d2e45c8ed4a647d3a2f858fe..27e646c1f983f54ac2d5e6d6d3c31e67ee80fe0d 100644
--- a/src/VertexArray.cpp
+++ b/src/VertexArray.cpp
@@ -35,7 +35,10 @@ void VertexArray::addBuffer(const VertexBuffer& vb, const VertexBufferAttribLayo
                 (const void*)offset
             )
         );
-        offset += attrib.count * GLTypeSize(attrib.type);
+        if (attrib.type == GL_INT_2_10_10_10_REV)
+            offset += attrib.count;
+        else
+            offset += attrib.count * GLTypeSize(attrib.type);
         i++;
     }
 
diff --git a/src/gl_util.cpp b/src/gl_util.cpp
index cc359c2338a46b3bb3893ec890c34db7dbb44ef8..f92c54cebafb4f10f8dffe2f2c61c234824b2b9b 100644
--- a/src/gl_util.cpp
+++ b/src/gl_util.cpp
@@ -1,6 +1,6 @@
 #include <overkill/gl_util.hpp>
 
-void GLPrintMaxContants()
+void GLPrintMaxConstants()
 {
     // @doc list of available constants https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml 18.04.2018
     GLint out;
@@ -52,21 +52,123 @@ void GLPrintMaxContants()
 }
 
 
-GLuint GLTypeSize(const GLenum type)
+// @TODO replace with a "magical" template specifier i.e. template<>GLTypeSize<GL_FLOAT>(){return sizeof(GLfloat);}
+auto GLTypeSize(const GLenum type) -> GLsizei 
 {
     switch (type)
     {
-        case GL_BOOL:                           return sizeof(GLboolean);
+		case GL_FLOAT										: return sizeof(GLfloat);
+		case GL_FLOAT_VEC2									: return sizeof(GLfloat)*2;
+		case GL_FLOAT_VEC3									: return sizeof(GLfloat)*3;
+		case GL_FLOAT_VEC4									: return sizeof(GLfloat)*4;
+		case GL_DOUBLE										: return sizeof(GLdouble);
+		case GL_DOUBLE_VEC2									: return sizeof(GLdouble)*2;
+		case GL_DOUBLE_VEC3									: return sizeof(GLdouble)*3;
+		case GL_DOUBLE_VEC4									: return sizeof(GLdouble)*4;
+		case GL_INT											: return sizeof(GLint);
+		case GL_INT_VEC2									: return sizeof(GLint)*2;
+		case GL_INT_VEC3									: return sizeof(GLint)*3;
+		case GL_INT_VEC4									: return sizeof(GLint)*4;
+		case GL_UNSIGNED_INT								: return sizeof(GLuint);
+		case GL_UNSIGNED_INT_VEC2							: return sizeof(GLuint)*2;
+		case GL_UNSIGNED_INT_VEC3							: return sizeof(GLuint)*3;
+		case GL_UNSIGNED_INT_VEC4							: return sizeof(GLuint)*4;
+		case GL_BOOL										: return sizeof(GLboolean);
+		case GL_BOOL_VEC2									: return sizeof(GLboolean)*2;
+		case GL_BOOL_VEC3									: return sizeof(GLboolean)*3;
+		case GL_BOOL_VEC4									: return sizeof(GLboolean)*4;
+		case GL_FLOAT_MAT2									: return sizeof(GLfloat)*2*2;
+		case GL_FLOAT_MAT3									: return sizeof(GLfloat)*3*3;
+		case GL_FLOAT_MAT4									: return sizeof(GLfloat)*4*4;
+		case GL_FLOAT_MAT2x3								: return sizeof(GLfloat)*2*3;
+		case GL_FLOAT_MAT2x4								: return sizeof(GLfloat)*2*4;
+		case GL_FLOAT_MAT3x2								: return sizeof(GLfloat)*3*2;
+		case GL_FLOAT_MAT3x4								: return sizeof(GLfloat)*3*4;
+		case GL_FLOAT_MAT4x2								: return sizeof(GLfloat)*4*2;
+		case GL_FLOAT_MAT4x3								: return sizeof(GLfloat)*4*3;
+		case GL_DOUBLE_MAT2									: return sizeof(GLdouble)*2*2;
+		case GL_DOUBLE_MAT3									: return sizeof(GLdouble)*3*3;
+		case GL_DOUBLE_MAT4									: return sizeof(GLdouble)*4*4;
+		case GL_DOUBLE_MAT2x3								: return sizeof(GLdouble)*2*3;
+		case GL_DOUBLE_MAT2x4								: return sizeof(GLdouble)*2*4;
+		case GL_DOUBLE_MAT3x2								: return sizeof(GLdouble)*3*2;
+		case GL_DOUBLE_MAT3x4								: return sizeof(GLdouble)*3*4;
+		case GL_DOUBLE_MAT4x2								: return sizeof(GLdouble)*4*2;
+		case GL_DOUBLE_MAT4x3								: return sizeof(GLdouble)*4*3;
+		case GL_SAMPLER_1D									: 
+		case GL_SAMPLER_2D									: 
+		case GL_SAMPLER_3D									: 
+		case GL_SAMPLER_CUBE								: 
+		case GL_SAMPLER_1D_SHADOW							: 
+		case GL_SAMPLER_2D_SHADOW							: 
+		case GL_SAMPLER_1D_ARRAY							: 
+		case GL_SAMPLER_2D_ARRAY							: 
+		case GL_SAMPLER_1D_ARRAY_SHADOW						: 
+		case GL_SAMPLER_2D_ARRAY_SHADOW						: 
+		case GL_SAMPLER_2D_MULTISAMPLE						: 
+		case GL_SAMPLER_2D_MULTISAMPLE_ARRAY				: 
+		case GL_SAMPLER_CUBE_SHADOW							: 
+		case GL_SAMPLER_BUFFER								: 
+		case GL_SAMPLER_2D_RECT								: 
+		case GL_SAMPLER_2D_RECT_SHADOW						: 
+		case GL_INT_SAMPLER_1D								: 
+		case GL_INT_SAMPLER_2D								: 
+		case GL_INT_SAMPLER_3D								: 
+		case GL_INT_SAMPLER_CUBE							: 
+		case GL_INT_SAMPLER_1D_ARRAY						: 
+		case GL_INT_SAMPLER_2D_ARRAY						: 
+		case GL_INT_SAMPLER_2D_MULTISAMPLE					: 
+		case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY			: 
+		case GL_INT_SAMPLER_BUFFER							: 
+		case GL_INT_SAMPLER_2D_RECT							: 
+		case GL_UNSIGNED_INT_SAMPLER_1D						: 
+		case GL_UNSIGNED_INT_SAMPLER_2D						: 
+		case GL_UNSIGNED_INT_SAMPLER_3D						: 
+		case GL_UNSIGNED_INT_SAMPLER_CUBE					: 
+		case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY				: 
+		case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY				: 
+		case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE			: 
+		case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY	: 
+		case GL_UNSIGNED_INT_SAMPLER_BUFFER					: 
+		case GL_UNSIGNED_INT_SAMPLER_2D_RECT				: 
+		case GL_IMAGE_1D									: 
+		case GL_IMAGE_2D									: 
+		case GL_IMAGE_3D									: 
+		case GL_IMAGE_2D_RECT								: 
+		case GL_IMAGE_CUBE									: 
+		case GL_IMAGE_BUFFER								: 
+		case GL_IMAGE_1D_ARRAY								: 
+		case GL_IMAGE_2D_ARRAY								: 
+		case GL_IMAGE_2D_MULTISAMPLE						: 
+		case GL_IMAGE_2D_MULTISAMPLE_ARRAY					: 
+		case GL_INT_IMAGE_1D								: 
+		case GL_INT_IMAGE_2D								: 
+		case GL_INT_IMAGE_3D								: 
+		case GL_INT_IMAGE_2D_RECT							: 
+		case GL_INT_IMAGE_CUBE								: 
+		case GL_INT_IMAGE_BUFFER							: 
+		case GL_INT_IMAGE_1D_ARRAY							: 
+		case GL_INT_IMAGE_2D_ARRAY							: 
+		case GL_INT_IMAGE_2D_MULTISAMPLE					: 
+		case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY				: 
+		case GL_UNSIGNED_INT_IMAGE_1D						: 
+		case GL_UNSIGNED_INT_IMAGE_2D						: 
+		case GL_UNSIGNED_INT_IMAGE_3D						: 
+		case GL_UNSIGNED_INT_IMAGE_2D_RECT					: 
+		case GL_UNSIGNED_INT_IMAGE_CUBE						: 
+		case GL_UNSIGNED_INT_IMAGE_BUFFER					: 
+		case GL_UNSIGNED_INT_IMAGE_1D_ARRAY					: 
+		case GL_UNSIGNED_INT_IMAGE_2D_ARRAY					: 
+		case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE			: 
+		case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY		: 
+			return sizeof(GLint);
+		case GL_UNSIGNED_INT_ATOMIC_COUNTER:    return sizeof(GLuint);
         case GL_BYTE:                           return sizeof(GLbyte);
         case GL_UNSIGNED_BYTE:                  return sizeof(GLubyte);
         case GL_SHORT:                          return sizeof(GLshort);
         case GL_UNSIGNED_SHORT:                 return sizeof(GLushort);
-        case GL_INT:                            return sizeof(GLint);
         case GL_INT_2_10_10_10_REV:             return sizeof(GLint);
-        case GL_UNSIGNED_INT:                   return sizeof(GLuint);
         case GL_UNSIGNED_INT_2_10_10_10_REV:    return sizeof(GLuint);
-        case GL_FLOAT:                          return sizeof(GLfloat);
-        case GL_DOUBLE:                         return sizeof(GLdouble);
         default:
             printf("WARNING!\nGLenum type: %u not supported!!!", type);
             return 0;
@@ -84,4 +186,4 @@ std::string ShaderTypeName(const GLenum typeID)
             printf("WARNING!\nGLenum type: %u not supported!!!", typeID);
             return "NONE";
     }
-}
+}
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 248b418adc443fc796f1936e0ed5561edbdaf221..4ca8b511171b5f2e194d36961d7385d38168b479 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -54,8 +54,7 @@ int main()
     MaterialSystem::load();
     ModelSystem::load();
 
-    auto renderer = EdgeRenderer();
-    
+    auto renderer = Renderer();
     //translation * view * rotate(time*0.1*F, time*0.333334*F, time*0.1666666667*F) //From shader, old system.
     auto modelCubeObject = EntityModel("cube");
     auto modelFloorObject = EntityModel("cube");
@@ -67,73 +66,121 @@ int main()
     modelFloorObject.setPosition(glm::vec3(0, -3, 0));
     modelFloorObject.setScale(glm::vec3(20, 0.5f, 20));
 
+    Transform modelTransform;
+    modelTransform.m_position = glm::vec3{ 0,0,0 };
+
+    Light light;
+
+    struct LightData {
+        glm::vec4 position;		//16->16
+        glm::vec4 intensities;	//16->32
+        float spread;			//4 ->36
+		float constant;			//4	->40
+		float linear;			//4 ->44
+		float quadratic;		//4 ->48
+
+    } lightData[8] = {
+        LightData{ { 0, 4, 0, 0	},{ 1.5f, 0, 0	 , 0}, /*0.0f, 0.0f, 0.0f, 0.0f    */ },        
+		LightData{ { 1.5f, 0, 0, 0 },{ 0, 0, 1.5f	 , 0 },/* 0.0f, 0.0f, 0.0f, 0.0f   */     },
+        LightData{ { 3, 1, 0, 0	},{ 0, 1.5f, 0		 , 0}, /*10.0f, 10.0f, 10.0f, 10.0f*/ },
+        LightData{ { 0, -2, 0,0	},{ 1, 0, 0		 , 0}, /*10.0f, 10.0f, 10.0f, 10.0f*/ },
+        LightData{ { 0, 1, -4,0	},{ 1, 0, 1		 , 0}, /*10.0f, 10.0f, 10.0f, 10.0f*/ },
+        LightData{ { 0, -5, 0,0	},{ 1, 1, 0		 , 0}, /*10.0f, 10.0f, 10.0f, 10.0f*/ },
+        LightData{ { 0, 5, 0,0	},{ 1, 1, 1		 , 0}, /*10.0f, 10.0f, 10.0f, 10.0f*/ },
+        LightData{ { 0,3, 3	,0	},{ 0.5, 0.5, 0.5, 0}, /*10.0f, 10.0f, 10.0f, 10.0f*/ },
+    };
+
 
-    //SCALE -> ROTATE -> TRANSLATE
     glm::mat4 projection = glm::perspective(C::FOV, C::AspectRatio, C::NearClip, C::FarClip);
-    glm::mat4 camera = glm::mat4(1); 
-    glm::mat4 pivot = glm::translate(glm::mat4(1),glm::vec3(0, 0, C::CameraOffset));  //Camera pos in world.
+    glm::mat4 camera = glm::mat4(1);
+    glm::mat4 pivot = glm::translate(glm::mat4(1), glm::vec3(0, 0, C::CameraOffset));  //Camera pos in world.
     glm::mat4 view = glm::mat4(1);
+    glm::mat4 m2w = modelTransform.modelToWorld();
+
+
+    float oldT = 0, t = 0, dt = 0;
 
-    //GLCall(glSetUn)
-    GLint uniformMVP, uniformTime;
-    GLint uniformMVP2, uniformTime2;
-    GLint uniformView;                                  //Will communicate camera orientation to shader.
+	auto matrixBuf = ShaderSystem::getUniformBufferByTag("OK_Matrices");
+	auto lightBuf = ShaderSystem::getUniformBufferByTag("OK_Lights");
 
-    //TODO move to shader system:
-    for (auto mesh : ModelSystem::getById(modelCubeObject.getModel()).m_meshes)
+    // Get Uniform buffer indicies
+    auto projectionIndex = matrixBuf.getUniformIndex("projection");
+    auto viewIndex       = matrixBuf.getUniformIndex("view");
+    auto viewPosIndex    = matrixBuf.getUniformIndex("view_position");
+
+    // @TODO make this getUniformIndex("light", 0)
+    // @note this could also be used as offset for all the other lights
+    auto light0PosIndex  = lightBuf.getUniformIndex("light[0].position"); 
+    auto light1PosIndex  = lightBuf.getUniformIndex("light[1].position"); 
+    auto light2PosIndex  = lightBuf.getUniformIndex("light[2].position"); 
+
+
+    for (auto mesh: ModelSystem::getById( modelCubeObject.getModel() ).m_meshes) 
+    {   
+        auto& shader = mesh.m_shaderProgram;
+        shader.setMaterial(MaterialSystem::getById(mesh.m_materialID));
+    }
+
+    for (auto mesh: ModelSystem::getById( modelFloorObject.getModel() ).m_meshes) 
     {
-        mesh.m_shaderProgram.bind();
-        uniformMVP = mesh.m_shaderProgram.getUniformLocation("projection");
-        uniformTime = mesh.m_shaderProgram.getUniformLocation("time");
-        uniformView = mesh.m_shaderProgram.getUniformLocation("view");
-        GLCall(glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, glm::value_ptr(projection)));
+        auto& shader = mesh.m_shaderProgram;
+        shader.setMaterial(MaterialSystem::getById(mesh.m_materialID));
     }
 
-    float oldT = 0, t = 0, dt = 0;
     for(;;)
     {
-        t = glfwGetTime();
+        t = (float)glfwGetTime();
         dt = t - oldT;
         if ((glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS || glfwWindowShouldClose(window) != 0))
             break;
 
-        // auto model = ModelSystem::getByTag("cube");          // This is a hack, its beting loaded every frame in case it was
-        Renderer::clear();                                      // changed by keypress.
-        // Renderer::draw(model, glm::mat4(1));                 // Replaced by new model, however some of old models components are still being used.
+        Renderer::clear();
         
         modelCubeObject.update(dt);
         modelFloorObject.update(dt);
 
-        // Set uniforms global shaders
-        for (auto mesh : ModelSystem::getById(modelCubeObject.getModel()).m_meshes)
-        {
-            mesh.m_shaderProgram.bind();
-            GLCall(glUniformMatrix4fv(uniformMVP, 1, GL_FALSE, glm::value_ptr(projection)));
-            GLCall(glUniformMatrix4fv(uniformView, 1, GL_FALSE, glm::value_ptr(view)));
-            GLCall(glUniform1f(uniformTime, 0));
-        }
-
-        modelCubeObject.draw();    //Important: currently uses the old model's 0th mesh's shader to draw. Also true for the camera.
-        modelFloorObject.draw();    //Important: currently uses the old model's 0th mesh's shader to draw. Also true for the camera.
-
-		//@TODO shader.bindDynamic()
+        // UPDATE CAMERA DATA
         projection = glm::perspective(Input::m_fovy, C::AspectRatio, 0.1f, -100.0f);
         camera = glm::rotate(glm::mat4(1), (Input::m_camRotX), glm::vec3(0.0f, 1.0f, 0.0f));
         camera = glm::rotate(glm::mat4(1), (Input::m_camRotY), glm::vec3(1.0f, 0.0f, 0.0f)) * camera;
-        pivot = glm::translate(glm::mat4(1),glm::vec3(Input::m_camPanX, Input::m_camPanY, C::CameraOffset));  //Camera pos in world.
-        
+        pivot = glm::translate(glm::mat4(1), glm::vec3(Input::m_camPanX, Input::m_camPanY, C::CameraOffset));  
         view = pivot * camera;
         glm::inverse(view); //To reverse both axis, so controls are not reverse.
 
 
 
+        // UPDATE LIGHT DATA
+        lightData[0].position = glm::vec4(10 * sin(3 * t), 0, 10 * cos(3 * t), 0);
+        lightData[0].intensities = glm::vec4(1.5f * sin(1.33*t), 1.5 * cos(1.33*t), 0, 0);
+
+        lightData[1].position = glm::vec4(10 * sin(1.33*t), 10 * cos(1.33*t), 0, 0);
+        lightData[1].intensities = glm::vec4(1.5f * sin(1.33*t + C::PI / 3), 1.5 * cos(1.33*t + C::PI / 3), 0, 0);
+
+        lightData[2].position = glm::vec4(0, 10 * sin(4.377*t), 10 * cos(4.377*t), 0);
+        lightData[2].intensities = glm::vec4(1.5f * sin(1.33*t + (C::PI * 2 / 3)), 1.5 * cos(1.33*t + (C::PI * 2 / 3)), 0, 0);
+
+
+        // UPDATE GLOBAL UNIFORM BUFFERS
+        lightBuf.update(light0PosIndex, 16, &(lightData[0].position));
+        lightBuf.update(light0PosIndex + 16, 16, &(lightData[0].intensities));
+        
+        lightBuf.update(light1PosIndex, 16, &(lightData[1].position));
+        lightBuf.update(light1PosIndex + 16, 16, &(lightData[1].intensities));
+        
+        lightBuf.update(light2PosIndex, 16, &(lightData[2].position));
+        lightBuf.update(light2PosIndex + 16, 16, &(lightData[2].intensities));
+        
+        matrixBuf.update(projectionIndex, sizeof(glm::mat4), glm::value_ptr(projection));
+        matrixBuf.update(viewIndex, sizeof(glm::mat4), glm::value_ptr(view));
+        matrixBuf.update(viewPosIndex, sizeof(glm::vec4), glm::value_ptr(pivot));
+
+        // DRAW ENTITIES
+        Renderer::draw(modelCubeObject, t);
+        Renderer::draw(modelFloorObject, t);
 
         glfwSwapBuffers(window);
         glfwPollEvents();
 
-        // LIVE UPDATE SHADER AND MATERIALS
-        // Util::everyTwoSeconds(t);
-
         oldT = t;
     }