def init_skylight(self, pending_chunk): """ Initializes the skylight of each chunks To avoid unsufferable lag from propagating from the top of the chunks when most of the heights would be air, it instead runs a simple algorithm to check where the highest point of the chunk is and propagates skylight from this height""" chunk_pos = pending_chunk.chunk_position # Retrieve the highest chunk point height = 0 for lx in range(chunk.CHUNK_WIDTH): for lz in range(chunk.CHUNK_LENGTH): for ly in range(chunk.CHUNK_HEIGHT-1, -1, -1): if pending_chunk.blocks[lx][ly][lz]: break if ly > height: height = ly # Initialize skylight to 15 until that point and then queue a skylight propagation increase for lx in range(chunk.CHUNK_WIDTH): for lz in range(chunk.CHUNK_LENGTH): for ly in range(chunk.CHUNK_HEIGHT - 1, height, -1): pending_chunk.set_sky_light(glm.ivec3(lx, ly, lz), 15) pos = glm.ivec3(chunk.CHUNK_WIDTH * chunk_pos[0] + lx, ly, chunk.CHUNK_LENGTH * chunk_pos[2] + lz ) self.skylight_increase_queue.append((pos, 15)) self.propagate_skylight_increase(False)
def block_pos_to_id(cls, pos: glm.ivec3) -> int: pos = glm.ivec3(pos) assert (all(abs(pos) < glm.ivec3(0x800))) u64 = lambda x: x & 0xffff_ffff_ffff_ffff s64 = lambda x: (x & 0x7fff_ffff_ffff_ffff) - (x & 0x8000_0000_0000_0000) return s64( u64(pos.z) * 0x1_000_000 + \ u64(pos.y) * 0x1_000 + \ u64(pos.x) )
def check_in_frustum(self, chunk_pos): """Frustum check of each chunk. If the chunk is not in the view frustum, it is discarded""" planes = (Frustum.left, Frustum.right, Frustum.bottom, Frustum.top, Frustum.near, Frustum.far) result = 2 center = glm.vec3(chunk_pos * glm.ivec3(chunk.CHUNK_WIDTH, 0, chunk.CHUNK_LENGTH) + glm.ivec3(chunk.CHUNK_WIDTH / 2, chunk.CHUNK_HEIGHT / 2, chunk.CHUNK_LENGTH / 2)) for plane in planes: _in = 0 _out = 0 normal = plane.xyz w = plane.w if glm.dot(normal, center + glm.vec3(chunk.CHUNK_WIDTH / 2, chunk.CHUNK_HEIGHT / 2, chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(-chunk.CHUNK_WIDTH / 2, chunk.CHUNK_HEIGHT / 2, chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(chunk.CHUNK_WIDTH / 2, chunk.CHUNK_HEIGHT / 2, -chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(-chunk.CHUNK_WIDTH / 2, chunk.CHUNK_HEIGHT / 2, -chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(chunk.CHUNK_WIDTH / 2, -chunk.CHUNK_HEIGHT / 2, chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(-chunk.CHUNK_WIDTH / 2, -chunk.CHUNK_HEIGHT / 2, chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(chunk.CHUNK_WIDTH / 2, -chunk.CHUNK_HEIGHT / 2, -chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if glm.dot(normal, center + glm.vec3(-chunk.CHUNK_WIDTH / 2, -chunk.CHUNK_HEIGHT / 2, -chunk.CHUNK_LENGTH / 2)) + w < 0: _out += 1 else: _in += 1 if not _in: return 0 elif _out: result = 1 return result
def get_local_position(position): x, y, z = position return glm.ivec3( int(x % chunk.CHUNK_WIDTH), int(y % chunk.CHUNK_HEIGHT), int(z % chunk.CHUNK_LENGTH))
def get_chunk_position(position): x, y, z = position return glm.ivec3( (x // chunk.CHUNK_WIDTH), (y // chunk.CHUNK_HEIGHT), (z // chunk.CHUNK_LENGTH))
def load(self): logging.info("Loading world") # for x in range(-1, 15): # for y in range(-15, 1): # self.load_chunk((x, y)) for x in range(-4, 4): for y in range(-4, 4): self.load_chunk((x, 0, y)) #for x in range(-1, 1): # for y in range(-1, 1): # self.load_chunk((x, 0, y)) for chunk_position, unlit_chunk in self.world.chunks.items(): for x in range(chunk.CHUNK_WIDTH): for y in range(chunk.CHUNK_HEIGHT): for z in range(chunk.CHUNK_LENGTH): if unlit_chunk.blocks[x][y][ z] in self.world.light_blocks: world_pos = glm.ivec3( chunk_position[0] * chunk.CHUNK_WIDTH + x, chunk_position[1] * chunk.CHUNK_HEIGHT + y, chunk_position[2] * chunk.CHUNK_LENGTH + z) self.world.increase_light(world_pos, 15, False)
def block_id_to_pos(cls, id: int) -> glm.ivec3: unsigned_to_signed = lambda x: x if x < 0x800 else x - 0x1000 i = id x = unsigned_to_signed(i % 0x1000) j = (i - x) // 0x1000 y = unsigned_to_signed(j % 0x1000) k = (j - y) // 0x1000 z = unsigned_to_signed(k % 0x1000) return glm.ivec3(x, y, z)
def __init__(self, gl, width, height, volume_res): super(Context, self).__init__() self.gl = gl self._vaos = [] self.u_res = glm.ivec2(width, height) self.u_vsize = glm.ivec3(volume_res) self.gx, self.gy, self.gz = ( int(self.u_vsize.x / 4), int(self.u_vsize.y / 4), int(self.u_vsize.z / 4), )
def update_mesh(self): self.mesh = [] self.translucent_mesh = [] for local_x in range(SUBCHUNK_WIDTH): for local_y in range(SUBCHUNK_HEIGHT): for local_z in range(SUBCHUNK_LENGTH): parent_lx = self.local_position[0] + local_x parent_ly = self.local_position[1] + local_y parent_lz = self.local_position[2] + local_z block_number = self.parent.blocks[parent_lx][parent_ly][parent_lz] parent_lpos = glm.ivec3(parent_lx, parent_ly, parent_lz) if block_number: block_type = self.world.block_types[block_number] x, y, z = pos = glm.ivec3( self.position[0] + local_x, self.position[1] + local_y, self.position[2] + local_z) # if block is cube, we want it to check neighbouring blocks so that we don't uselessly render faces # if block isn't a cube, we just want to render all faces, regardless of neighbouring blocks # since the vast majority of blocks are probably anyway going to be cubes, this won't impact performance all that much; the amount of useless faces drawn is going to be minimal if block_type.is_cube: for face, direction in enumerate(DIRECTIONS): npos = pos + direction if self.can_render_face(block_type, block_number, npos): self.add_face(face, pos, parent_lpos, block_number, block_type, npos) else: for i in range(len(block_type.vertex_positions)): self.add_face(i, pos, parent_lpos, block_number, block_type)
def load_chunk(self, chunk_position): logging.debug(f"Loading chunk at position {chunk_position}") # load the chunk file chunk_path = self.chunk_position_to_path(chunk_position) try: chunk_blocks = nbt.load(chunk_path)["Level"]["Blocks"] except FileNotFoundError: return # create chunk and fill it with the blocks from our chunk file self.world.chunks[glm.ivec3(chunk_position)] = chunk.Chunk( self.world, glm.ivec3(chunk_position)) for x in range(chunk.CHUNK_WIDTH): for y in range(chunk.CHUNK_HEIGHT): for z in range(chunk.CHUNK_LENGTH): self.world.chunks[glm.ivec3( chunk_position)].blocks[x][y][z] = chunk_blocks[ x * chunk.CHUNK_LENGTH * chunk.CHUNK_HEIGHT + z * chunk.CHUNK_HEIGHT + y]
def init(self): gl = self.gl WHD = glm.ivec3(self.u_vsize) self.volume_0 = gl.buffer(reserve=WHD.x * WHD.y * WHD.z * 4) self.vbo = gl.buffer( np.array([-1.0, -1.0, -1.0, +1.0, +1.0, -1.0, +1.0, +1.0]) .astype(np.float32) .tobytes() ) self.ibo = gl.buffer(np.array([0, 1, 2, 2, 1, 3]).astype(np.int32).tobytes()) self.recompile() recompile_handler = FileSystemEventHandler() recompile_handler.on_modified = self.on_src_modified self.src_observer = Observer() self.src_observer.schedule(recompile_handler, "./gl") self.src_observer.start()
def set_block(self, position, number): # set number to 0 (air) to remove block x, y, z = position chunk_position = get_chunk_position(position) if not chunk_position in self.chunks: # if no chunks exist at this position, create a new one if number == 0: return # no point in creating a whole new chunk if we're not gonna be adding anything self.create_chunk(chunk_position) if self.get_block_number(position) == number: # no point updating mesh if the block is the same return lx, ly, lz = get_local_position(position) self.chunks[chunk_position].blocks[lx][ly][lz] = number self.chunks[chunk_position].modified = True self.chunks[chunk_position].update_at_position((x, y, z)) if number: if number in self.light_blocks: self.increase_light(position, 15) elif self.block_types[number].transparent != 2: self.decrease_light(position) self.decrease_skylight(position) elif not number: self.decrease_light(position) self.decrease_skylight(position) cx, cy, cz = chunk_position def try_update_chunk_at_position(chunk_position, position): if chunk_position in self.chunks: self.chunks[chunk_position].update_at_position(position) if lx == chunk.CHUNK_WIDTH - 1: try_update_chunk_at_position(glm.ivec3(cx + 1, cy, cz), (x + 1, y, z)) if lx == 0: try_update_chunk_at_position(glm.ivec3(cx - 1, cy, cz), (x - 1, y, z)) if ly == chunk.CHUNK_HEIGHT - 1: try_update_chunk_at_position(glm.ivec3(cx, cy + 1, cz), (x, y + 1, z)) if ly == 0: try_update_chunk_at_position(glm.ivec3(cx, cy - 1, cz), (x, y - 1, z)) if lz == chunk.CHUNK_LENGTH - 1: try_update_chunk_at_position(glm.ivec3(cx, cy, cz + 1), (x, y, z + 1)) if lz == 0: try_update_chunk_at_position(glm.ivec3(cx, cy, cz - 1), (x, y, z - 1))
def __init__(self, width, height): global m_vao global m_vbo i = 0 j = 0 load_shaders() initial_positions = [glm.vec4() for _ in range(POINTS_TOTAL)] initial_velocities = [glm.vec3() for _ in range(POINTS_TOTAL)] connection_vectors = [glm.ivec3() for _ in range(POINTS_TOTAL)] n = 0 for j in range(0, POINTS_Y): fj = float(j) / float(POINTS_Y) for i in range(0, POINTS_X): fi = float(i) / float(POINTS_X) initial_positions[n] = glm.vec4((fi - 0.5) * float(POINTS_X), (fj - 0.5) * float(POINTS_Y), 0.6 * sin(fi) * cos(fj), 1.0) initial_velocities[n] = glm.vec3(0.0) connection_vectors[n] = glm.ivec4(-1) if (j != (POINTS_Y - 1)): if (i != 0): connection_vectors[n][0] = n - 1 if (j != 0): connection_vectors[n][1] = n - POINTS_X if (i != (POINTS_X - 1)): connection_vectors[n][2] = n + 1 if (j != (POINTS_Y - 1)): connection_vectors[n][3] = n + POINTS_X n += 1 for i in range(0, 2): glGenVertexArrays(1, m_vao[i]) for i in range(0, 5): glGenBuffers(i + 1, m_vbo[i]) for i in range(0, 2): glBindVertexArray(m_vao[i]) glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i]) # POSITION_A glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i]) ar_position = np.empty([POINTS_TOTAL, 4], dtype='float32') for j, e in enumerate(initial_positions): ar_position[j] = e glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4()), ar_position, GL_DYNAMIC_COPY) glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None) glEnableVertexAttribArray(0) # VELOCITY_A glBindBuffer(GL_ARRAY_BUFFER, m_vbo[VELOCITY_A + i]) ar_velocities = np.empty([POINTS_TOTAL, 3], dtype='float32') for j, e in enumerate(initial_velocities): ar_velocities[j] = e glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec3()), ar_velocities, GL_DYNAMIC_COPY) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None) glEnableVertexAttribArray(1) # CONNECTION glBindBuffer(GL_ARRAY_BUFFER, m_vbo[CONNECTION]) ar_connection = np.empty([POINTS_TOTAL, 4], dtype='uint32') for j, e in enumerate(connection_vectors): ar_connection[j] = e glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.ivec4()), ar_connection, GL_STATIC_DRAW) glVertexAttribIPointer(2, 4, GL_INT, 0, None) glEnableVertexAttribArray(2) glGenTextures(2, m_pos_tbo) glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[0]) glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_A]) glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[1]) glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_B]) lines = (POINTS_X - 1) * POINTS_Y + (POINTS_Y - 1) * POINTS_X glGenBuffers(1, m_index_buffer) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer) glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * ctypes.sizeof(ctypes.c_int), None, GL_STATIC_DRAW) e = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, lines * 2 * ctypes.sizeof(ctypes.c_int), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT) int_array = (ctypes.c_int * (4 * lines * 2)).from_address(e) n = 0 for j in range(0, POINTS_Y): for i in range(0, POINTS_X - 1): int_array[n] = i + j * POINTS_X n += 1 int_array[n] = 1 + i + j * POINTS_X n += 1 for i in range(0, POINTS_X): for j in range(0, POINTS_Y - 1): int_array[n] = i + j * POINTS_X n += 1 int_array[n] = POINTS_X + i + j * POINTS_X n += 1 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER)
def intersect(self, ray_origin, ray_direction): size = glm.ivec3(*self.size) abs_d = glm.abs(ray_direction) max_d = max(abs_d.x, max(abs_d.y, abs_d.z)) dir = ray_direction / max_d / glm.vec3(size) origin = ray_origin * glm.vec3(size) map = glm.ivec3(origin + 1) - 1 side = 0 stepAmount = glm.ivec3(0) tDelta = abs(1.0 / dir) tMax = glm.vec3(0) voxel = 0 if dir.x < 0: stepAmount.x = -1 tMax.x = (origin.x - map.x) * tDelta.x elif dir.x > 0: stepAmount.x = 1 tMax.x = (map.x + 1 - origin.x) * tDelta.x else: stepAmount.x = 0 tMax.x = 0 if dir.y < 0: stepAmount.y = -1 tMax.y = (origin.y - map.y) * tDelta.y elif dir.y > 0: stepAmount.y = 1 tMax.y = (map.y + 1 - origin.y) * tDelta.y else: stepAmount.y = 0 tMax.y = 0 if dir.z < 0: stepAmount.z = -1 tMax.z = (origin.z - map.z) * tDelta.z elif dir.z > 0: stepAmount.z = 1 tMax.z = (map.z + 1.0 - origin.z) * tDelta.z else: stepAmount.z = 0 tMax.z = 0 while True: if tMax.x < tMax.y: if tMax.x < tMax.z: map.x += stepAmount.x if (stepAmount.x > 0 and map.x >= size.x) or (stepAmount.x < 0 and map.x < 0): return (False, map, side) tMax.x += tDelta.x side = 0 else: map.z += stepAmount.z if (stepAmount.z > 0 and map.z >= size.z) or (stepAmount.z < 0 and map.z < 0): return (False, map, side) tMax.z += tDelta.z side = 2 else: if tMax.y < tMax.z: map.y += stepAmount.y if (stepAmount.y > 0 and map.y >= size.y) or (stepAmount.y < 0 and map.y < 0): return (False, map, side) tMax.y += tDelta.y side = 1 else: map.z += stepAmount.z if (stepAmount.z > 0 and map.z >= size.z) or (stepAmount.z < 0 and map.z < 0): return (False, map, side) tMax.z += tDelta.z side = 2 voxel = self.lookup(map) if voxel != 0: break return (True, map, side)
def value(self): v = b'\x00' * 12 native.n_svivec3_value(self.m_cptr, ffi.from_buffer('int[]', v)) return glm.ivec3(struct.unpack('3i', v))
def main(): global delta_time, last_frame fps_list = [] test_world = world_gen.world('__DELETEME__', '-o') window = utilities.window() camera.setup_window(window) ctx = mgl.create_context() window.bind_context(ctx) ctx.enable(mgl.DEPTH_TEST | mgl.BLEND) ctx.blend_func = mgl.SRC_ALPHA, mgl.ONE_MINUS_SRC_ALPHA scene = ctx.program(vertex_shader=vertex_source_3d, fragment_shader=fragment_source_3d, geometry_shader=geometry_source_3d, varyings=("texture_coord", "texture_layer")) hud = ctx.program(vertex_shader=vertex_source_GUI, fragment_shader=fragment_source_GUI) sky = ctx.program(vertex_shader=vertex_source_sky, fragment_shader=fragment_source_sky) sky_data = np.array( [ # Sky -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ], dtype='float32') sky_vbo = ctx.buffer(sky_data) sky_vao = ctx.vertex_array(sky, sky_vbo, "aPos") crosshair = np.array([0, 0, 0], dtype='float32') # Crosshair vbo_2d = ctx.buffer(crosshair) vao_2d = ctx.vertex_array(hud, vbo_2d, "aPos") crosshair_file = Image.open(texturepath / "icons.png").crop((0, 0, 16, 16)) crosshair_texture = ctx.texture((16, 16), 4, crosshair_file.tobytes()) crosshair_texture.filter = (mgl.NEAREST, mgl.NEAREST) camera_direction = glm.vec3() second_counter = 0 frame_counter = 0 all_textures, layers = render.load_all_block_textures( blocktexturepath, ctx) all_models = model.load_all(rootpath) world_render = render.render(layers, all_models, all_textures, scene, ctx) all_chunks = test_world.return_all_chunks() chunk_arrays = world_render.create_buffers_from_chunks(all_chunks) while not window.check_if_closed(): current_frame = glfw.get_time() delta_time = current_frame - last_frame last_frame = current_frame second_counter += delta_time frame_counter += 1 window.refresh(0, ctx) sky['orientation'] = glm.radians(camera.pitch) ctx.disable(mgl.DEPTH_TEST) sky_vao.render(mode=mgl.TRIANGLE_STRIP) camera.process_input(window, delta_time) camera.testing_commands(window) pos, looking, up = camera.return_vectors() view = glm.lookAt(pos, looking, up) projection = glm.perspective(glm.radians(45), window.size[0] / window.size[1], 0.1, 256) scene['view'].write(view) scene['projection'].write(projection) scene['texture0'] = 0 all_textures.use(location=0) if glfw.get_key(window.window, glfw.KEY_U) == glfw.PRESS: test_world.set_block(tuple(glm.ivec3(pos)), 3, world_render) ctx.enable(mgl.DEPTH_TEST) world_render.draw_from_chunks(chunk_arrays) hud['texture0'] = 0 crosshair_texture.use(location=0) vao_2d.render(mode=mgl.POINTS) if second_counter >= 1: fps_list.append(frame_counter) second_counter, frame_counter = 0, 0 window.refresh(1, ctx) window.close() print('\n===== End statistics =====') print("Average FPS: {}".format(np.mean(fps_list))) print("Render buffer creation: ", world_render.time_required) print(test_world.return_time())