diff --git a/Client/Client.py b/Client/Client.py
new file mode 100644
index 0000000..dfe456e
--- /dev/null
+++ b/Client/Client.py
@@ -0,0 +1,386 @@
+from OpenGL.GL import *
+import numpy as np
+from OpenGL.GL.ARB.vertex_array_object import glDeleteVertexArrays
+from OpenGL.GL.framebufferobjects import glBindRenderbuffer
+from OpenGL.GLUT import *
+import OpenGL.GLUT.freeglut
+from OpenGL.GLU import *
+from OpenGL.GL import *
+from ctypes import sizeof, c_float, c_void_p, c_uint
+
+from Lights.Spotlight.Spotlight import Spotlight
+from WorldProvider.WorldProvider import WorldProvider
+from MatrixStuff.Transformations import perspectiveMatrix, lookAt, translate, rotate
+from Objects.Cube.Cube import Cube
+from Objects.Cuboid.Cuboid import Cuboid
+from Objects.World import World
+import json
+
+import random
+import time
+from scipy.signal import convolve
+
+MAX_DISTANCE = 200.0
+FRICTION_COEFFICENT = 0.9
+EPSILON = 0.00001
+
+def value_to_color(v, min_value, max_value):
+    r = g = b = 0.0
+    scope = max_value - min_value
+    normalized = (v - min_value) / (max_value - min_value)
+    if 0.5 * scope + min_value != 0:
+        b = max(0, 1.0 - abs(2.0 * normalized))
+        g = max(0, 1.0 - abs(2.0 * normalized - 1.0))
+        r = max(0, 1.0 - abs(2.0 * normalized - 2.0))
+        l = np.sqrt((r*r + b*b + g*g))
+        r /= l
+        g /= l
+        b /= l
+    return r, g, b
+
+class Client:
+    def __init__(self, test=False, pos=[0, 0, 0]):
+        with open('./config.json', 'r') as f:
+            self.config = json.load(f)
+        glutInit(sys.argv)
+        self.width = 1920
+        self.height = 1080
+        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
+        glutInitWindowSize(self.width, self.height)
+        glutCreateWindow(b'Voxelengine')
+
+        with open('passthroughvertex.glsl', 'r') as f:
+            vertex_shader_string = f.read()
+        self.passthrough_vertex_shader_id = glCreateShader(GL_VERTEX_SHADER)
+        glShaderSource(self.passthrough_vertex_shader_id, vertex_shader_string)
+        glCompileShader(self.passthrough_vertex_shader_id)
+        if glGetShaderiv(self.passthrough_vertex_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
+            raise RuntimeError(glGetShaderInfoLog(self.passthrough_vertex_shader_id))
+
+        with open('vertex.glsl', 'r') as f:
+            vertex_shader_string = f.read()
+        self.vertex_shader_id = glCreateShader(GL_VERTEX_SHADER)
+        glShaderSource(self.vertex_shader_id, vertex_shader_string)
+        glCompileShader(self.vertex_shader_id)
+        if glGetShaderiv(self.vertex_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
+            raise RuntimeError(glGetShaderInfoLog(self.vertex_shader_id))
+
+        with open('fragment.glsl', 'r') as f:
+            fragment_shader_string = f.read()
+        self.fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
+        glShaderSource(self.fragment_shader_id, fragment_shader_string)
+        glCompileShader(self.fragment_shader_id)
+        if glGetShaderiv(self.fragment_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
+            raise RuntimeError(glGetShaderInfoLog(self.fragment_shader_id))
+
+        Cube.initializeShader()
+        Cuboid.initializeShader()
+        self.geometry_shaders = {
+            Cube: Cube.GeometryShaderId,
+            Cuboid: Cuboid.GeometryShaderId
+        }
+
+        self.normal_program = {}
+        self.depth_program = {}
+
+        for key in self.geometry_shaders.keys():
+            self.normal_program[key] = glCreateProgram()
+            glAttachShader(self.normal_program[key], self.vertex_shader_id)
+            glAttachShader(self.normal_program[key], key.GeometryShaderId)
+            glAttachShader(self.normal_program[key], self.fragment_shader_id)
+            glLinkProgram(self.normal_program[key])
+
+            self.depth_program[self.normal_program[key]] = Spotlight.getDepthProgram(self.vertex_shader_id, key.GeometryShaderId)
+
+        self.world_provider = WorldProvider(self.normal_program)
+        for x_pos in range(0, 100):
+            for y_pos in range(0, 100):
+                for z_pos in range(0, 1):
+                    self.world_provider.world.put_object(x_pos, y_pos, z_pos, Cuboid().setColor(
+                        random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0))
+
+        self.projMatrix = perspectiveMatrix(45.0, 400 / 400, 0.01, MAX_DISTANCE)
+
+        self.rx = self.cx = self.cy = 0
+        self.opening = 45
+
+        glutReshapeFunc(self.resize)
+        glutDisplayFunc(self.display)
+        glutKeyboardFunc(self.keyboardHandler)
+        glutSpecialFunc(self.funcKeydHandler)
+
+        self.pos = pos
+
+        self.time = time.time()
+
+        self.heat_map = np.zeros((100, 100, 1))
+
+        self.v_map_x = np.zeros((100, 100, 1))
+        self.v_map_y = np.zeros((100, 100, 1))
+        self.v_map_z = np.zeros((100, 100, 1))
+
+        if not test:
+            glutMainLoop()
+        else:
+            self.display()
+            self.resize(100, 100)
+
+
+
+    def display(self):
+        glClearColor(0, 0, 0, 0)
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+        projMatrix = perspectiveMatrix(45, float(self.width) / float(self.height), 0.01, MAX_DISTANCE)
+
+        world: World = self.world_provider.world
+        lights = world.get_lights_to_render(self.pos, self.config['render_light_distance'])
+        for light in lights:
+            light.prepareForDepthMapping()
+            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+            light_mat = translate(light.pos[0], light.pos[1], light.pos[2]) * \
+                        lookAt(0, 0, 0, -light.pos[0], -light.pos[1], -light.pos[2], 0, 1, 0) * \
+                        perspectiveMatrix(90, float(light.map_size) / float(light.map_size), 0.01, MAX_DISTANCE)
+
+            for obj_type, program_id in self.depth_program.items():
+                glUseProgram(program_id)
+                widthid = glGetUniformLocation(program_id, 'width')
+                heightid = glGetUniformLocation(program_id, 'height')
+                nearid = glGetUniformLocation(program_id, 'near')
+                farid = glGetUniformLocation(program_id, 'far')
+                glUniform1f(nearid, 0.01)
+                glUniform1f(farid, 100)
+                glUniform1f(widthid, light.map_size)
+                glUniform1f(heightid, light.map_size)
+
+            world.render(light_mat, rotate(0, 0, 0), self.depth_program)
+            glFlush()
+            light.finishDepthMapping()
+            glClearColor(0, 0, 0, 0)
+            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+        glClearColor(0, 0, 0, 0)
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+        for obj_type, program_id in self.normal_program.items():
+            glUseProgram(program_id)
+            widthid = glGetUniformLocation(program_id, 'width')
+            heightid = glGetUniformLocation(program_id, 'height')
+            nearid = glGetUniformLocation(program_id, 'near')
+            farid = glGetUniformLocation(program_id, 'far')
+            glUniform1f(nearid, 0.01)
+            glUniform1f(farid, 100)
+            glUniform1f(widthid, self.width)
+            glUniform1f(heightid, self.height)
+
+        world.render(translate(self.pos[0], self.pos[1], self.pos[2]) * lookAt(0, 0, 0, 0, 0, -self.pos[2], 0, 1, 0) * projMatrix, rotate(0, 0, 0))
+        glFlush()
+
+        glutSwapBuffers()
+
+        max_value = np.max(self.heat_map)
+        # min_value = np.min(self.heat_map)
+        min_value = 0
+
+        vel = np.sqrt(np.square(self.v_map_x) + np.square(self.v_map_y) + np.square(self.v_map_z))
+        max_value = np.max(vel)
+        min_value = np.min(vel)
+
+        for x_pos in range(0, 100):
+            for y_pos in range(0, 100):
+                for z_pos in range(0, 1):
+                    # r, g, b = value_to_color(self.heat_map[x_pos, y_pos, z_pos], min_value, max_value)
+                    r, g, b = value_to_color(vel[x_pos, y_pos, z_pos], min_value, max_value)
+
+                    self.world_provider.world.set_color(x_pos, y_pos, z_pos, r, g, b)
+        # friction
+        # self.heat_map += np.sqrt(np.square(self.v_map_x * (1.0 - FRICTION_COEFFICENT)) +
+        #                          np.square(self.v_map_y * (1.0 - FRICTION_COEFFICENT)) +
+        #                          np.square(self.v_map_z * (1.0 - FRICTION_COEFFICENT)))
+        self.v_map_x *= FRICTION_COEFFICENT
+        self.v_map_y *= FRICTION_COEFFICENT
+        self.v_map_z *= FRICTION_COEFFICENT
+        # hot stuff rises / cool stuff sinks
+        rise = self.heat_map[:, :-1, :] > (self.heat_map[:, 1:, :] + EPSILON)
+        self.v_map_y[:, :-1, :] += 1.0 * rise
+        sink = self.heat_map[:, :-1, :] < (self.heat_map[:, 1:, :] - EPSILON)
+        self.v_map_y[:, 1:, :] -= 1.0 * sink
+        #flow
+        new_v_map_x = np.zeros(self.v_map_x.shape)
+        new_v_map_y = np.zeros(self.v_map_x.shape)
+        new_v_map_z = np.zeros(self.v_map_x.shape)
+        for x_pos in range(self.v_map_x.shape[0]):
+            for y_pos in range(self.v_map_x.shape[1]):
+                for z_pos in range(self.v_map_x.shape[2]):
+                    target_x = min(self.v_map_x.shape[0] - 1,
+                                   max(0, int(round(x_pos + self.v_map_x[x_pos, y_pos, z_pos]))))
+                    target_y = min(self.v_map_x.shape[1] - 1,
+                                   max(0, int(round(y_pos + self.v_map_y[x_pos, y_pos, z_pos]))))
+                    target_z = min(self.v_map_x.shape[2] - 1,
+                                   max(0, int(round(z_pos + self.v_map_z[x_pos, y_pos, z_pos]))))
+
+                    friction_dispersion = (1.0 -FRICTION_COEFFICENT) / 4
+                    # velocity dispersion
+                    # x
+                    new_v_map_x[target_x, target_y, target_z] += self.v_map_x[x_pos, y_pos, z_pos] * FRICTION_COEFFICENT
+                    if target_y + 1 < self.v_map_x.shape[1] - 1:
+                        new_v_map_y[target_x, target_y + 1, target_z] += self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_x[target_x, target_y, target_z] += self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    if target_y - 1 > 0:
+                        new_v_map_y[target_x, target_y - 1, target_z] -= self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_x[target_x, target_y, target_z] += self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+
+                    if target_z + 1 < self.v_map_x.shape[2] - 1:
+                        new_v_map_z[target_x, target_y, target_z + 1] += self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_x[target_x, target_y, target_z] += self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    if target_z - 1 > 0:
+                        new_v_map_z[target_x, target_y, target_z - 1] -= self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_x[target_x, target_y, target_z] += self.v_map_x[x_pos, y_pos, z_pos] * friction_dispersion
+                    # y
+                    new_v_map_y[target_x, target_y, target_z] += self.v_map_y[x_pos, y_pos, z_pos] * FRICTION_COEFFICENT
+                    if target_x + 1 < self.v_map_x.shape[0] - 1:
+                        new_v_map_x[target_x + 1, target_y, target_z] += self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_y[target_x, target_y, target_z] += self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    if target_x - 1 > 0:
+                        new_v_map_x[target_x - 1, target_y, target_z] -= self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_y[target_x, target_y, target_z] += self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+
+                    if target_z + 1 < self.v_map_x.shape[2] - 1:
+                        new_v_map_z[target_x, target_y, target_z + 1] += self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_y[target_x, target_y, target_z] += self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    if target_z - 1 > 0:
+                        new_v_map_z[target_x, target_y, target_z - 1] -= self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_y[target_x, target_y, target_z] += self.v_map_y[x_pos, y_pos, z_pos] * friction_dispersion
+                    # z
+                    new_v_map_z[target_x, target_y, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * FRICTION_COEFFICENT
+                    if target_x + 1 < self.v_map_x.shape[0] - 1:
+                        new_v_map_x[target_x + 1, target_y, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_z[target_x, target_y, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+                    if target_x - 1 > 0:
+                        new_v_map_x[target_x - 1, target_y, target_z] -= self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_z[target_x, target_y, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+
+                    if target_y + 1 < self.v_map_x.shape[1] - 1:
+                        new_v_map_y[target_x, target_y + 1, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_z[target_x, target_y, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+                    if target_y - 1 > 0:
+                        new_v_map_y[target_x, target_y - 1, target_z] -= self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+                    else:
+                        new_v_map_z[target_x, target_y, target_z] += self.v_map_z[x_pos, y_pos, z_pos] * friction_dispersion
+        # handle boundaries
+        filter_mat = np.array([[-1.0], [0], [1.0]]) / 2.0
+        new_v_map_y[0, :, :] += convolve(new_v_map_x[0, :, :], filter_mat, 'same')
+        new_v_map_x[0, :, :] = 0
+        new_v_map_y[new_v_map_x.shape[0] - 1, :, :] +=\
+            convolve(new_v_map_x[new_v_map_x.shape[0] - 1, :, :], filter_mat, 'same')
+        new_v_map_x[new_v_map_x.shape[0] - 1, :, :] = 0
+
+        filter_mat = np.array([[-1.0], [0], [1.0]]) / 2.0
+        new_v_map_x[:, 0, :] += convolve(new_v_map_y[:, 0, :], filter_mat, 'same')
+        new_v_map_y[:, 0, :] = 0
+        new_v_map_x[:, new_v_map_x.shape[1] - 1, :] +=\
+            convolve(new_v_map_y[:, new_v_map_x.shape[1] - 1, :], filter_mat, 'same')
+        new_v_map_y[:, new_v_map_x.shape[1] - 1, :] = 0
+
+        # corners
+        new_v_map_x[0, 0, 0] = new_v_map_y[0, 0, 0] = new_v_map_z[0, 0, 0] = 0
+        new_v_map_x[-1, 0, 0] = new_v_map_y[-1, 0, 0] = new_v_map_z[-1, 0, 0] = 0
+        new_v_map_x[-1, -1, 0] = new_v_map_y[-1, -1, 0] = new_v_map_z[-1, -1, 0] = 0
+        new_v_map_x[-1, -1, -1] = new_v_map_y[-1, -1, -1] = new_v_map_z[-1, -1, -1] = 0
+        new_v_map_x[0, -1, -1] = new_v_map_y[0, -1, -1] = new_v_map_z[0, -1, -1] = 0
+        new_v_map_x[0, -1, 0] = new_v_map_y[0, -1, 0] = new_v_map_z[0, -1, 0] = 0
+        new_v_map_x[-1, -1, 0] = new_v_map_y[-1, -1, 0] = new_v_map_z[-1, -1, 0] = 0
+        new_v_map_x[-1, 0, -1] = new_v_map_y[-1, 0, -1] = new_v_map_z[-1, 0, -1] = 0
+
+        self.v_map_x = new_v_map_x
+        self.v_map_y = new_v_map_y
+        self.v_map_z = new_v_map_z
+
+        filter_mat = (np.zeros((3, 3, 1)) + 1.0) / 9.0
+
+        v_map = np.pad(self.v_map_x, 1, 'edge')
+        self.v_map_x = convolve(v_map, filter_mat, mode='same')[1:-1, 1:-1, 1:2]
+        v_map = np.pad(self.v_map_y, 1, 'edge')
+        self.v_map_y = convolve(v_map, filter_mat, mode='same')[1:-1, 1:-1, 1:2]
+        v_map = np.pad(self.v_map_z, 1, 'edge')
+        self.v_map_z = convolve(v_map, filter_mat, mode='same')[1:-1, 1:-1, 1:2]
+        # moving heat
+        heat_map = np.zeros(self.heat_map.shape)
+        for x_pos in range(self.v_map_x.shape[0]):
+            for y_pos in range(self.v_map_x.shape[1]):
+                for z_pos in range(self.v_map_x.shape[2]):
+                    target_x = min(self.v_map_x.shape[0] - 1,
+                                   max(0, int(round(x_pos + self.v_map_x[x_pos, y_pos, z_pos]))))
+                    target_y = min(self.v_map_x.shape[1] - 1,
+                                   max(0, int(round(y_pos + self.v_map_y[x_pos, y_pos, z_pos]))))
+                    target_z = min(self.v_map_x.shape[2] - 1,
+                                   max(0, int(round(z_pos + self.v_map_z[x_pos, y_pos, z_pos]))))
+                    heat_map[target_x, target_y, target_z] += self.heat_map[x_pos, y_pos, z_pos]
+
+        self.heat_map = heat_map
+        # dispersing heat
+        heat_map = np.pad(self.heat_map, 1, 'edge')
+        self.heat_map = convolve(heat_map, filter_mat, mode='same')[1:-1, 1:-1, 1:2]
+        # heat source keeps source block on constant heat
+        self.heat_map[50-5:50+5, 0, 0] = 100.0
+        # roof gets cooled off to min temp
+        self.heat_map[:, 99, :] = np.maximum(self.heat_map[:, 99, :] * 0.8, 0.0)
+
+        print(1.0 / (time.time() - self.time))
+        self.time = time.time()
+        glutPostRedisplay()
+
+    def resize(self, w, h):
+        w = max(w, 1)
+        h = max(h, 1)
+        glViewport(0, 0, w, h)
+        self.projMatrix = perspectiveMatrix(45.0, float(w) / float(h), 0.01, MAX_DISTANCE)
+        self.width = w
+        self.height = h
+
+    def keyboardHandler(self, key: int, x: int, y: int):
+        if key == b'\x1b':
+            exit()
+
+        if key == b'+':
+            self.rx += 0.25
+        if key == b'-':
+            self.rx -= 0.25
+
+        if key == b'w':
+            self.cy += 0.25
+        if key == b's':
+            self.cy -= 0.25
+
+        if key == b'a':
+            self.cx -= 0.25
+        if key == b'd':
+            self.cx += 0.25
+
+        if key == b'q':
+            self.opening -= 0.25
+        if key == b'e':
+            self.opening += 0.25
+
+        if key == b'r':
+            print(self.cx, self.cy, self.opening)
+        # glutPostRedisplay()
+        # print(key,x,y)
+
+    def funcKeydHandler(self, key: int, x: int, y: int):
+        if key == 11:
+            glutFullScreenToggle()
+        # print(key)
+
+if __name__ == '__main__':
+    client = Client(pos=[-50, -50, -200])
diff --git a/Client/__init__.py b/Client/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Lights/Lights.py b/Lights/Lights.py
index d12a3f5..43e760e 100644
--- a/Lights/Lights.py
+++ b/Lights/Lights.py
@@ -1,44 +1,16 @@
-from OpenGL.GL import *
-import numpy as np
-from OpenGL.GL.ARB.vertex_array_object import glDeleteVertexArrays
-from OpenGL.GL.framebufferobjects import glBindRenderbuffer
-from OpenGL.GLUT import *
-import OpenGL.GLUT.freeglut
-from OpenGL.GLU import *
-from OpenGL.GL import *
-from ctypes import sizeof, c_float, c_void_p, c_uint
 from MatrixStuff.Transformations import *
+from Objects.Identifiable import Identifiable
 
 
-class Light:
+class Light(Identifiable):
     programId = {}
     depthshaderId = -1
 
     def getDepthProgram(self, vertexshader=-1, geometryshader=-1):
-        if ((
-        vertexshader, geometryshader) not in self.programId.keys() and vertexshader != -1 and geometryshader != -1):
-            if self.depthshaderId == -1:
-                with open('./Lights/depthfragment.glsl', 'r') as f:
-                    fragment_shader_string = f.read()
-                fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
-                glShaderSource(fragment_shader_id, fragment_shader_string)
-                glCompileShader(fragment_shader_id)
-                if glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
-                    raise RuntimeError(glGetShaderInfoLog(fragment_shader_id))
-
-            program_id = glCreateProgram()
-            glAttachShader(program_id, vertexshader)
-            glAttachShader(program_id, geometryshader)
-            glAttachShader(program_id, fragment_shader_id)
-            glLinkProgram(program_id)
-            self.programId[(vertexshader, geometryshader)] = program_id
-            return program_id
-        else:
-            if (vertexshader, geometryshader) not in self.programId.keys():
-                return -1
-            return self.programId[(vertexshader, geometryshader)]
+        pass
 
     def __init__(self):
+        super(Light, self).__init__()
         self._ModelviewProjectionMatrix = np.identity(4)
         self._pos = [0, 0, 0]
         self._lightColor = [1, 1, 1]
@@ -71,36 +43,7 @@ class Light:
         self._pos = value
 
     def prepareForDepthMapping(self):
-        new = False
-        if self.FramebufferId == -1:
-            self.FramebufferId = glGenFramebuffers(1)
-            new = True
-        glClearColor(1.0, 1.0, 1.0, 1.0)
-        glBindFramebuffer(GL_FRAMEBUFFER, self.FramebufferId)
-        glCullFace(GL_FRONT)
-        glViewport(0, 0, self.map_size, self.map_size)
-        if new:
-            if self.DepthBuffer == -1:
-                self.DepthBuffer = glGenTextures(1)
-            glBindTexture(GL_TEXTURE_2D, self.DepthBuffer)
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, self.map_size, self.map_size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None)
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
-            glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, np.array([0, 0, 0], dtype=np.float32))
-
-            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.FramebufferId, 0)
-
-            DrawBuffers = [GL_NONE]
-            glDrawBuffers(DrawBuffers)
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
-            return False
+        pass
 
     def finishDepthMapping(self):
-        glCullFace(GL_BACK)
-        DrawBuffers = [GL_COLOR_ATTACHMENT0]
-        glDrawBuffers(DrawBuffers)
-        glClearColor(0.0, 0.0, 0.0, 1.0)
-        glBindFramebuffer(GL_FRAMEBUFFER, 0)
+        pass
diff --git a/Lights/Spotlight/Spotlight.py b/Lights/Spotlight/Spotlight.py
index e0e1570..ed8cc84 100644
--- a/Lights/Spotlight/Spotlight.py
+++ b/Lights/Spotlight/Spotlight.py
@@ -3,4 +3,95 @@ from OpenGL.GL import *
 from MatrixStuff.Transformations import *
 
 class Spotlight(Light):
-    pass
+    programId = {}
+    depthshaderId = -1
+
+    @classmethod
+    def getDepthProgram(cls, vertexshader=-1, geometryshader=-1):
+        if ((
+        vertexshader, geometryshader) not in cls.programId.keys() and vertexshader != -1 and geometryshader != -1):
+            fragment_shader_id = -1
+            if cls.depthshaderId == -1:
+                with open('./Lights/depthfragment.glsl', 'r') as f:
+                    fragment_shader_string = f.read()
+                fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
+                glShaderSource(fragment_shader_id, fragment_shader_string)
+                glCompileShader(fragment_shader_id)
+                if glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
+                    raise RuntimeError(glGetShaderInfoLog(fragment_shader_id))
+
+            program_id = glCreateProgram()
+            glAttachShader(program_id, vertexshader)
+            glAttachShader(program_id, geometryshader)
+            glAttachShader(program_id, fragment_shader_id)
+            glLinkProgram(program_id)
+            cls.programId[(vertexshader, geometryshader)] = program_id
+            return program_id
+        else:
+            if (vertexshader, geometryshader) not in cls.programId.keys():
+                return -1
+            return cls.programId[(vertexshader, geometryshader)]
+
+    def __init__(self, target=[0, 0, 0], opening=45):
+        super(Spotlight, self).__init__()
+        self.target = target
+        self.opening = opening
+
+    @property
+    def lightColor(self):
+        return self._lightColor
+
+    @lightColor.setter
+    def lightColor(self, value):
+        self._lightColor = value
+
+    @property
+    def ModelviewProjectionMatrix(self):
+        return self._ModelviewProjectionMatrix
+
+    @ModelviewProjectionMatrix.setter
+    def ModelviewProjectionMatrix(self, value):
+        self._ModelviewProjectionMatrix = np.matrix(value)
+
+    @property
+    def pos(self):
+        return self._pos
+
+    @pos.setter
+    def pos(self, value):
+        self._pos = value
+
+    def prepareForDepthMapping(self):
+        new = False
+        if self.FramebufferId == -1:
+            self.FramebufferId = glGenFramebuffers(1)
+            new = True
+        glClearColor(1.0, 1.0, 1.0, 1.0)
+        glBindFramebuffer(GL_FRAMEBUFFER, self.FramebufferId)
+        glCullFace(GL_FRONT)
+        glViewport(0, 0, self.map_size, self.map_size)
+        if new:
+            if self.DepthBuffer == -1:
+                self.DepthBuffer = glGenTextures(1)
+            glBindTexture(GL_TEXTURE_2D, self.DepthBuffer)
+            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, self.map_size, self.map_size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None)
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
+            glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, np.array([0, 0, 0], dtype=np.float32))
+
+            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.FramebufferId, 0)
+
+            DrawBuffers = [GL_NONE]
+            glDrawBuffers(DrawBuffers)
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
+            return False
+
+    def finishDepthMapping(self):
+        glCullFace(GL_BACK)
+        DrawBuffers = [GL_COLOR_ATTACHMENT0]
+        glDrawBuffers(DrawBuffers)
+        glClearColor(0.0, 0.0, 0.0, 1.0)
+        glBindFramebuffer(GL_FRAMEBUFFER, 0)
\ No newline at end of file
diff --git a/MatrixStuff/Transformations.py b/MatrixStuff/Transformations.py
index 013fdfe..c2ccb9a 100644
--- a/MatrixStuff/Transformations.py
+++ b/MatrixStuff/Transformations.py
@@ -3,7 +3,7 @@ import numpy as np
 
 
 def lookAt(eyeX, eyeY, eyeZ, cX, cY, cZ, upX, upY, upZ):
-    F = np.matrix([cX - eyeX, cY - eyeY, cZ - eyeZ])
+    F = np.matrix([eyeX - cX, eyeY -cY, eyeZ - cZ])
     UP = np.matrix([upX, upY, upZ])
     f = F / math.sqrt(np.sum(np.square(F)))
     UP = UP / math.sqrt(np.sum(np.square(UP)))
diff --git a/Objects/Cube/Cube.py b/Objects/Cube/Cube.py
index d64e9a7..7ba6ecc 100644
--- a/Objects/Cube/Cube.py
+++ b/Objects/Cube/Cube.py
@@ -6,7 +6,6 @@ import numpy as np
 
 
 class Cube(Object):
-
     def __init__(self):
         super(Cube, self).__init__()
         if (Cube.GeometryShaderId == -1):
diff --git a/Objects/Identifiable.py b/Objects/Identifiable.py
new file mode 100644
index 0000000..7b55365
--- /dev/null
+++ b/Objects/Identifiable.py
@@ -0,0 +1,15 @@
+from multiprocessing import Semaphore
+
+creatingID = Semaphore()
+
+class Identifiable:
+    lastID: int = 0
+
+    def __init__(self):
+        creatingID.acquire()
+        self.lastID += 1
+        self.id = self.lastID
+        creatingID.release()
+
+    def __eq__(self, other):
+        return self.id == other.id and type(self) == type(other)
diff --git a/Objects/Renderable.py b/Objects/Renderable.py
new file mode 100644
index 0000000..a24d804
--- /dev/null
+++ b/Objects/Renderable.py
@@ -0,0 +1,18 @@
+from OpenGL.GLU import gluErrorString
+from OpenGL.GL import glGetError, GL_NO_ERROR
+
+class Renderable:
+    def render(self, projMatrix, geometryRotMatrix, alternateprograms=None):
+        pass
+
+    @staticmethod
+    def check_error(message):
+        gl_error = glGetError()
+        if (gl_error != GL_NO_ERROR):
+            print("Error: " + message)
+            if (gluErrorString(gl_error)):
+                print(gluErrorString(gl_error))
+            else:
+                print(hex(gl_error))
+            return True
+        return False
\ No newline at end of file
diff --git a/Objects/Structure.py b/Objects/Structure.py
index 0697fb2..5b71802 100644
--- a/Objects/Structure.py
+++ b/Objects/Structure.py
@@ -11,31 +11,13 @@ import numpy as np
 from OpenGL.extensions import alternate
 
 from Objects.Objects import Object
-from MatrixStuff.Transformations import translate
-
-
-def check_error(message):
-    gl_error = glGetError()
-    if (gl_error != GL_NO_ERROR):
-        print("Error: " + message)
-        if (gluErrorString(gl_error)):
-            print(gluErrorString(gl_error))
-        else:
-            print(hex(gl_error))
-        return True
-    return False
-
-
-class Renderable:
-    def render(self, projMatrix, geometryRotMatrix, alternateprograms=None):
-        pass
+from Objects.Renderable import Renderable
 
 
 class Structure(Renderable):
     def __init__(self):
         self.Objects = {}
         self.vais = {}
-        self.Matrix = np.identity(4, np.float32)
         self.dirty = False
 
     def addShape(self, program, shape):
@@ -65,6 +47,7 @@ class Structure(Renderable):
                 tpbi = GLuint(0)
                 tcbi = GLuint(0)
                 tsbi = GLuint(0)
+                num = len(objects)
 
                 glGenVertexArrays(1, tvai)
                 glBindVertexArray(tvai)
@@ -81,7 +64,7 @@ class Structure(Renderable):
                     positions.append(o.pos[2])
                 glBufferData(GL_ARRAY_BUFFER, np.array(positions, dtype=np.float32), GL_STATIC_DRAW)
                 glVertexAttribPointer(vid, 3, GL_FLOAT, GL_FALSE, 0, None)
-                check_error("Could not create position buffer")
+                self.check_error("Could not create position buffer")
 
                 colors = []
                 for o in objects:
@@ -95,7 +78,7 @@ class Structure(Renderable):
                 if vc != -1:
                     glEnableVertexAttribArray(vc)
                     glVertexAttribPointer(vc, 3, GL_FLOAT, GL_FALSE, 0, None)
-                    check_error("Could not create color buffer")
+                    self.check_error("Could not create color buffer")
 
                 if hasattr(objects[0], 'size'):
                     sizes = []
@@ -110,36 +93,38 @@ class Structure(Renderable):
                     if vs != -1:
                         glEnableVertexAttribArray(vs)
                         glVertexAttribPointer(vs, 3, GL_FLOAT, GL_FALSE, 0, None)
-                        check_error("Could not create size buffer")
+                        self.check_error("Could not create size buffer")
 
                 glBindVertexArray(0)
-                self.vais[key] = (tvai, tpbi, tcbi, tsbi)
+                self.vais[key] = (tvai, tpbi, tcbi, tsbi, num)
             self.dirty = False
 
     def clearVertexArrays(self):
-        for key, (a, p, c, s) in self.vais.items():
-            if p != -1:
-                glDisableVertexAttribArray(p)
-                glDeleteBuffers(1, [p])
-            if c != -1:
-                glDisableVertexAttribArray(c)
-                glDeleteBuffers(1, [c])
-            if s != -1 and s != GLuint(-1):
-                glDisableVertexAttribArray(s)
-                glDeleteBuffers(1, [s])
-            glDeleteVertexArrays(1, a)
-            check_error("Could not destroy vertex array")
+        temp = dict(self.vais)
+        self.vais = {}
+        for key, vertex_array_ids in temp.items():
+            if vertex_array_ids[1] != -1 and vertex_array_ids[1] != GLuint(-1):
+                glDeleteBuffers(1, [vertex_array_ids[1]])
+                self.check_error("Could not destroy buffer")
+            if vertex_array_ids[2] != -1 and vertex_array_ids[2] != GLuint(-1):
+                glDeleteBuffers(1, [vertex_array_ids[2]])
+                self.check_error("Could not destroy buffer")
+            if vertex_array_ids[3] != -1 and vertex_array_ids[3] != GLuint(-1):
+                glDeleteBuffers(1, [vertex_array_ids[3]])
+                self.check_error("Could not destroy buffer")
+            glDeleteVertexArrays(1, vertex_array_ids[0])
+            self.check_error("Could not destroy vertex array")
 
     def render(self, projMatrix, geometryRotMatrix, alternateprograms=None):
-
-        for key, tupel in self.vais.items():
+        self.buildvertexArrays()
+        for key, vertex_array_ids in self.vais.items():
             if alternateprograms == None:
                 program_id = key
             else:
                 assert key in alternateprograms
                 program_id = alternateprograms[key]
             glUseProgram(program_id)
-            check_error("Renderingprogram is not initialized!")
+            self.check_error("Renderingprogram is not initialized!")
 
             projection = glGetUniformLocation(program_id, 'projModelViewMatrix')
             rot = glGetUniformLocation(program_id, 'rotMatrix')
@@ -147,9 +132,9 @@ class Structure(Renderable):
             glUniformMatrix4fv(projection, 1, GL_FALSE, np.array(projMatrix))
             glUniformMatrix3fv(rot, 1, GL_FALSE, np.array(geometryRotMatrix))
 
-            glBindVertexArray(tupel[0])
-            glDrawArrays(GL_POINTS, 0, len(self.Objects[key]))
-            check_error("Rendering problem")
+            glBindVertexArray(vertex_array_ids[0])
+            glDrawArrays(GL_POINTS, 0, vertex_array_ids[4])
+            self.check_error("Rendering problem")
 
             glBindVertexArray(0)
             glUseProgram(0)
@@ -171,83 +156,10 @@ class CompoundStructure(Renderable):
 
     def render(self, projMatrix, geometryRotMatrix, alternateprograms=None):
         for (structure, M, R) in self.Structures:
-            structure.buildvertexArrays()
             structure.render(M * projMatrix, R * geometryRotMatrix, alternateprograms)
 
     def __eq__(self, other):
         if type(other) is type(self):
             return self.Structures == other.Structures
         else:
-            return False
-
-
-class WorldChunk(Renderable):
-    def __init__(self, width, length, height):
-        assert width > 0, 'Width must be greater than 0'
-        assert length > 0, 'length must be greater than 0'
-        assert height > 0, 'height must be greater than 0'
-        self.visible = []
-        self.content = []
-        self.entities = []
-
-        self.width = width
-        self.length = length
-        self.height = height
-
-        for x in range(width):
-            self.content.append([])
-            self.visible.append([])
-            for y in range(length):
-                self.content[x].append([])
-                self.visible[x].append([])
-                for z in range(height):
-                    self.content[x][y].append(None)
-                    self.visible[x][y].append(4)
-
-    def put_object(self, x: int, y: int, z: int, new_object: Object):
-        assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
-        assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
-        assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
-        self.content[x][y][z] = new_object
-
-        change = -1 if new_object is not None else 1
-        visible_carry_over = []
-        if x + 1 >= self.width:
-            visible_carry_over.append((1, 0, 0, change))
-        else:
-            self.visible[x + 1][y][z] += change
-        if x - 1 < 0:
-            visible_carry_over.append((-1, 0, 0, change))
-        else:
-            self.visible[x - 1][y][z] += change
-
-        if y + 1 >= self.length:
-            visible_carry_over.append((0, 1, 0, change))
-        else:
-            self.visible[x][y + 1][z] += change
-        if y - 1 < 0:
-            visible_carry_over.append((0, -1, 0, change))
-        else:
-            self.visible[x][y - 1][z] += change
-
-        if z + 1 >= self.height:
-            visible_carry_over.append((0, 0, 1, change))
-        else:
-            self.visible[x][y][z + 1] += change
-        if z - 1 < 0:
-            visible_carry_over.append((0, 0, -1, change))
-        else:
-            self.visible[x][y][z - 1] += change
-
-        return visible_carry_over
-
-    def render(self, projMatrix, geometryRotMatrix, alternateprograms=None):
-        for x in range(self.width):
-            for y in range(self.length):
-                for z in range(self.height):
-                    if self.visible[x][y][z] > 0 and self.content[x][y][z] is not None:
-                        self.content[x][y][z].render(translate(x, y, z) * projMatrix,
-                                                     geometryRotMatrix, alternateprograms)
-
-        for entity in self.entities:
-            entity.render(projMatrix, geometryRotMatrix, alternateprograms)
+            return False
\ No newline at end of file
diff --git a/Objects/World.py b/Objects/World.py
new file mode 100644
index 0000000..4443f01
--- /dev/null
+++ b/Objects/World.py
@@ -0,0 +1,363 @@
+from Lights.Lights import Light
+from Objects.Objects import Object
+from Objects.Renderable import Renderable
+from Objects.Structure import Structure
+from MatrixStuff.Transformations import translate
+from OpenGL.GLU import *
+from OpenGL.GL import *
+import math
+import numpy as np
+
+class WorldChunk(Structure):
+    def __init__(self, width: int, length: int, height: int, programs: dict):
+        assert width > 0, 'Width must be greater than 0'
+        assert length > 0, 'length must be greater than 0'
+        assert height > 0, 'height must be greater than 0'
+        super(WorldChunk, self).__init__()
+        self.visible = []
+        self.content = []
+        self.entities = []
+        self.lights = []
+
+        self.width = width
+        self.length = length
+        self.height = height
+        self.programs = programs
+
+        for x in range(width):
+            self.content.append([])
+            self.visible.append([])
+            for y in range(length):
+                self.content[x].append([])
+                self.visible[x].append([])
+                for z in range(height):
+                    self.content[x][y].append(None)
+                    self.visible[x][y].append(4)
+
+    def put_object(self, x: int, y: int, z: int, new_object: Object):
+        assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
+        assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
+        assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
+        no_visibility_changes = (self.content[x][y][z] is None) == (new_object is None)
+
+        self.content[x][y][z] = new_object
+        new_object.translate(translate(x, y, z))
+
+        change = -1 if new_object is not None else 1
+        visible_carry_over = []
+        if not no_visibility_changes:
+            if x + 1 >= self.width:
+                visible_carry_over.append((1, 0, 0, change))
+            else:
+                self.visible[x + 1][y][z] += change
+            if x - 1 < 0:
+                visible_carry_over.append((-1, 0, 0, change))
+            else:
+                self.visible[x - 1][y][z] += change
+
+            if y + 1 >= self.length:
+                visible_carry_over.append((0, 1, 0, change))
+            else:
+                self.visible[x][y + 1][z] += change
+            if y - 1 < 0:
+                visible_carry_over.append((0, -1, 0, change))
+            else:
+                self.visible[x][y - 1][z] += change
+
+            if z + 1 >= self.height:
+                visible_carry_over.append((0, 0, 1, change))
+            else:
+                self.visible[x][y][z + 1] += change
+            if z - 1 < 0:
+                visible_carry_over.append((0, 0, -1, change))
+            else:
+                self.visible[x][y][z - 1] += change
+
+        return visible_carry_over
+
+    def get_object(self, x: int, y: int, z: int):
+        assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
+        assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
+        assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
+
+        return self.content[x][y][z]
+
+    def apply_visible_carry_over(self, x: int, y: int, z: int, change: int):
+        assert 0 <= x < self.width, 'Apply visible out of bounds for x coordinate! Must be between 0 and %i' % self.width
+        assert 0 <= y < self.length, 'Apply visible out of bounds for y coordinate! Must be between 0 and %i' % self.length
+        assert 0 <= z < self.height, 'Apply visible out of bounds for z coordinate! Must be between 0 and %i' % self.height
+
+        self.visible[x][y][z] += change
+
+    def set_visibility(self, x: int, y: int, z: int, visibility: int):
+        assert 0 <= x < self.width, 'Apply visible out of bounds for x coordinate! Must be between 0 and %i' % self.width
+        assert 0 <= y < self.length, 'Apply visible out of bounds for y coordinate! Must be between 0 and %i' % self.length
+        assert 0 <= z < self.height, 'Apply visible out of bounds for z coordinate! Must be between 0 and %i' % self.height
+
+        self.visible[x][y][z] = visibility
+
+    def buildvertexArrays(self):
+        if self.dirty:
+            self.clearVertexArrays()
+            glEnableClientState(GL_VERTEX_ARRAY)
+            glEnableClientState(GL_TEXTURE_COORD_ARRAY)
+            glEnableClientState(GL_NORMAL_ARRAY)
+            glEnableClientState(GL_COLOR_ARRAY)
+            self.vais = {}
+
+            objects = {}
+            counts = {}
+            for x in range(self.width):
+                for y in range(self.length):
+                    for z in range(self.height):
+                        if self.content[x][y][z] is not None:  # and self.visible[x][y][z] > 0: TODO: check visibility...
+                            if self.programs[type(self.content[x][y][z])] not in objects.keys():
+                                objects[self.programs[type(self.content[x][y][z])]] = []
+                                counts[self.programs[type(self.content[x][y][z])]] = 0
+                            objects[self.programs[type(self.content[x][y][z])]].append(self.content[x][y][z])
+                            counts[self.programs[type(self.content[x][y][z])]] += 1
+
+            for key, object_list in objects.items():
+                tvai = GLuint(0)
+                tpbi = GLuint(0)
+                tcbi = GLuint(0)
+                tsbi = GLuint(0)
+
+                glGenVertexArrays(1, tvai)
+                glBindVertexArray(tvai)
+
+                vid = glGetAttribLocation(key, "in_position")
+                glEnableVertexAttribArray(vid)
+
+                tpbi = glGenBuffers(1)
+                glBindBuffer(GL_ARRAY_BUFFER, tpbi)
+                positions = []
+                for o in object_list:
+                    positions.append(o.pos[0])
+                    positions.append(o.pos[1])
+                    positions.append(o.pos[2])
+                glBufferData(GL_ARRAY_BUFFER, np.array(positions, dtype=np.float32), GL_STATIC_DRAW)
+                glVertexAttribPointer(vid, 3, GL_FLOAT, GL_FALSE, 0, None)
+                self.check_error("Could not create position buffer")
+
+                colors = []
+                for o in object_list:
+                    colors.append(o.color[0])
+                    colors.append(o.color[1])
+                    colors.append(o.color[2])
+                tcbi = glGenBuffers(1)
+                glBindBuffer(GL_ARRAY_BUFFER, tcbi)
+                glBufferData(GL_ARRAY_BUFFER, np.array(colors, dtype=np.float32), GL_STATIC_DRAW)
+                vc = glGetAttribLocation(key, "MyInColor")
+                if vc != -1:
+                    glEnableVertexAttribArray(vc)
+                    glVertexAttribPointer(vc, 3, GL_FLOAT, GL_FALSE, 0, None)
+                    self.check_error("Could not create color buffer")
+
+                if hasattr(object_list[0], 'size'):
+                    sizes = []
+                    for o in object_list:
+                        sizes.append(o.size[0])
+                        sizes.append(o.size[1])
+                        sizes.append(o.size[2])
+                    tsbi = glGenBuffers(1)
+                    glBindBuffer(GL_ARRAY_BUFFER, tsbi)
+                    glBufferData(GL_ARRAY_BUFFER, np.array(sizes, dtype=np.float32), GL_STATIC_DRAW)
+                    vs = glGetAttribLocation(key, "MyInSize")
+                    if vs != -1:
+                        glEnableVertexAttribArray(vs)
+                        glVertexAttribPointer(vs, 3, GL_FLOAT, GL_FALSE, 0, None)
+                        self.check_error("Could not create size buffer")
+
+                glBindVertexArray(0)
+                self.vais[key] = (tvai, tpbi, tcbi, tsbi, counts[key])
+            self.dirty = False
+
+    def render(self, proj_matrix, geometry_rot_matrix, alternate_programs=None):
+        super(WorldChunk, self).render(proj_matrix, geometry_rot_matrix, alternate_programs)
+
+        for entity in self.entities:
+            entity.render(proj_matrix, geometry_rot_matrix, alternate_programs)
+
+    def set_color(self, x: int, y: int, z: int, r: float, g: float, b: float):
+        assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
+        assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
+        assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
+
+        if self.content[x][y][z] is not None:
+            self.content[x][y][z].setColor(r, g, b)
+            self.dirty = True
+
+class World(Renderable):
+    def __init__(self, chunk_size_x: int, chunk_size_y: int, chunk_size_z: int,
+                 chunk_n_x: int, chunk_n_y: int, chunk_n_z: int, programs: dict):
+        super(World, self).__init__()
+        self.chunk_size_x = chunk_size_x
+        self.chunk_size_y = chunk_size_y
+        self.chunk_size_z = chunk_size_z
+        self.chunk_n_x = chunk_n_x
+        self.chunk_n_y = chunk_n_y
+        self.chunk_n_z = chunk_n_z
+        self.programs = programs
+
+        self.chunks: [[[WorldChunk]]] = []
+        for x in range(chunk_n_x):
+            self.chunks.append([])
+            for y in range(chunk_n_y):
+                self.chunks[x].append([])
+                for z in range(chunk_n_z):
+                    self.chunks[x][y].append(None)
+
+    def set_color(self, x: int, y: int, z: int, r: float, g: float, b: float):
+        x = x % (self.chunk_size_x * self.chunk_n_x)
+        y = y % (self.chunk_size_y * self.chunk_n_y)
+        z = z % (self.chunk_size_z * self.chunk_n_z)
+
+        chunk_x = int(x / self.chunk_size_x)
+        chunk_y = int(y / self.chunk_size_y)
+        chunk_z = int(z / self.chunk_size_z)
+        if self.chunks[chunk_x][chunk_y][chunk_z] is not None:
+            self.chunks[chunk_x][chunk_y][chunk_z].set_color(x % self.chunk_size_x,
+                                                              y % self.chunk_size_y,
+                                                              z % self.chunk_size_z,
+                                                              r, g, b)
+
+    def put_object(self, x: int, y: int, z: int, new_object: Object):
+        x = x % (self.chunk_size_x * self.chunk_n_x)
+        y = y % (self.chunk_size_y * self.chunk_n_y)
+        z = z % (self.chunk_size_z * self.chunk_n_z)
+
+        chunk_x = int(x / self.chunk_size_x)
+        chunk_y = int(y / self.chunk_size_y)
+        chunk_z = int(z / self.chunk_size_z)
+
+        if self.chunks[chunk_x][chunk_y][chunk_z] is None:
+            self.chunks[chunk_x][chunk_y][chunk_z] = WorldChunk(self.chunk_size_x, self.chunk_size_y, self.chunk_size_z, self.programs)
+
+        carry_overs = self.chunks[chunk_x][chunk_y][chunk_z].put_object(x % self.chunk_size_x,
+                                                                        y % self.chunk_size_y,
+                                                                        z % self.chunk_size_z,
+                                                                        new_object)
+        for carry_over in carry_overs:
+            if self.chunks[(chunk_x + carry_over[0]) % self.chunk_n_x][(chunk_y + carry_over[1]) % self.chunk_n_y][(chunk_z + carry_over[2]) % self.chunk_n_z] is not None:
+                self.chunks[
+                    (chunk_x + carry_over[0]) % self.chunk_n_x][
+                    (chunk_y + carry_over[1]) % self.chunk_n_y][
+                    (chunk_z + carry_over[2]) % self.chunk_n_z].apply_visible_carry_over(
+                    (x + carry_over[0]) % self.chunk_size_x,
+                    (y + carry_over[1]) % self.chunk_size_y,
+                    (z + carry_over[2]) % self.chunk_size_z,
+                    carry_over[3])
+                self.chunks[
+                    (chunk_x + carry_over[0]) % self.chunk_n_x][
+                    (chunk_y + carry_over[1]) % self.chunk_n_y][
+                    (chunk_z + carry_over[2]) % self.chunk_n_z].dirty = True
+
+        visibility = 6
+        neighbour = self.get_object(x - 1, y, z)
+        if neighbour is not None:
+            visibility -= 1
+
+        neighbour = self.get_object(x + 1, y, z)
+        if neighbour is not None:
+            visibility -= 1
+
+        neighbour = self.get_object(x, y - 1, z)
+        if neighbour is not None:
+            visibility -= 1
+
+        neighbour = self.get_object(x, y + 1, z)
+        if neighbour is not None:
+            visibility -= 1
+
+        neighbour = self.get_object(x, y, z - 1)
+        if neighbour is not None:
+            visibility -= 1
+
+        neighbour = self.get_object(x, y, z + 1)
+        if neighbour is not None:
+            visibility -= 1
+
+        self.chunks[chunk_x][chunk_y][chunk_z].set_visibility(x % self.chunk_size_x,
+                                                              y % self.chunk_size_y,
+                                                              z % self.chunk_size_z,
+                                                              visibility)
+        self.chunks[chunk_x][chunk_y][chunk_z].dirty = True
+
+    def get_object(self, x: int, y: int, z: int):
+        x = x % (self.chunk_size_x * self.chunk_n_x)
+        y = y % (self.chunk_size_y * self.chunk_n_y)
+        z = z % (self.chunk_size_z * self.chunk_n_z)
+
+        chunk_x = int(x / self.chunk_size_x)
+        chunk_y = int(y / self.chunk_size_y)
+        chunk_z = int(z / self.chunk_size_z)
+
+        if self.chunks[chunk_x][chunk_y][chunk_z] is None:
+            return None
+
+        return self.chunks[chunk_x][chunk_y][chunk_z].get_object(x % self.chunk_size_x,
+                                                                 y % self.chunk_size_y,
+                                                                 z % self.chunk_size_z)
+
+    def render(self, proj_matrix, geometry_rot_matrix, alternate_programs=None):
+        for x in range(self.chunk_n_x):
+            for y in range(self.chunk_n_y):
+                for z in range(self.chunk_n_z):
+                    if self.chunks[x][y][z] is not None:
+                        self.chunks[x][y][z].render(translate(x * self.chunk_size_x,
+                                                              y * self.chunk_size_y,
+                                                              z * self.chunk_size_z) * proj_matrix,
+                                                    geometry_rot_matrix, alternate_programs)
+
+    def add_light(self, x: float, y: float, z: float, l: Light):
+        x = x % (self.chunk_size_x * self.chunk_n_x)
+        y = y % (self.chunk_size_y * self.chunk_n_y)
+        z = z % (self.chunk_size_z * self.chunk_n_z)
+
+        chunk_x = int(x / self.chunk_size_x)
+        chunk_y = int(y / self.chunk_size_y)
+        chunk_z = int(z / self.chunk_size_z)
+
+        if self.chunks[chunk_x][chunk_y][chunk_z] is None:
+            self.chunks[chunk_x][chunk_y][chunk_z] = WorldChunk(self.chunk_size_x, self.chunk_size_y, self.chunk_size_z, self.programs)
+
+        self.chunks[chunk_x][chunk_y][chunk_z].lights.append(l)
+        l.pos = [x, y, z]
+
+    def remove_light(self, l: Light):
+        chunk_x = int(l.pos[0] / self.chunk_size_x)
+        chunk_y = int(l.pos[1] / self.chunk_size_y)
+        chunk_z = int(l.pos[2] / self.chunk_size_z)
+
+        if self.chunks[chunk_x][chunk_y][chunk_z] is None:
+            return False
+
+        if l in self.chunks[chunk_x][chunk_y][chunk_z].lights:
+            self.chunks[chunk_x][chunk_y][chunk_z].lights.remove(l)
+            return True
+        else:
+            return False
+
+    def move_light(self, l: Light, target_x: float, target_y: float, target_z: float):
+        self.remove_light(l)
+        self.add_light(target_x, target_y, target_z, l)
+
+    def get_lights_to_render(self, pos, distance):
+        distance_x = math.ceil(float(distance) / self.chunk_size_x)
+        distance_y = math.ceil(float(distance) / self.chunk_size_y)
+        distance_z = math.ceil(float(distance) / self.chunk_size_z)
+
+        pos_x = int(pos[0] / self.chunk_size_x)
+        pos_y = int(pos[1] / self.chunk_size_y)
+        pos_z = int(pos[2] / self.chunk_size_z)
+
+        lights = []
+        for x in range(distance_x):
+            for y in range(distance_y):
+                for z in range(distance_z):
+                    chunk = self.chunks[(pos_x + x) % self.chunk_n_x][(pos_y + y) % self.chunk_n_y][(pos_z + z) % self.chunk_n_z]
+                    if chunk is not None:
+                        lights += chunk.lights
+
+        return lights
diff --git a/WorldProvider/WorldProvider.py b/WorldProvider/WorldProvider.py
new file mode 100644
index 0000000..0401eca
--- /dev/null
+++ b/WorldProvider/WorldProvider.py
@@ -0,0 +1,10 @@
+from Objects.World import World
+
+
+class WorldProvider:
+    def __init__(self, programs):
+        self.world: World = World(10, 10, 10, 10, 10, 10, programs)
+
+    def update(self):
+        pass
+
diff --git a/config.json b/config.json
new file mode 100644
index 0000000..67ec66c
--- /dev/null
+++ b/config.json
@@ -0,0 +1,3 @@
+{
+  "render_light_distance": 100
+}
\ No newline at end of file
diff --git a/fragment.glsl b/fragment.glsl
index 6fc9d06..053317e 100644
--- a/fragment.glsl
+++ b/fragment.glsl
@@ -21,7 +21,7 @@ const float diffuseFactor = 0.5;
 const float specFactor = 1.0;
 const float shininess = 16.0;
 const float screenGamma = 2.2;
-const float pitl = 2*3.14159265359 / 16.0;
+const float pitl = 2.0*3.14159265359 / 24.0;
 const float circlelength = 100000.0;
 
 bool isVisible(int i, vec2 offs, float lambertian)
@@ -44,17 +44,25 @@ void main()
         vec3 halfDir = normalize(lightDir + viewDir);
         float specAngle = max(dot(halfDir, normalize(normal)), 0.0);
         specular = int(lambertian > 0)*pow(specAngle, shininess);
-        //int visible = int(!(texture(ShadowMaps,lightpos[i].xy/2 + vec2(0.5,0.5)).z < (lightpos[i].z)));
         float visible = 0;
         int count = 0;
 
         vec2 texelSize = 1.0 / textureSize(ShadowMaps[i], 0);
-        for(int x = -2; x <= 2; x++){
-            for(int y = -2; y <= 2; y++){
-                vec2 offs = vec2(x, y) * texelSize;
-                visible += float(int(isVisible(i, offs, lambertian))) * 1.0/25.0;
-            }
+
+        // Grid sampling
+//        for(int x = -2; x <= 2; x++){
+//            for(int y = -2; y <= 2; y++){
+//                vec2 offs = vec2(x, y) * texelSize;
+//                visible += float(int(isVisible(i, offs, lambertian))) * 1.0/25.0;
+//            }
+//        }
+        //Circle Sampling
+        visible += float(int(isVisible(i, vec2(0, 0), lambertian))) * 1.0/25.0;
+        for(int r = 0; r < 24; r++){
+            vec2 offs = vec2(sin(r * pitl), cos(r * pitl)) * texelSize;
+            visible += float(int(isVisible(i, offs, lambertian))) * 1.0/25.0;
         }
+
         bool condition = visible >= (1.0/5.0);
         visible = float(condition) * 1.0 + float(!condition) * visible;
 
diff --git a/main.py b/main.py
index 03eede3..a12950d 100644
--- a/main.py
+++ b/main.py
@@ -1,24 +1,13 @@
 import random
 import sys
 
+from WorldProvider.WorldProvider import WorldProvider
+
 print('64' if sys.maxsize > 2 ** 32 else '32')
 
-from wsgiref.validate import check_errors
-
-from OpenGL.GL.ARB.vertex_array_object import glDeleteVertexArrays
-from OpenGL.GL.framebufferobjects import glBindFramebuffer
-from OpenGL.GLUT import *
-import OpenGL.GLUT.freeglut
-from OpenGL.GLU import *
-from OpenGL.GL import *
-from ctypes import sizeof, c_float, c_void_p, c_uint
-
-import sys
-import math as math
-from Objects.Cube.Cube import *
-from Objects.Cuboid.Cuboid import *
-from Objects.Structure import *
-from MatrixStuff.Transformations import *
+from Objects.Cube.Cube import Cube
+from Objects.Cuboid.Cuboid import Cuboid
+from Objects.Structure import Structure, CompoundStructure
 from Lights.Spotlight.Spotlight import Spotlight
 from Lights.LightingManager import *
 import numpy as np
@@ -38,6 +27,8 @@ height = 1080
 opening = 45
 l = Spotlight()
 
+wp = None
+
 
 def main():
     lm = LightingManager()
@@ -142,16 +133,24 @@ def main():
     # struct.addShape(program_id, Cube().translate(translate(-1, 1, 0)).setColor(1, 1, 1))
     # struct.addShape(program_id, Cube().translate(translate(0, 1, 0)).setColor(0, 0, 1))
 
-    for x_pos in range(-10, 10, 1):
-        for y_pos in range(-10, 10):
+    for x_pos in range(0, 10, 1):
+        for y_pos in range(0, 10):
             z = random.random() * 2 - 1
             struct.addShape(program_id, Cuboid().translate(translate(x_pos, y_pos, z)).setColor(
                 random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0).setSize(
                 random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0
             ))
 
+    global wp
+    wp = WorldProvider({Cuboid: program_id})
 
-    struct.buildvertexArrays()
+    for x_pos in range(0, 10):
+        for y_pos in range(0, 10):
+            for z_pos in range(0, 10):
+                wp.world.put_object(x_pos, y_pos, z_pos, Cuboid().setColor(
+                random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0).setSize(
+                random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0
+            ))
 
     # struct.clearVertexArrays()
 
@@ -320,9 +319,10 @@ ry = 0
 
 
 def render(program_id, projMatrix, vai, x, y, z, alternateprograms=None):
-    global struct, cstruct
+    global struct, cstruct, wp
 
     cstruct.render(translate(x, y, z) * rotate(0, ry, 0, True) * projMatrix, rotate(rx, 0, 0), alternateprograms)
+    wp.world.render(translate(x, y, z) * rotate(0, ry, 0, True) * projMatrix, rotate(rx, 0, 0), alternateprograms)
     '''
     glUseProgram(program_id)
     check_error("Renderingprogram is not initialized!")
@@ -365,7 +365,7 @@ def display():
     global projMatrix
     projMatrix = perspectiveMatrix(45, float(width) / float(height), 0.01, 100.0)
 
-    newMat = translate(-cx, -cy, -5) * lookAt(cx, cy, 5, 0, 0, 0, 0, 1, 0) * perspectiveMatrix(opening,
+    newMat = translate(-cx, -cy, -5) * lookAt(0, 0, 0, cx, cy, 5, 0, 1, 0) * perspectiveMatrix(opening,
                                                                                                float(l.map_size) /
                                                                                                float(l.map_size),
                                                                                                0.01, 100.0)
@@ -380,8 +380,8 @@ def display():
     farid = glGetUniformLocation(program3_id, 'far')
     glUniform1f(nearid, 0.01)
     glUniform1f(farid, 100)
-    glUniform1f(widthid, width)
-    glUniform1f(heightid, height)
+    glUniform1f(widthid, l.map_size)
+    glUniform1f(heightid, l.map_size)
 
     altPrId = {}
     altPrId[program_id] = program3_id
@@ -418,7 +418,7 @@ def display():
     glUniform1iv(texID, 1, 0)
 
     glViewport(0, 0, width, height)
-    render(program_id, translate(0, 0, -10) * lookAt(0, 0, 10, 0, 0, 0, 0, 1, 0) * projMatrix, vai, 0, 0, 0)
+    render(program_id, translate(0, 0, -10) * lookAt(0, 0, 0, 0, 0, 10, 0, 1, 0) * projMatrix, vai, 0, 0, 0)
 
     # temp, _, _ = create_vertex_buffers(np.array([cx, cy, 5], dtype=np.float32),
     #                                    np.array([1, 1, 0], dtype=np.float32), program_id)
diff --git a/tests/test_Client.py b/tests/test_Client.py
new file mode 100644
index 0000000..2e850bc
--- /dev/null
+++ b/tests/test_Client.py
@@ -0,0 +1,6 @@
+from Client.Client import Client
+from WorldProvider.WorldProvider import WorldProvider
+
+def test_client_init():
+    wp = WorldProvider()
+    client = Client(wp, test=True)
diff --git a/tests/test_Structures.py b/tests/test_Structures.py
new file mode 100644
index 0000000..830f2fb
--- /dev/null
+++ b/tests/test_Structures.py
@@ -0,0 +1,59 @@
+from Lights.Lights import Light
+from Objects.Objects import Object
+from Objects.World import World
+
+
+def test_world_put_object():
+    w = World(5, 5, 5, 3, 3, 3)
+    o = Object()
+    w.put_object(5, 5, 5, o)
+
+    assert w.chunks[1][1][1].content[0][0][0] == o, 'Put Object into world failed'
+
+
+def test_world_get_object():
+    w = World(5, 5, 5, 3, 3, 3)
+    o = Object()
+    w.put_object(5, 5, 5, o)
+
+    assert w.get_object(5, 5, 5) == o, 'Put Object into world failed'
+
+
+def test_world_put_visibility():
+    w = World(5, 5, 5, 3, 3, 3)
+    o = Object()
+    w.put_object(4, 5, 5, o)
+    w.put_object(6, 5, 5, o)
+
+    w.put_object(5, 4, 5, o)
+    w.put_object(5, 6, 5, o)
+
+    w.put_object(5, 5, 4, o)
+    w.put_object(5, 5, 6, o)
+
+    w.put_object(5, 5, 5, o)
+
+    assert w.chunks[1][1][1].visible[0][0][0] == 0, 'Initial visibility not set!'
+
+    assert w.chunks[1][1][1].visible[1][0][0] == 5, 'Neighbours visibility not set!'
+    assert w.chunks[1][1][1].visible[0][1][0] == 5, 'Neighbours visibility not set!'
+    assert w.chunks[1][1][1].visible[0][0][1] == 5, 'Neighbours visibility not set!'
+
+    assert w.chunks[0][1][1].visible[4][0][0] == 5, 'Neighbours visibility not set!'
+    assert w.chunks[1][0][1].visible[0][4][0] == 5, 'Neighbours visibility not set!'
+    assert w.chunks[1][1][0].visible[0][0][4] == 5, 'Neighbours visibility not set!'
+
+
+def test_world_add_light():
+    w = World(5, 5, 5, 3, 3, 3)
+    l = Light()
+
+    w.add_light(1.5, 2, 3.7, l)
+
+
+def test_world_remove_light():
+    w = World(5, 5, 5, 3, 3, 3)
+    l = Light()
+
+    w.add_light(1.5, 2, 3.7, l)
+    w.remove_light(l)