From 41761b7b56f494199ec06213ff301efd1bc5c671 Mon Sep 17 00:00:00 2001 From: zomseffen Date: Sun, 8 Nov 2020 10:20:23 +0100 Subject: [PATCH] thermodynamic v1 --- Client/Client.py | 386 +++++++++++++++++++++++++++++++++ Client/__init__.py | 0 Lights/Lights.py | 69 +----- Lights/Spotlight/Spotlight.py | 93 +++++++- MatrixStuff/Transformations.py | 2 +- Objects/Cube/Cube.py | 1 - Objects/Identifiable.py | 15 ++ Objects/Renderable.py | 18 ++ Objects/Structure.py | 142 +++--------- Objects/World.py | 363 +++++++++++++++++++++++++++++++ WorldProvider/WorldProvider.py | 10 + config.json | 3 + fragment.glsl | 22 +- main.py | 48 ++-- tests/test_Client.py | 6 + tests/test_Structures.py | 59 +++++ 16 files changed, 1025 insertions(+), 212 deletions(-) create mode 100644 Client/Client.py create mode 100644 Client/__init__.py create mode 100644 Objects/Identifiable.py create mode 100644 Objects/Renderable.py create mode 100644 Objects/World.py create mode 100644 WorldProvider/WorldProvider.py create mode 100644 config.json create mode 100644 tests/test_Client.py create mode 100644 tests/test_Structures.py 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)