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